ochre-sdk 0.22.19 → 0.22.21
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 +5 -0
- package/dist/index.mjs +326 -131
- package/package.json +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -1293,6 +1293,11 @@ declare function fetchGallery(params: {
|
|
|
1293
1293
|
* Fetches and parses an OCHRE item from the OCHRE API
|
|
1294
1294
|
*
|
|
1295
1295
|
* @param uuid - The UUID of the OCHRE item to fetch
|
|
1296
|
+
* @param category - The category of the OCHRE item to fetch
|
|
1297
|
+
* @param itemCategories - The categories of the OCHRE items to fetch
|
|
1298
|
+
* @param options - The options for the fetch
|
|
1299
|
+
* @param options.fetch - The fetch function to use
|
|
1300
|
+
* @param options.version - The version of the OCHRE API to use
|
|
1296
1301
|
* @returns Object containing the parsed OCHRE item, or an error message if the fetch/parse fails
|
|
1297
1302
|
*/
|
|
1298
1303
|
declare function fetchItem<T extends DataCategory = DataCategory, U extends DataCategory | Array<DataCategory> = (T extends "tree" ? Exclude<DataCategory, "tree"> : T extends "set" ? Array<DataCategory> : never)>(uuid: string, category?: T, itemCategories?: U, options?: {
|
package/dist/index.mjs
CHANGED
|
@@ -2065,6 +2065,11 @@ async function fetchByUuid(uuid, options) {
|
|
|
2065
2065
|
* Fetches and parses an OCHRE item from the OCHRE API
|
|
2066
2066
|
*
|
|
2067
2067
|
* @param uuid - The UUID of the OCHRE item to fetch
|
|
2068
|
+
* @param category - The category of the OCHRE item to fetch
|
|
2069
|
+
* @param itemCategories - The categories of the OCHRE items to fetch
|
|
2070
|
+
* @param options - The options for the fetch
|
|
2071
|
+
* @param options.fetch - The fetch function to use
|
|
2072
|
+
* @param options.version - The version of the OCHRE API to use
|
|
2068
2073
|
* @returns Object containing the parsed OCHRE item, or an error message if the fetch/parse fails
|
|
2069
2074
|
*/
|
|
2070
2075
|
async function fetchItem(uuid, category, itemCategories, options) {
|
|
@@ -2511,6 +2516,16 @@ function buildPropertyStringQueryExpression(params) {
|
|
|
2511
2516
|
value,
|
|
2512
2517
|
matchMode,
|
|
2513
2518
|
isCaseSensitive
|
|
2519
|
+
}),
|
|
2520
|
+
rawValueQueryExpression: buildValueRawValueInnerQuery({
|
|
2521
|
+
value,
|
|
2522
|
+
matchMode,
|
|
2523
|
+
isCaseSensitive
|
|
2524
|
+
}),
|
|
2525
|
+
bareValueQueryExpression: buildValueDirectTextInnerQuery({
|
|
2526
|
+
value,
|
|
2527
|
+
matchMode,
|
|
2528
|
+
isCaseSensitive
|
|
2514
2529
|
})
|
|
2515
2530
|
});
|
|
2516
2531
|
}
|
|
@@ -3030,6 +3045,47 @@ function buildOrderedItemsClause(sort) {
|
|
|
3030
3045
|
})}
|
|
3031
3046
|
return $item`;
|
|
3032
3047
|
}
|
|
3048
|
+
function isExactStringPropertyQuery(query) {
|
|
3049
|
+
return "target" in query && query.target === "property" && query.dataType === "string" && query.value != null && query.matchMode === "exact" && query.isNegated !== true;
|
|
3050
|
+
}
|
|
3051
|
+
function getCtsQueriesWithoutExactStringPropertyQueries(queries) {
|
|
3052
|
+
if (queries == null) return null;
|
|
3053
|
+
if ("target" in queries) return isExactStringPropertyQuery(queries) ? null : queries;
|
|
3054
|
+
if ("or" in queries) return queries;
|
|
3055
|
+
const filteredChildren = [];
|
|
3056
|
+
for (const childQuery of queries.and) {
|
|
3057
|
+
const filteredChildQuery = getCtsQueriesWithoutExactStringPropertyQueries(childQuery);
|
|
3058
|
+
if (filteredChildQuery != null) filteredChildren.push(filteredChildQuery);
|
|
3059
|
+
}
|
|
3060
|
+
if (filteredChildren.length === 0) return null;
|
|
3061
|
+
return filteredChildren.length === 1 ? filteredChildren[0] ?? null : { and: filteredChildren };
|
|
3062
|
+
}
|
|
3063
|
+
function buildExactStringPropertyPredicate(query) {
|
|
3064
|
+
const propertyPredicates = [];
|
|
3065
|
+
const value = stringLiteral(query.value);
|
|
3066
|
+
if (query.propertyVariable != null) propertyPredicates.push(`label/@uuid = ${stringLiteral(query.propertyVariable)}`);
|
|
3067
|
+
propertyPredicates.push(`value[
|
|
3068
|
+
not(@inherited = "true")
|
|
3069
|
+
and (
|
|
3070
|
+
content[@xml:lang = ${stringLiteral(query.language)}]/string = ${value}
|
|
3071
|
+
or @rawValue = ${value}
|
|
3072
|
+
or (not(content) and text() = ${value})
|
|
3073
|
+
)
|
|
3074
|
+
]`);
|
|
3075
|
+
return `.//properties/property[${propertyPredicates.join(" and ")}]`;
|
|
3076
|
+
}
|
|
3077
|
+
function buildExactStringPropertyXPathFilterExpression(queries) {
|
|
3078
|
+
if (queries == null) return null;
|
|
3079
|
+
if ("target" in queries) return isExactStringPropertyQuery(queries) ? buildExactStringPropertyPredicate(queries) : null;
|
|
3080
|
+
if ("or" in queries) return null;
|
|
3081
|
+
const childExpressions = [];
|
|
3082
|
+
for (const childQuery of queries.and) {
|
|
3083
|
+
const childExpression = buildExactStringPropertyXPathFilterExpression(childQuery);
|
|
3084
|
+
if (childExpression != null) childExpressions.push(childExpression);
|
|
3085
|
+
}
|
|
3086
|
+
if (childExpressions.length === 0) return null;
|
|
3087
|
+
return childExpressions.join(" and ");
|
|
3088
|
+
}
|
|
3033
3089
|
/**
|
|
3034
3090
|
* Build an XQuery string to fetch Set items from the OCHRE API
|
|
3035
3091
|
* @param params - The parameters for the fetch
|
|
@@ -3047,7 +3103,9 @@ function buildXQuery$1(params) {
|
|
|
3047
3103
|
const startPosition = (page - 1) * pageSize + 1;
|
|
3048
3104
|
const setScopeDeclaration = `declare variable $setScopeUuids := (${setScopeUuids.map((uuid) => stringLiteral(uuid)).join(", ")});`;
|
|
3049
3105
|
const baseItemsExpression = "doc()/ochre/set[@uuid = $setScopeUuids]/items/*";
|
|
3050
|
-
const
|
|
3106
|
+
const ctsQueries = getCtsQueriesWithoutExactStringPropertyQueries(queries);
|
|
3107
|
+
const exactStringPropertyXPathFilterExpression = buildExactStringPropertyXPathFilterExpression(queries);
|
|
3108
|
+
const compiledQueryPlan = buildQueryPlan({ queries: ctsQueries });
|
|
3051
3109
|
const itemsQueryExpressions = [];
|
|
3052
3110
|
const belongsToCollectionQueryExpression = buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, BELONGS_TO_COLLECTION_UUID);
|
|
3053
3111
|
if (compiledQueryPlan.queryExpression != null) itemsQueryExpressions.push(compiledQueryPlan.queryExpression);
|
|
@@ -3056,8 +3114,11 @@ function buildXQuery$1(params) {
|
|
|
3056
3114
|
const orderedItemsClause = buildOrderedItemsClause(sort);
|
|
3057
3115
|
const xqueryDeclarations = ["xquery version \"1.0-ml\";", setScopeDeclaration];
|
|
3058
3116
|
if (compiledQueryPlan.prolog !== "") xqueryDeclarations.push(compiledQueryPlan.prolog);
|
|
3059
|
-
const
|
|
3060
|
-
let $
|
|
3117
|
+
const searchedItemsClause = itemsQueryExpression == null ? `let $searchedItems := ${baseItemsExpression}` : `let $query := ${itemsQueryExpression}
|
|
3118
|
+
let $searchedItems := cts:search(${baseItemsExpression}, $query)`;
|
|
3119
|
+
const itemsClause = exactStringPropertyXPathFilterExpression == null ? `${searchedItemsClause}
|
|
3120
|
+
let $items := $searchedItems` : `${searchedItemsClause}
|
|
3121
|
+
let $items := $searchedItems[${exactStringPropertyXPathFilterExpression}]`;
|
|
3061
3122
|
return `${xqueryDeclarations.join("\n\n")}
|
|
3062
3123
|
|
|
3063
3124
|
<ochre>{
|
|
@@ -3190,67 +3251,27 @@ function parsePropertyValueBooleanContent(rawValue) {
|
|
|
3190
3251
|
if (typeof rawValue === "boolean") return rawValue;
|
|
3191
3252
|
return rawValue.toString().toLocaleLowerCase("en-US") === "true";
|
|
3192
3253
|
}
|
|
3193
|
-
function
|
|
3194
|
-
|
|
3195
|
-
return `${value.dataType}|${contentKey}`;
|
|
3196
|
-
}
|
|
3197
|
-
function aggregatePropertyValues(values) {
|
|
3198
|
-
const groupedPropertyValuesMap = /* @__PURE__ */ new Map();
|
|
3199
|
-
for (const value of values) {
|
|
3200
|
-
const key = getPropertyValueGroupKey(value);
|
|
3201
|
-
const existing = groupedPropertyValuesMap.get(key);
|
|
3202
|
-
if (existing == null) {
|
|
3203
|
-
groupedPropertyValuesMap.set(key, {
|
|
3204
|
-
dataType: value.dataType,
|
|
3205
|
-
content: value.content,
|
|
3206
|
-
label: value.label,
|
|
3207
|
-
itemUuids: new Set([value.itemUuid])
|
|
3208
|
-
});
|
|
3209
|
-
continue;
|
|
3210
|
-
}
|
|
3211
|
-
existing.itemUuids.add(value.itemUuid);
|
|
3212
|
-
if (existing.label == null && value.label != null) existing.label = value.label;
|
|
3213
|
-
}
|
|
3214
|
-
const groupedPropertyValues = [];
|
|
3215
|
-
for (const group of groupedPropertyValuesMap.values()) {
|
|
3216
|
-
if (group.content == null) continue;
|
|
3217
|
-
groupedPropertyValues.push({
|
|
3218
|
-
count: group.itemUuids.size,
|
|
3219
|
-
dataType: group.dataType,
|
|
3220
|
-
content: group.content,
|
|
3221
|
-
label: group.label
|
|
3222
|
-
});
|
|
3223
|
-
}
|
|
3224
|
-
return groupedPropertyValues.toSorted((a, b) => {
|
|
3254
|
+
function sortPropertyValues(values) {
|
|
3255
|
+
return values.toSorted((a, b) => {
|
|
3225
3256
|
if (a.count !== b.count) return b.count - a.count;
|
|
3226
3257
|
if (a.label !== b.label) return a.label?.localeCompare(b.label ?? "") ?? 0;
|
|
3227
3258
|
return a.content?.toString().localeCompare(b.content?.toString() ?? "") ?? 0;
|
|
3228
3259
|
});
|
|
3229
3260
|
}
|
|
3230
|
-
function
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
if (existing == null) {
|
|
3236
|
-
groupedAttributeValuesMap.set(value.content, {
|
|
3237
|
-
content: value.content,
|
|
3238
|
-
itemUuids: new Set([value.itemUuid])
|
|
3239
|
-
});
|
|
3240
|
-
continue;
|
|
3241
|
-
}
|
|
3242
|
-
existing.itemUuids.add(value.itemUuid);
|
|
3243
|
-
}
|
|
3244
|
-
const groupedAttributeValues = [];
|
|
3245
|
-
for (const group of groupedAttributeValuesMap.values()) groupedAttributeValues.push({
|
|
3246
|
-
count: group.itemUuids.size,
|
|
3247
|
-
content: group.content
|
|
3248
|
-
});
|
|
3249
|
-
return groupedAttributeValues.toSorted((a, b) => {
|
|
3261
|
+
function getPropertyValueKey(value) {
|
|
3262
|
+
return `${value.dataType}|${typeof value.content}:${value.content.toLocaleString("en-US")}`;
|
|
3263
|
+
}
|
|
3264
|
+
function sortAttributeValues(values) {
|
|
3265
|
+
return values.toSorted((a, b) => {
|
|
3250
3266
|
if (a.count !== b.count) return b.count - a.count;
|
|
3251
3267
|
return a.content.localeCompare(b.content);
|
|
3252
3268
|
});
|
|
3253
3269
|
}
|
|
3270
|
+
const countSchema = z.union([z.number(), z.string()]).optional().transform((val) => {
|
|
3271
|
+
if (val == null || val === "") return 1;
|
|
3272
|
+
const count = Number(val);
|
|
3273
|
+
return Number.isFinite(count) ? count : 1;
|
|
3274
|
+
});
|
|
3254
3275
|
function getPropertyVariableUuidsFromQueries(queries) {
|
|
3255
3276
|
const propertyVariableUuids = /* @__PURE__ */ new Set();
|
|
3256
3277
|
if (queries == null) return [];
|
|
@@ -3287,8 +3308,10 @@ function getItemFilterQueriesFromPropertyValueQueries(queries) {
|
|
|
3287
3308
|
*/
|
|
3288
3309
|
const propertyValueQueryItemSchema = z.object({
|
|
3289
3310
|
uuid: z.string(),
|
|
3311
|
+
scope: z.enum(["global", "variable"]).default("global"),
|
|
3290
3312
|
variableUuid: z.string().optional(),
|
|
3291
|
-
|
|
3313
|
+
count: countSchema,
|
|
3314
|
+
globalCount: countSchema.nullish(),
|
|
3292
3315
|
dataType: z.string(),
|
|
3293
3316
|
rawValue: fakeStringSchema.optional(),
|
|
3294
3317
|
content: z.union([
|
|
@@ -3298,8 +3321,10 @@ const propertyValueQueryItemSchema = z.object({
|
|
|
3298
3321
|
]).optional()
|
|
3299
3322
|
}).transform((val) => {
|
|
3300
3323
|
const returnValue = {
|
|
3324
|
+
scope: val.scope,
|
|
3301
3325
|
variableUuid: val.variableUuid != null && val.variableUuid !== "" ? val.variableUuid : null,
|
|
3302
|
-
|
|
3326
|
+
count: val.count,
|
|
3327
|
+
globalCount: val.globalCount ?? null,
|
|
3303
3328
|
dataType: val.dataType,
|
|
3304
3329
|
content: null,
|
|
3305
3330
|
label: null
|
|
@@ -3331,11 +3356,11 @@ const propertyValueQueryItemSchema = z.object({
|
|
|
3331
3356
|
});
|
|
3332
3357
|
const attributeValueQueryItemSchema = z.object({
|
|
3333
3358
|
attributeType: z.enum(["bibliographies", "periods"]),
|
|
3334
|
-
|
|
3359
|
+
count: countSchema,
|
|
3335
3360
|
content: z.string().optional()
|
|
3336
3361
|
}).transform((val) => ({
|
|
3337
3362
|
attributeType: val.attributeType,
|
|
3338
|
-
|
|
3363
|
+
count: val.count,
|
|
3339
3364
|
content: val.content != null && val.content !== "" ? val.content : null
|
|
3340
3365
|
}));
|
|
3341
3366
|
/**
|
|
@@ -3360,9 +3385,8 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
|
|
|
3360
3385
|
*/
|
|
3361
3386
|
function buildXQuery(params) {
|
|
3362
3387
|
const { setScopeUuids, belongsToCollectionScopeUuids, queries, propertyVariableUuids, attributes, isLimitedToLeafPropertyValues } = params;
|
|
3363
|
-
const
|
|
3364
|
-
const
|
|
3365
|
-
const baseItemsExpression = setScopeValues.length > 0 ? "doc()/ochre/set[@uuid = $setScopeUuids]/items/*" : "doc()/ochre/set/items/*";
|
|
3388
|
+
const setScopeDeclaration = `declare variable $setScopeUuids := (${setScopeUuids.map((uuid) => stringLiteral(uuid)).join(", ")});`;
|
|
3389
|
+
const baseItemsExpression = "doc()/ochre/set[@uuid = $setScopeUuids]/items/*";
|
|
3366
3390
|
const compiledQueryPlan = buildQueryPlan({ queries: getItemFilterQueriesFromPropertyValueQueries(queries) });
|
|
3367
3391
|
const itemsQueryExpressions = [];
|
|
3368
3392
|
const belongsToCollectionQueryExpression = buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, BELONGS_TO_COLLECTION_UUID);
|
|
@@ -3372,39 +3396,186 @@ function buildXQuery(params) {
|
|
|
3372
3396
|
const valueFilter = isLimitedToLeafPropertyValues ? "[not(@i)]" : "";
|
|
3373
3397
|
const queryBlocks = [];
|
|
3374
3398
|
const returnedSequences = [];
|
|
3375
|
-
const xqueryDeclarations = [
|
|
3376
|
-
|
|
3399
|
+
const xqueryDeclarations = [
|
|
3400
|
+
"xquery version \"1.0-ml\";",
|
|
3401
|
+
"declare namespace map = \"http://marklogic.com/xdmp/map\";",
|
|
3402
|
+
setScopeDeclaration,
|
|
3403
|
+
`declare function local:increment-count($counts, $key) {
|
|
3404
|
+
let $current := map:get($counts, $key)
|
|
3405
|
+
return map:put(
|
|
3406
|
+
$counts,
|
|
3407
|
+
$key,
|
|
3408
|
+
if (empty($current)) then 1 else xs:integer($current) + 1
|
|
3409
|
+
)
|
|
3410
|
+
};
|
|
3411
|
+
|
|
3412
|
+
declare function local:value-display($v) {
|
|
3413
|
+
if ($v/content)
|
|
3414
|
+
then string-join($v/content[@xml:lang="eng"]//text(), "")
|
|
3415
|
+
else string($v)
|
|
3416
|
+
};
|
|
3417
|
+
|
|
3418
|
+
declare function local:value-content($data-type, $raw-value, $value-uuid, $display) {
|
|
3419
|
+
if ($data-type = "IDREF") then $value-uuid
|
|
3420
|
+
else if ($data-type = ("integer", "decimal", "time")) then
|
|
3421
|
+
if ($raw-value castable as xs:double)
|
|
3422
|
+
then string(xs:double($raw-value))
|
|
3423
|
+
else ""
|
|
3424
|
+
else if ($data-type = "boolean") then
|
|
3425
|
+
if ($raw-value = "") then ""
|
|
3426
|
+
else if (lower-case($raw-value) = "true") then "true"
|
|
3427
|
+
else "false"
|
|
3428
|
+
else if ($raw-value != "") then $raw-value
|
|
3429
|
+
else if ($display != "" and $display != "<unassigned>") then $display
|
|
3430
|
+
else ""
|
|
3431
|
+
};
|
|
3432
|
+
|
|
3433
|
+
declare function local:value-kind($data-type) {
|
|
3434
|
+
if ($data-type = ("integer", "decimal", "time")) then "number"
|
|
3435
|
+
else if ($data-type = "boolean") then "boolean"
|
|
3436
|
+
else "string"
|
|
3437
|
+
};
|
|
3438
|
+
|
|
3439
|
+
declare function local:property-output-raw-value($data-type, $raw-value, $content) {
|
|
3440
|
+
if ($data-type = ("integer", "decimal", "time", "boolean")) then $content
|
|
3441
|
+
else $raw-value
|
|
3442
|
+
};
|
|
3443
|
+
|
|
3444
|
+
declare function local:put-property-detail(
|
|
3445
|
+
$details,
|
|
3446
|
+
$key,
|
|
3447
|
+
$scope,
|
|
3448
|
+
$variable-uuid,
|
|
3449
|
+
$value-uuid,
|
|
3450
|
+
$raw-value,
|
|
3451
|
+
$data-type,
|
|
3452
|
+
$display
|
|
3453
|
+
) {
|
|
3454
|
+
let $existing := map:get($details, $key)
|
|
3455
|
+
return
|
|
3456
|
+
if (
|
|
3457
|
+
empty($existing)
|
|
3458
|
+
or (string-length(string($existing)) = 0 and string-length($display) gt 0)
|
|
3459
|
+
) then
|
|
3460
|
+
map:put(
|
|
3461
|
+
$details,
|
|
3462
|
+
$key,
|
|
3463
|
+
<propertyValue scope="{$scope}" variableUuid="{$variable-uuid}" uuid="{$value-uuid}" rawValue="{$raw-value}" dataType="{$data-type}">{$display}</propertyValue>
|
|
3464
|
+
)
|
|
3465
|
+
else ()
|
|
3466
|
+
};
|
|
3467
|
+
|
|
3468
|
+
declare function local:add-property-facet(
|
|
3469
|
+
$counts,
|
|
3470
|
+
$details,
|
|
3471
|
+
$seen,
|
|
3472
|
+
$key,
|
|
3473
|
+
$scope,
|
|
3474
|
+
$variable-uuid,
|
|
3475
|
+
$value-uuid,
|
|
3476
|
+
$raw-value,
|
|
3477
|
+
$data-type,
|
|
3478
|
+
$display
|
|
3479
|
+
) {
|
|
3480
|
+
if (exists(map:get($seen, $key))) then ()
|
|
3481
|
+
else (
|
|
3482
|
+
map:put($seen, $key, true()),
|
|
3483
|
+
local:increment-count($counts, $key),
|
|
3484
|
+
local:put-property-detail($details, $key, $scope, $variable-uuid, $value-uuid, $raw-value, $data-type, $display)
|
|
3485
|
+
)
|
|
3486
|
+
};
|
|
3487
|
+
|
|
3488
|
+
declare function local:add-attribute-facet($counts, $seen, $key) {
|
|
3489
|
+
if (exists(map:get($seen, $key))) then ()
|
|
3490
|
+
else (
|
|
3491
|
+
map:put($seen, $key, true()),
|
|
3492
|
+
local:increment-count($counts, $key)
|
|
3493
|
+
)
|
|
3494
|
+
};`
|
|
3495
|
+
];
|
|
3377
3496
|
if (compiledQueryPlan.prolog !== "") xqueryDeclarations.push(compiledQueryPlan.prolog);
|
|
3378
3497
|
if (propertyVariableUuids.length > 0) {
|
|
3379
|
-
const
|
|
3380
|
-
|
|
3498
|
+
const propertyVariableValues = propertyVariableUuids.map((uuid) => stringLiteral(uuid));
|
|
3499
|
+
xqueryDeclarations.push(`declare variable $facetLabelUuids := (${propertyVariableValues.join(", ")});`);
|
|
3500
|
+
queryBlocks.push(`let $global-property-counts := map:map()
|
|
3501
|
+
let $variable-property-counts := map:map()
|
|
3502
|
+
let $variable-property-details := map:map()
|
|
3503
|
+
let $variable-property-global-keys := map:map()
|
|
3504
|
+
let $_property-aggregation := xdmp:eager(
|
|
3505
|
+
for $item in $items
|
|
3506
|
+
let $global-seen := map:map()
|
|
3507
|
+
let $variable-seen := map:map()
|
|
3508
|
+
return
|
|
3509
|
+
for $p in $item/properties/property[label/@uuid = $facetLabelUuids]
|
|
3510
|
+
let $variable-uuid := string($p/label/@uuid)
|
|
3511
|
+
for $v in $p/value${valueFilter}
|
|
3512
|
+
let $value-uuid := string($v/@uuid)
|
|
3513
|
+
let $raw-value := string($v/@rawValue)
|
|
3514
|
+
let $data-type := string($v/@dataType)
|
|
3515
|
+
let $display := local:value-display($v)
|
|
3516
|
+
let $content := local:value-content($data-type, $raw-value, $value-uuid, $display)
|
|
3517
|
+
let $value-kind := local:value-kind($data-type)
|
|
3518
|
+
let $output-raw-value := local:property-output-raw-value($data-type, $raw-value, $content)
|
|
3519
|
+
let $global-key := string-join(($data-type, $value-kind, $content), "||")
|
|
3520
|
+
let $variable-key := string-join(($variable-uuid, $data-type, $value-kind, $content), "||")
|
|
3521
|
+
where $content != ""
|
|
3522
|
+
return (
|
|
3523
|
+
local:add-attribute-facet($global-property-counts, $global-seen, $global-key),
|
|
3524
|
+
local:add-property-facet($variable-property-counts, $variable-property-details, $variable-seen, $variable-key, "variable", $variable-uuid, $value-uuid, $output-raw-value, $data-type, $display),
|
|
3525
|
+
map:put($variable-property-global-keys, $variable-key, $global-key)
|
|
3526
|
+
)
|
|
3527
|
+
)
|
|
3381
3528
|
|
|
3382
3529
|
let $property-values :=
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
let $
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3530
|
+
(
|
|
3531
|
+
$_property-aggregation,
|
|
3532
|
+
for $key in map:keys($variable-property-counts)
|
|
3533
|
+
let $detail := map:get($variable-property-details, $key)
|
|
3534
|
+
let $global-key := map:get($variable-property-global-keys, $key)
|
|
3535
|
+
return <propertyValue scope="variable" variableUuid="{string($detail/@variableUuid)}" uuid="{string($detail/@uuid)}" rawValue="{string($detail/@rawValue)}" dataType="{string($detail/@dataType)}" count="{map:get($variable-property-counts, $key)}" globalCount="{map:get($global-property-counts, $global-key)}">{
|
|
3536
|
+
string($detail)
|
|
3537
|
+
}</propertyValue>
|
|
3538
|
+
)`);
|
|
3390
3539
|
returnedSequences.push("$property-values");
|
|
3391
3540
|
}
|
|
3392
3541
|
if (attributes.bibliographies) {
|
|
3393
|
-
queryBlocks.push(`let $bibliography-
|
|
3542
|
+
queryBlocks.push(`let $bibliography-counts := map:map()
|
|
3543
|
+
let $_bibliography-aggregation := xdmp:eager(
|
|
3394
3544
|
for $item in $items
|
|
3395
|
-
|
|
3545
|
+
let $seen := map:map()
|
|
3546
|
+
return
|
|
3547
|
+
for $bibliography in $item/bibliographies/bibliography
|
|
3396
3548
|
let $label := string-join($bibliography/identification/label/content[@xml:lang="eng"]//text(), "")
|
|
3397
3549
|
where string-length($label) gt 0
|
|
3398
|
-
return
|
|
3550
|
+
return local:add-attribute-facet($bibliography-counts, $seen, $label)
|
|
3551
|
+
)
|
|
3552
|
+
|
|
3553
|
+
let $bibliography-values :=
|
|
3554
|
+
(
|
|
3555
|
+
$_bibliography-aggregation,
|
|
3556
|
+
for $label in map:keys($bibliography-counts)
|
|
3557
|
+
return <attributeValue attributeType="bibliographies" count="{map:get($bibliography-counts, $label)}" content="{$label}" />
|
|
3558
|
+
)`);
|
|
3399
3559
|
returnedSequences.push("$bibliography-values");
|
|
3400
3560
|
}
|
|
3401
3561
|
if (attributes.periods) {
|
|
3402
|
-
queryBlocks.push(`let $period-
|
|
3562
|
+
queryBlocks.push(`let $period-counts := map:map()
|
|
3563
|
+
let $_period-aggregation := xdmp:eager(
|
|
3403
3564
|
for $item in $items
|
|
3404
|
-
|
|
3565
|
+
let $seen := map:map()
|
|
3566
|
+
return
|
|
3567
|
+
for $period in $item/periods/period
|
|
3405
3568
|
let $label := string-join($period/identification/label/content[@xml:lang="eng"]//text(), "")
|
|
3406
3569
|
where string-length($label) gt 0
|
|
3407
|
-
return
|
|
3570
|
+
return local:add-attribute-facet($period-counts, $seen, $label)
|
|
3571
|
+
)
|
|
3572
|
+
|
|
3573
|
+
let $period-values :=
|
|
3574
|
+
(
|
|
3575
|
+
$_period-aggregation,
|
|
3576
|
+
for $label in map:keys($period-counts)
|
|
3577
|
+
return <attributeValue attributeType="periods" count="{map:get($period-counts, $label)}" content="{$label}" />
|
|
3578
|
+
)`);
|
|
3408
3579
|
returnedSequences.push("$period-values");
|
|
3409
3580
|
}
|
|
3410
3581
|
const itemsClause = itemsQueryExpression == null ? `let $items := ${baseItemsExpression}` : `let $query := ${itemsQueryExpression}
|
|
@@ -3470,35 +3641,50 @@ async function fetchSetPropertyValues(params, options) {
|
|
|
3470
3641
|
if (parsedResultRaw.result.ochre.propertyValue != null) parsedPropertyValues.push(...Array.isArray(parsedResultRaw.result.ochre.propertyValue) ? parsedResultRaw.result.ochre.propertyValue : [parsedResultRaw.result.ochre.propertyValue]);
|
|
3471
3642
|
if (parsedResultRaw.result.ochre.attributeValue != null) parsedAttributeValues.push(...Array.isArray(parsedResultRaw.result.ochre.attributeValue) ? parsedResultRaw.result.ochre.attributeValue : [parsedResultRaw.result.ochre.attributeValue]);
|
|
3472
3643
|
}
|
|
3473
|
-
const
|
|
3474
|
-
const
|
|
3644
|
+
const propertyValuesByPropertyVariableUuid = {};
|
|
3645
|
+
const flattenedPropertyValuesByKey = /* @__PURE__ */ new Map();
|
|
3475
3646
|
for (const propertyValue of parsedPropertyValues) {
|
|
3476
|
-
|
|
3477
|
-
|
|
3647
|
+
if (propertyValue.content == null) continue;
|
|
3648
|
+
const propertyValueItem = {
|
|
3649
|
+
count: propertyValue.count,
|
|
3478
3650
|
dataType: propertyValue.dataType,
|
|
3479
3651
|
content: propertyValue.content,
|
|
3480
3652
|
label: propertyValue.label
|
|
3481
3653
|
};
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
const
|
|
3489
|
-
|
|
3654
|
+
const globalPropertyValueItem = {
|
|
3655
|
+
count: propertyValue.globalCount ?? propertyValue.count,
|
|
3656
|
+
dataType: propertyValue.dataType,
|
|
3657
|
+
content: propertyValue.content,
|
|
3658
|
+
label: propertyValue.label
|
|
3659
|
+
};
|
|
3660
|
+
const globalPropertyValueKey = getPropertyValueKey({
|
|
3661
|
+
dataType: globalPropertyValueItem.dataType,
|
|
3662
|
+
content: propertyValue.content
|
|
3663
|
+
});
|
|
3664
|
+
const existingGlobalPropertyValue = flattenedPropertyValuesByKey.get(globalPropertyValueKey);
|
|
3665
|
+
if (existingGlobalPropertyValue == null) flattenedPropertyValuesByKey.set(globalPropertyValueKey, globalPropertyValueItem);
|
|
3666
|
+
else if (existingGlobalPropertyValue.label == null && globalPropertyValueItem.label != null) existingGlobalPropertyValue.label = globalPropertyValueItem.label;
|
|
3667
|
+
if (propertyValue.scope === "global") continue;
|
|
3668
|
+
if (propertyValue.variableUuid != null) (propertyValuesByPropertyVariableUuid[propertyValue.variableUuid] ??= []).push(propertyValueItem);
|
|
3490
3669
|
}
|
|
3491
|
-
const
|
|
3670
|
+
for (const [propertyVariableUuid, values] of Object.entries(propertyValuesByPropertyVariableUuid)) propertyValuesByPropertyVariableUuid[propertyVariableUuid] = sortPropertyValues(values);
|
|
3671
|
+
const attributeValuesByType = {
|
|
3492
3672
|
bibliographies: [],
|
|
3493
3673
|
periods: []
|
|
3494
3674
|
};
|
|
3495
|
-
for (const attributeValue of parsedAttributeValues)
|
|
3675
|
+
for (const attributeValue of parsedAttributeValues) {
|
|
3676
|
+
if (attributeValue.content == null || attributeValue.content === "") continue;
|
|
3677
|
+
attributeValuesByType[attributeValue.attributeType].push({
|
|
3678
|
+
count: attributeValue.count,
|
|
3679
|
+
content: attributeValue.content
|
|
3680
|
+
});
|
|
3681
|
+
}
|
|
3496
3682
|
return {
|
|
3497
|
-
propertyValues:
|
|
3683
|
+
propertyValues: sortPropertyValues([...flattenedPropertyValuesByKey.values()]),
|
|
3498
3684
|
propertyValuesByPropertyVariableUuid,
|
|
3499
3685
|
attributeValues: {
|
|
3500
|
-
bibliographies: attributes.bibliographies ?
|
|
3501
|
-
periods: attributes.periods ?
|
|
3686
|
+
bibliographies: attributes.bibliographies ? sortAttributeValues(attributeValuesByType.bibliographies) : null,
|
|
3687
|
+
periods: attributes.periods ? sortAttributeValues(attributeValuesByType.periods) : null
|
|
3502
3688
|
},
|
|
3503
3689
|
error: null
|
|
3504
3690
|
};
|
|
@@ -3869,6 +4055,15 @@ function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
|
|
|
3869
4055
|
//#region src/utils/parse/website.ts
|
|
3870
4056
|
const SEGMENT_UNIQUE_SLUG_PREFIX_REGEX = /^\$[^-]*-/;
|
|
3871
4057
|
const TRAILING_SLASH_REGEX = /\/$/;
|
|
4058
|
+
function formatRawResourceMetadata(resource) {
|
|
4059
|
+
const metadata = [`label “${parseStringContent(resource.identification.label)}”`, `uuid “${resource.uuid}”`];
|
|
4060
|
+
if (resource.slug != null) metadata.push(`slug “${resource.slug}”`);
|
|
4061
|
+
if (resource.identification.abbreviation != null) metadata.push(`abbreviation “${parseFakeStringOrContent(resource.identification.abbreviation)}”`);
|
|
4062
|
+
return metadata.join(", ");
|
|
4063
|
+
}
|
|
4064
|
+
function formatComponentError(message, componentName, elementResource) {
|
|
4065
|
+
return `${message} for component “${componentName ?? "(unknown)"}” (${formatRawResourceMetadata(elementResource)})`;
|
|
4066
|
+
}
|
|
3872
4067
|
/**
|
|
3873
4068
|
* Extracts CSS style properties for a given presentation variant.
|
|
3874
4069
|
*
|
|
@@ -3992,7 +4187,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
3992
4187
|
switch (componentName) {
|
|
3993
4188
|
case "3d-viewer": {
|
|
3994
4189
|
const resourceLink = links.find((link) => link.category === "resource" && link.fileFormat === "model/obj");
|
|
3995
|
-
if (resourceLink?.uuid == null) throw new Error(
|
|
4190
|
+
if (resourceLink?.uuid == null) throw new Error(formatComponentError("Resource link not found", componentName, elementResource));
|
|
3996
4191
|
let isInteractive = getPropertyValueContentByLabel(componentProperty.properties, "is-interactive");
|
|
3997
4192
|
isInteractive ??= true;
|
|
3998
4193
|
let isControlsDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "controls-displayed");
|
|
@@ -4010,7 +4205,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4010
4205
|
const boundElementPropertyUuid = getPropertyByLabel(componentProperty.properties, "bound-element")?.values[0]?.uuid ?? null;
|
|
4011
4206
|
const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
|
|
4012
4207
|
const href = linkToProperty?.values[0]?.href != null ? transformPermanentIdentificationUrl(linkToProperty.values[0].href) : linkToProperty?.values[0]?.slug ?? null;
|
|
4013
|
-
if (boundElementPropertyUuid == null && href == null) throw new Error(
|
|
4208
|
+
if (boundElementPropertyUuid == null && href == null) throw new Error(formatComponentError("Bound element or href not found", componentName, elementResource));
|
|
4014
4209
|
properties = {
|
|
4015
4210
|
component: "advanced-search",
|
|
4016
4211
|
boundElementUuid: boundElementPropertyUuid,
|
|
@@ -4020,7 +4215,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4020
4215
|
}
|
|
4021
4216
|
case "annotated-document": {
|
|
4022
4217
|
const documentLink = links.find((link) => link.type === "internalDocument");
|
|
4023
|
-
if (documentLink?.uuid == null) throw new Error(
|
|
4218
|
+
if (documentLink?.uuid == null) throw new Error(formatComponentError("Document link not found", componentName, elementResource));
|
|
4024
4219
|
properties = {
|
|
4025
4220
|
component: "annotated-document",
|
|
4026
4221
|
linkUuid: documentLink.uuid
|
|
@@ -4029,7 +4224,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4029
4224
|
}
|
|
4030
4225
|
case "annotated-image": {
|
|
4031
4226
|
const imageLinks = links.filter((link) => link.type === "image" || link.type === "IIIF");
|
|
4032
|
-
if (imageLinks.length === 0 || imageLinks[0].uuid == null) throw new Error(
|
|
4227
|
+
if (imageLinks.length === 0 || imageLinks[0].uuid == null) throw new Error(formatComponentError("Image link not found", componentName, elementResource));
|
|
4033
4228
|
let isFilterInputDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "filter-input-displayed");
|
|
4034
4229
|
isFilterInputDisplayed ??= true;
|
|
4035
4230
|
let isOptionsDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "options-displayed");
|
|
@@ -4050,7 +4245,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4050
4245
|
}
|
|
4051
4246
|
case "audio-player": {
|
|
4052
4247
|
const audioLink = links.find((link) => link.type === "audio");
|
|
4053
|
-
if (audioLink?.uuid == null) throw new Error(
|
|
4248
|
+
if (audioLink?.uuid == null) throw new Error(formatComponentError("Audio link not found", componentName, elementResource));
|
|
4054
4249
|
let isSpeedControlsDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "speed-controls-displayed");
|
|
4055
4250
|
isSpeedControlsDisplayed ??= true;
|
|
4056
4251
|
let isVolumeControlsDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "volume-controls-displayed");
|
|
@@ -4069,7 +4264,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4069
4264
|
case "bibliography": {
|
|
4070
4265
|
const itemLinks = links.filter((link) => link.category !== "bibliography");
|
|
4071
4266
|
const bibliographyLink = links.find((link) => link.category === "bibliography");
|
|
4072
|
-
if (itemLinks.length === 0 && bibliographyLink?.bibliographies == null) throw new Error(
|
|
4267
|
+
if (itemLinks.length === 0 && bibliographyLink?.bibliographies == null) throw new Error(formatComponentError("No links found", componentName, elementResource));
|
|
4073
4268
|
let layout = getPropertyValueContentByLabel(componentProperty.properties, "layout");
|
|
4074
4269
|
layout ??= "long";
|
|
4075
4270
|
let isSourceDocumentDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "source-document-displayed");
|
|
@@ -4092,7 +4287,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4092
4287
|
if (href === null) {
|
|
4093
4288
|
const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
|
|
4094
4289
|
href = linkToProperty?.values[0]?.href != null ? transformPermanentIdentificationUrl(linkToProperty.values[0].href) : linkToProperty?.values[0]?.slug ?? null;
|
|
4095
|
-
if (href === null) throw new Error(
|
|
4290
|
+
if (href === null) throw new Error(formatComponentError("Properties “navigate-to” or “link-to” not found", componentName, elementResource));
|
|
4096
4291
|
else isExternal = true;
|
|
4097
4292
|
}
|
|
4098
4293
|
let startIcon = getPropertyValueContentByLabel(componentProperty.properties, "start-icon");
|
|
@@ -4123,7 +4318,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4123
4318
|
}
|
|
4124
4319
|
case "collection": {
|
|
4125
4320
|
const setLinks = links.filter((link) => link.category === "set");
|
|
4126
|
-
if (setLinks.every((link) => link.uuid === null)) throw new Error(
|
|
4321
|
+
if (setLinks.every((link) => link.uuid === null)) throw new Error(formatComponentError("Set links not found", componentName, elementResource));
|
|
4127
4322
|
const displayedProperties = getPropertyByLabel(componentProperty.properties, "use-property");
|
|
4128
4323
|
let variant = getPropertyValueContentByLabel(componentProperty.properties, "variant");
|
|
4129
4324
|
variant ??= "slide";
|
|
@@ -4202,7 +4397,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4202
4397
|
}
|
|
4203
4398
|
case "entries": {
|
|
4204
4399
|
const entriesLink = links.find((link) => link.category === "tree" || link.category === "set");
|
|
4205
|
-
if (entriesLink?.uuid == null) throw new Error(
|
|
4400
|
+
if (entriesLink?.uuid == null) throw new Error(formatComponentError("Entries link not found", componentName, elementResource));
|
|
4206
4401
|
let variant = getPropertyValueContentByLabel(componentProperty.properties, "variant");
|
|
4207
4402
|
variant ??= "entry";
|
|
4208
4403
|
let isFilterInputDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "filter-input-displayed");
|
|
@@ -4217,7 +4412,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4217
4412
|
}
|
|
4218
4413
|
case "iframe": {
|
|
4219
4414
|
const href = links.find((link) => link.type === "webpage")?.href;
|
|
4220
|
-
if (!href) throw new Error(
|
|
4415
|
+
if (!href) throw new Error(formatComponentError("URL not found", componentName, elementResource));
|
|
4221
4416
|
const height = getPropertyValueContentByLabel(componentProperty.properties, "height");
|
|
4222
4417
|
const width = getPropertyValueContentByLabel(componentProperty.properties, "width");
|
|
4223
4418
|
properties = {
|
|
@@ -4230,7 +4425,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4230
4425
|
}
|
|
4231
4426
|
case "iiif-viewer": {
|
|
4232
4427
|
const manifestLink = links.find((link) => link.type === "IIIF");
|
|
4233
|
-
if (manifestLink?.uuid == null) throw new Error(
|
|
4428
|
+
if (manifestLink?.uuid == null) throw new Error(formatComponentError("Manifest link not found", componentName, elementResource));
|
|
4234
4429
|
let variant = getPropertyValueContentByLabel(componentProperty.properties, "variant");
|
|
4235
4430
|
variant ??= "universal-viewer";
|
|
4236
4431
|
properties = {
|
|
@@ -4241,7 +4436,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4241
4436
|
break;
|
|
4242
4437
|
}
|
|
4243
4438
|
case "image": {
|
|
4244
|
-
if (links.length === 0) throw new Error(
|
|
4439
|
+
if (links.length === 0) throw new Error(formatComponentError("No links found", componentName, elementResource));
|
|
4245
4440
|
let imageQuality = getPropertyValueContentByLabel(componentProperty.properties, "image-quality");
|
|
4246
4441
|
imageQuality ??= "high";
|
|
4247
4442
|
const images = [];
|
|
@@ -4329,7 +4524,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4329
4524
|
}
|
|
4330
4525
|
case "image-gallery": {
|
|
4331
4526
|
const galleryLink = links.find((link) => link.category === "tree" || link.category === "set");
|
|
4332
|
-
if (galleryLink?.uuid == null) throw new Error(
|
|
4527
|
+
if (galleryLink?.uuid == null) throw new Error(formatComponentError("Image gallery link not found", componentName, elementResource));
|
|
4333
4528
|
let isFilterInputDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "filter-input-displayed");
|
|
4334
4529
|
isFilterInputDisplayed ??= true;
|
|
4335
4530
|
properties = {
|
|
@@ -4341,7 +4536,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4341
4536
|
}
|
|
4342
4537
|
case "map": {
|
|
4343
4538
|
const mapLink = links.find((link) => link.category === "set" || link.category === "tree");
|
|
4344
|
-
if (mapLink?.uuid == null) throw new Error(
|
|
4539
|
+
if (mapLink?.uuid == null) throw new Error(formatComponentError("Map link not found", componentName, elementResource));
|
|
4345
4540
|
let isInteractive = getPropertyValueContentByLabel(componentProperty.properties, "is-interactive");
|
|
4346
4541
|
isInteractive ??= true;
|
|
4347
4542
|
let isClustered = getPropertyValueContentByLabel(componentProperty.properties, "is-clustered");
|
|
@@ -4376,17 +4571,17 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4376
4571
|
}
|
|
4377
4572
|
case "query": {
|
|
4378
4573
|
const setLinks = links.filter((link) => link.category === "set");
|
|
4379
|
-
if (setLinks.every((link) => link.uuid === null)) throw new Error(
|
|
4574
|
+
if (setLinks.every((link) => link.uuid === null)) throw new Error(formatComponentError("Set links not found", componentName, elementResource));
|
|
4380
4575
|
const items = [];
|
|
4381
|
-
if (componentProperty.properties.length === 0) throw new Error(
|
|
4576
|
+
if (componentProperty.properties.length === 0) throw new Error(formatComponentError("Query properties not found", componentName, elementResource));
|
|
4382
4577
|
for (const queryItem of componentProperty.properties) {
|
|
4383
4578
|
const querySubProperties = queryItem.properties;
|
|
4384
4579
|
const label = getPropertyValueContentByLabel(querySubProperties, "query-prompt");
|
|
4385
4580
|
if (label === null) continue;
|
|
4386
4581
|
const queries = (getPropertyByLabel(querySubProperties, "use-property")?.values.filter((value) => value.uuid !== null) ?? []).map((propertyVariable) => {
|
|
4387
|
-
if (propertyVariable.uuid === null) throw new Error(
|
|
4582
|
+
if (propertyVariable.uuid === null) throw new Error(formatComponentError("Property variable UUID not found", componentName, elementResource));
|
|
4388
4583
|
const dataType = propertyVariable.dataType;
|
|
4389
|
-
if (dataType === "coordinate") throw new Error(
|
|
4584
|
+
if (dataType === "coordinate") throw new Error(formatComponentError("Query prompts with data type \"coordinate\" are not supported", componentName, elementResource));
|
|
4390
4585
|
return {
|
|
4391
4586
|
target: "property",
|
|
4392
4587
|
propertyVariable: propertyVariable.uuid,
|
|
@@ -4407,7 +4602,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4407
4602
|
endIcon
|
|
4408
4603
|
});
|
|
4409
4604
|
}
|
|
4410
|
-
if (items.length === 0) throw new Error(
|
|
4605
|
+
if (items.length === 0) throw new Error(formatComponentError("No queries found", componentName, elementResource));
|
|
4411
4606
|
const options = {
|
|
4412
4607
|
scopes: elementResource.options?.scopes != null ? ensureArray(elementResource.options.scopes.scope).map((scope) => ({
|
|
4413
4608
|
uuid: scope.uuid.content,
|
|
@@ -4453,7 +4648,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4453
4648
|
}
|
|
4454
4649
|
case "table": {
|
|
4455
4650
|
const tableLink = links.find((link) => link.category === "set");
|
|
4456
|
-
if (tableLink?.uuid == null) throw new Error(
|
|
4651
|
+
if (tableLink?.uuid == null) throw new Error(formatComponentError("Table link not found", componentName, elementResource));
|
|
4457
4652
|
properties = {
|
|
4458
4653
|
component: "table",
|
|
4459
4654
|
linkUuid: tableLink.uuid
|
|
@@ -4467,7 +4662,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4467
4662
|
const boundElementUuid = getPropertyByLabel(componentProperty.properties, "bound-element")?.values[0]?.uuid ?? null;
|
|
4468
4663
|
const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
|
|
4469
4664
|
const href = linkToProperty?.values[0]?.href != null ? transformPermanentIdentificationUrl(linkToProperty.values[0].href) : linkToProperty?.values[0]?.slug ?? null;
|
|
4470
|
-
if (!boundElementUuid && !href) throw new Error(
|
|
4665
|
+
if (!boundElementUuid && !href) throw new Error(formatComponentError("Bound element or href not found", componentName, elementResource));
|
|
4471
4666
|
let placeholder = getPropertyValueContentByLabel(componentProperty.properties, "placeholder-text");
|
|
4472
4667
|
placeholder ??= null;
|
|
4473
4668
|
let baseFilterQueries = getPropertyValueContentByLabel(componentProperty.properties, "base-filter-queries");
|
|
@@ -4484,7 +4679,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4484
4679
|
}
|
|
4485
4680
|
case "text": {
|
|
4486
4681
|
const content = elementResource.document && "content" in elementResource.document ? parseDocument(elementResource.document.content) : null;
|
|
4487
|
-
if (!content) throw new Error(
|
|
4682
|
+
if (!content) throw new Error(formatComponentError("Content not found", componentName, elementResource));
|
|
4488
4683
|
let variantName = "block";
|
|
4489
4684
|
let variant;
|
|
4490
4685
|
const variantProperty = getPropertyByLabel(componentProperty.properties, "variant");
|
|
@@ -4511,7 +4706,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4511
4706
|
}
|
|
4512
4707
|
case "timeline": {
|
|
4513
4708
|
const timelineLink = links.find((link) => link.category === "tree");
|
|
4514
|
-
if (timelineLink?.uuid == null) throw new Error(
|
|
4709
|
+
if (timelineLink?.uuid == null) throw new Error(formatComponentError("Timeline link not found", componentName, elementResource));
|
|
4515
4710
|
properties = {
|
|
4516
4711
|
component: "timeline",
|
|
4517
4712
|
linkUuid: timelineLink.uuid
|
|
@@ -4520,7 +4715,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4520
4715
|
}
|
|
4521
4716
|
case "video": {
|
|
4522
4717
|
const videoLink = links.find((link) => link.type === "video");
|
|
4523
|
-
if (videoLink?.uuid == null) throw new Error(
|
|
4718
|
+
if (videoLink?.uuid == null) throw new Error(formatComponentError("Video link not found", componentName, elementResource));
|
|
4524
4719
|
let isChaptersDisplayed = getPropertyValueContentByLabel(componentProperty.properties, "chapters-displayed");
|
|
4525
4720
|
isChaptersDisplayed ??= true;
|
|
4526
4721
|
properties = {
|
|
@@ -4534,7 +4729,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
4534
4729
|
console.warn(`Invalid or non-implemented component name “${unparsedComponentName?.toString() ?? "(unknown)"}” for the following element: “${parseStringContent(elementResource.identification.label)}”`);
|
|
4535
4730
|
break;
|
|
4536
4731
|
}
|
|
4537
|
-
if (properties === null) throw new Error(
|
|
4732
|
+
if (properties === null) throw new Error(formatComponentError("Properties not found", componentName, elementResource));
|
|
4538
4733
|
return properties;
|
|
4539
4734
|
}
|
|
4540
4735
|
function parseWebTitle(properties, identification, overrides) {
|
|
@@ -4570,9 +4765,9 @@ function parseWebElement(elementResource) {
|
|
|
4570
4765
|
const identification = parseIdentification(elementResource.identification);
|
|
4571
4766
|
const elementProperties = elementResource.properties?.property ? parseProperties(Array.isArray(elementResource.properties.property) ? elementResource.properties.property : [elementResource.properties.property]) : [];
|
|
4572
4767
|
const presentationProperty = getPropertyByLabel(elementProperties, "presentation");
|
|
4573
|
-
if (presentationProperty === null) throw new Error(`Presentation property not found for element
|
|
4768
|
+
if (presentationProperty === null) throw new Error(`Presentation property not found for element (${formatRawResourceMetadata(elementResource)})`);
|
|
4574
4769
|
const componentProperty = getPropertyByLabel(presentationProperty.properties, "component");
|
|
4575
|
-
if (componentProperty === null) throw new Error(`Component for element
|
|
4770
|
+
if (componentProperty === null) throw new Error(`Component property not found for element (${formatRawResourceMetadata(elementResource)})`);
|
|
4576
4771
|
const properties = parseWebElementProperties(componentProperty, elementResource);
|
|
4577
4772
|
const cssStyles = parseResponsiveCssStyles(elementProperties);
|
|
4578
4773
|
const title = parseWebTitle(elementProperties, identification, {
|
|
@@ -4629,7 +4824,7 @@ function parseWebpage(webpageResource, slugPrefix) {
|
|
|
4629
4824
|
if (webpageProperties.length === 0 || getPropertyValueContentByLabel(webpageProperties, "presentation") !== "page") return null;
|
|
4630
4825
|
const identification = parseIdentification(webpageResource.identification);
|
|
4631
4826
|
const slug = webpageResource.slug?.replace(SEGMENT_UNIQUE_SLUG_PREFIX_REGEX, "") ?? null;
|
|
4632
|
-
if (slug == null) throw new Error(`Slug not found for page
|
|
4827
|
+
if (slug == null) throw new Error(`Slug not found for page (${formatRawResourceMetadata(webpageResource)})`);
|
|
4633
4828
|
const returnWebpage = {
|
|
4634
4829
|
uuid: webpageResource.uuid,
|
|
4635
4830
|
type: "page",
|
|
@@ -4724,7 +4919,7 @@ function parseWebSegment(segmentResource, slugPrefix) {
|
|
|
4724
4919
|
if (webpageProperties.length === 0 || getPropertyValueContentByLabel(webpageProperties, "presentation") !== "segment") return null;
|
|
4725
4920
|
const identification = parseIdentification(segmentResource.identification);
|
|
4726
4921
|
const slug = segmentResource.identification.abbreviation != null ? parseFakeStringOrContent(segmentResource.identification.abbreviation) : null;
|
|
4727
|
-
if (slug == null) throw new Error(`Slug not found for segment
|
|
4922
|
+
if (slug == null) throw new Error(`Slug not found for segment (${formatRawResourceMetadata(segmentResource)})`);
|
|
4728
4923
|
const returnSegment = {
|
|
4729
4924
|
uuid: segmentResource.uuid,
|
|
4730
4925
|
type: "segment",
|
|
@@ -4761,7 +4956,7 @@ function parseWebSegmentItem(segmentItemResource, slugPrefix) {
|
|
|
4761
4956
|
if (webpageProperties.length === 0 || getPropertyValueContentByLabel(webpageProperties, "presentation") !== "segment-item") return null;
|
|
4762
4957
|
const identification = parseIdentification(segmentItemResource.identification);
|
|
4763
4958
|
const slug = segmentItemResource.identification.abbreviation != null ? parseFakeStringOrContent(segmentItemResource.identification.abbreviation) : null;
|
|
4764
|
-
if (slug == null) throw new Error(`Slug not found for segment item
|
|
4959
|
+
if (slug == null) throw new Error(`Slug not found for segment item (${formatRawResourceMetadata(segmentItemResource)})`);
|
|
4765
4960
|
const returnSegmentItem = {
|
|
4766
4961
|
uuid: segmentItemResource.uuid,
|
|
4767
4962
|
type: "segment-item",
|
|
@@ -4987,9 +5182,9 @@ function parseWebBlock(blockResource) {
|
|
|
4987
5182
|
for (const resource of blockResources) {
|
|
4988
5183
|
const resourceProperties = resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [];
|
|
4989
5184
|
const resourceType = getPropertyValueContentByLabel(resourceProperties, "presentation");
|
|
4990
|
-
if (resourceType !== "element") throw new Error(`Accordion only accepts elements, but got “${resourceType}”
|
|
5185
|
+
if (resourceType !== "element") throw new Error(`Accordion only accepts elements, but got “${resourceType}” (${formatRawResourceMetadata(resource)})`);
|
|
4991
5186
|
const componentType = getPropertyValueContentByLabel(getPropertyByLabel(resourceProperties, "presentation")?.properties ?? [], "component");
|
|
4992
|
-
if (componentType !== "text") throw new Error(`Accordion only accepts text components, but got “${componentType}”
|
|
5187
|
+
if (componentType !== "text") throw new Error(`Accordion only accepts text components, but got “${componentType}” (${formatRawResourceMetadata(resource)})`);
|
|
4993
5188
|
const element = parseWebElementForAccordion(resource);
|
|
4994
5189
|
accordionItems.push(element);
|
|
4995
5190
|
}
|
|
@@ -5088,7 +5283,7 @@ function parseWebsiteProperties(properties, websiteTree, sidebar) {
|
|
|
5088
5283
|
name: contactContent[0],
|
|
5089
5284
|
email: contactContent[1] ?? null
|
|
5090
5285
|
};
|
|
5091
|
-
else throw new Error(`Contact property must
|
|
5286
|
+
else throw new Error(`Contact property must use “name;email”, got “${contactProperty.values[0]?.content}” (website uuid “${websiteTree.uuid}”)`);
|
|
5092
5287
|
}
|
|
5093
5288
|
returnProperties.loadingVariant = getPropertyValueContentByLabel(websiteProperties, "loading-variant") ?? "spinner";
|
|
5094
5289
|
returnProperties.theme.isThemeToggleDisplayed = getPropertyValueContentByLabel(websiteProperties, "supports-theme-toggle") ?? true;
|
|
@@ -5208,8 +5403,8 @@ function parseFilterContexts(filterContextLevels) {
|
|
|
5208
5403
|
return filterContextTreeLevels;
|
|
5209
5404
|
}
|
|
5210
5405
|
function parseWebsite(websiteTree, metadata, belongsTo, { version = 2 } = {}) {
|
|
5211
|
-
if (!websiteTree.properties) throw new Error(
|
|
5212
|
-
if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) throw new Error(
|
|
5406
|
+
if (!websiteTree.properties) throw new Error(`Website properties not found (website uuid “${websiteTree.uuid}”)`);
|
|
5407
|
+
if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) throw new Error(`Website pages not found (website uuid “${websiteTree.uuid}”)`);
|
|
5213
5408
|
const resources = ensureArray(websiteTree.items.resource);
|
|
5214
5409
|
const items = [...parseWebpages(resources), ...parseSegments(resources)];
|
|
5215
5410
|
const sidebar = parseSidebar(resources);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ochre-sdk",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.21",
|
|
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,7 +46,7 @@
|
|
|
46
46
|
"@date-fns/utc": "^2.1.1",
|
|
47
47
|
"date-fns": "^4.1.0",
|
|
48
48
|
"fast-equals": "^6.0.0",
|
|
49
|
-
"zod": "^4.
|
|
49
|
+
"zod": "^4.4.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@antfu/eslint-config": "^8.2.0",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"bumpp": "^11.0.1",
|
|
55
55
|
"eslint": "^10.2.1",
|
|
56
56
|
"prettier": "^3.8.3",
|
|
57
|
-
"tsdown": "^0.21.
|
|
57
|
+
"tsdown": "^0.21.10",
|
|
58
58
|
"typescript": "^6.0.3",
|
|
59
|
-
"vitest": "^4.1.
|
|
59
|
+
"vitest": "^4.1.5"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"dev": "tsdown src/index.ts --watch",
|