ochre-sdk 1.0.78 → 1.1.0

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 (84) hide show
  1. package/README.md +31 -3
  2. package/dist/_virtual/_rolldown/runtime.mjs +13 -0
  3. package/dist/categories.d.mts +249 -0
  4. package/dist/categories.mjs +259 -0
  5. package/dist/constants.d.mts +14 -0
  6. package/dist/constants.mjs +18 -1
  7. package/dist/errors.d.mts +23 -0
  8. package/dist/{utilities.mjs → errors.mjs} +33 -84
  9. package/dist/fetchers/gallery.mjs +15 -51
  10. package/dist/fetchers/item-children.mjs +20 -66
  11. package/dist/fetchers/item-links.mjs +26 -79
  12. package/dist/fetchers/item-ocr-data.d.mts +2 -2
  13. package/dist/fetchers/item-ocr-data.mjs +11 -15
  14. package/dist/fetchers/item.d.mts +0 -15
  15. package/dist/fetchers/item.mjs +20 -72
  16. package/dist/fetchers/request.d.mts +70 -0
  17. package/dist/fetchers/request.mjs +100 -0
  18. package/dist/fetchers/set/items.mjs +27 -73
  19. package/dist/fetchers/set/property-values.d.mts +2 -3
  20. package/dist/fetchers/set/property-values.mjs +96 -130
  21. package/dist/fetchers/website-metadata.mjs +35 -57
  22. package/dist/fetchers/website.d.mts +2 -3
  23. package/dist/fetchers/website.mjs +22 -31
  24. package/dist/getters.d.mts +78 -148
  25. package/dist/getters.mjs +127 -208
  26. package/dist/helpers.d.mts +0 -4
  27. package/dist/helpers.mjs +19 -6
  28. package/dist/index.d.mts +8 -6
  29. package/dist/index.mjs +6 -4
  30. package/dist/ocr.d.mts +37 -0
  31. package/dist/ocr.mjs +52 -0
  32. package/dist/parsers/helpers.d.mts +21 -1
  33. package/dist/parsers/helpers.mjs +26 -6
  34. package/dist/parsers/index.d.mts +0 -8
  35. package/dist/parsers/index.mjs +118 -259
  36. package/dist/parsers/languages.d.mts +72 -0
  37. package/dist/parsers/languages.mjs +132 -0
  38. package/dist/parsers/multilingual.d.mts +49 -74
  39. package/dist/parsers/multilingual.mjs +88 -189
  40. package/dist/parsers/property-token.d.mts +34 -0
  41. package/dist/parsers/property-token.mjs +29 -0
  42. package/dist/parsers/string.d.mts +19 -0
  43. package/dist/parsers/string.mjs +45 -25
  44. package/dist/parsers/website/bounds.d.mts +10 -0
  45. package/dist/parsers/website/bounds.mjs +28 -0
  46. package/dist/parsers/website/components.d.mts +91 -0
  47. package/dist/parsers/website/components.mjs +681 -0
  48. package/dist/parsers/website/index.d.mts +0 -7
  49. package/dist/parsers/website/index.mjs +92 -1153
  50. package/dist/parsers/website/links.d.mts +36 -0
  51. package/dist/parsers/website/links.mjs +58 -0
  52. package/dist/parsers/website/messages.d.mts +24 -0
  53. package/dist/parsers/website/messages.mjs +31 -0
  54. package/dist/parsers/website/options.d.mts +6 -0
  55. package/dist/parsers/website/options.mjs +114 -0
  56. package/dist/parsers/website/properties.d.mts +12 -0
  57. package/dist/parsers/website/properties.mjs +158 -0
  58. package/dist/parsers/website/reader.d.mts +54 -4
  59. package/dist/parsers/website/reader.mjs +65 -20
  60. package/dist/parsers/website/slug.d.mts +64 -0
  61. package/dist/parsers/website/slug.mjs +82 -0
  62. package/dist/parsers/website/styles.d.mts +28 -0
  63. package/dist/parsers/website/styles.mjs +103 -0
  64. package/dist/parsers/website/walk.d.mts +68 -0
  65. package/dist/parsers/website/walk.mjs +116 -0
  66. package/dist/query.d.mts +66 -18
  67. package/dist/query.mjs +202 -48
  68. package/dist/reflection.d.mts +64 -0
  69. package/dist/reflection.mjs +79 -0
  70. package/dist/schemas.d.mts +7 -0
  71. package/dist/schemas.mjs +12 -3
  72. package/dist/types/index.d.mts +1 -31
  73. package/dist/types/utilities.d.mts +9 -0
  74. package/dist/types/utilities.mjs +1 -0
  75. package/dist/types/website.d.mts +49 -58
  76. package/dist/xml/metadata.d.mts +16 -0
  77. package/dist/xml/metadata.mjs +32 -11
  78. package/dist/xml/schemas.d.mts +5970 -3
  79. package/dist/xml/schemas.mjs +43 -45
  80. package/dist/xml/types.d.mts +13 -30
  81. package/dist/xquery.d.mts +46 -0
  82. package/dist/xquery.mjs +66 -0
  83. package/package.json +3 -3
  84. package/dist/utilities.d.mts +0 -54
@@ -0,0 +1,64 @@
1
+ //#region src/parsers/website/slug.d.ts
2
+ /**
3
+ * How a website page slug is built
4
+ *
5
+ * The rule runs in two places: here, while parsing a website, and inside the
6
+ * website metadata fetcher's XQuery, which resolves a slug to a page without
7
+ * shipping the whole tree. Both adapters live in this module so the rule
8
+ * cannot drift. The only difference between them is how "no prefix" is
9
+ * spelled: `undefined` in TypeScript, the empty string in XQuery, which has no
10
+ * way to pass an absent argument.
11
+ */
12
+ /**
13
+ * The prefix OCHRE puts on a segment page's slug to keep it unique
14
+ */
15
+ export declare const SEGMENT_UNIQUE_SLUG_PREFIX_PATTERN: string;
16
+ /**
17
+ * The separator between a slug and its prefix
18
+ */
19
+ export declare const WEBSITE_PAGE_SLUG_SEPARATOR = "/";
20
+ /**
21
+ * The full slug of every page in a website, keyed by the page's UUID
22
+ *
23
+ * Resolved before parsing begins, because a link on any page can point at any
24
+ * other page and the target's slug depends on where that target sits in the
25
+ * tree.
26
+ * @internal
27
+ */
28
+ export type WebsitePageSlugs = ReadonlyMap<string, string>;
29
+ /**
30
+ * Strip the uniqueness prefix from a website page slug
31
+ * @param slug - The raw slug
32
+ * @returns The cleaned slug, or null when there was none
33
+ * @internal
34
+ */
35
+ export declare function cleanWebsitePageSlug(slug: string | undefined): string | null;
36
+ /**
37
+ * Join a website page slug onto the slug of the segment holding it
38
+ * @param slug - The page slug
39
+ * @param slugPrefix - The slug of the enclosing segment, if any
40
+ * @returns The full slug
41
+ * @internal
42
+ */
43
+ export declare function prefixSlug(slug: string, slugPrefix: string | undefined): string;
44
+ /**
45
+ * The slug a page gives its own child pages
46
+ *
47
+ * A page only prefixes its children once it is itself inside a segment, so a
48
+ * top-level website keeps flat page slugs.
49
+ * @param pageSlug - The page's own full slug
50
+ * @param slugPrefix - The prefix the page was resolved with
51
+ * @returns The prefix for the page's children, or undefined for no prefix
52
+ * @internal
53
+ */
54
+ export declare function childSlugPrefix(pageSlug: string, slugPrefix: string | undefined): string | undefined;
55
+ /**
56
+ * The XQuery counterpart of {@link cleanWebsitePageSlug},
57
+ * {@link prefixSlug} and {@link childSlugPrefix}
58
+ *
59
+ * Declares `local:clean-slug`, `local:page-slug` and
60
+ * `local:page-child-slug-prefix`. "No prefix" is the empty string here.
61
+ * @internal
62
+ */
63
+ export declare const WEBSITE_PAGE_SLUG_DECLARATIONS: string;
64
+ //#endregion
@@ -0,0 +1,82 @@
1
+ import { stringLiteral } from "../../xquery.mjs";
2
+ //#region src/parsers/website/slug.ts
3
+ /**
4
+ * How a website page slug is built
5
+ *
6
+ * The rule runs in two places: here, while parsing a website, and inside the
7
+ * website metadata fetcher's XQuery, which resolves a slug to a page without
8
+ * shipping the whole tree. Both adapters live in this module so the rule
9
+ * cannot drift. The only difference between them is how "no prefix" is
10
+ * spelled: `undefined` in TypeScript, the empty string in XQuery, which has no
11
+ * way to pass an absent argument.
12
+ */
13
+ /**
14
+ * The prefix OCHRE puts on a segment page's slug to keep it unique
15
+ */
16
+ const SEGMENT_UNIQUE_SLUG_PREFIX_PATTERN = String.raw`^\$[^-]*-`;
17
+ /**
18
+ * The separator between a slug and its prefix
19
+ */
20
+ const WEBSITE_PAGE_SLUG_SEPARATOR = "/";
21
+ const SEGMENT_UNIQUE_SLUG_PREFIX_REGEX = new RegExp(SEGMENT_UNIQUE_SLUG_PREFIX_PATTERN);
22
+ /**
23
+ * Strip the uniqueness prefix from a website page slug
24
+ * @param slug - The raw slug
25
+ * @returns The cleaned slug, or null when there was none
26
+ * @internal
27
+ */
28
+ function cleanWebsitePageSlug(slug) {
29
+ return slug?.replace(SEGMENT_UNIQUE_SLUG_PREFIX_REGEX, "") ?? null;
30
+ }
31
+ /**
32
+ * Join a website page slug onto the slug of the segment holding it
33
+ * @param slug - The page slug
34
+ * @param slugPrefix - The slug of the enclosing segment, if any
35
+ * @returns The full slug
36
+ * @internal
37
+ */
38
+ function prefixSlug(slug, slugPrefix) {
39
+ if (slugPrefix === "" || slugPrefix == null) return slug;
40
+ if (slug === "") return slugPrefix;
41
+ return `${slugPrefix}/${slug}`;
42
+ }
43
+ /**
44
+ * The slug a page gives its own child pages
45
+ *
46
+ * A page only prefixes its children once it is itself inside a segment, so a
47
+ * top-level website keeps flat page slugs.
48
+ * @param pageSlug - The page's own full slug
49
+ * @param slugPrefix - The prefix the page was resolved with
50
+ * @returns The prefix for the page's children, or undefined for no prefix
51
+ * @internal
52
+ */
53
+ function childSlugPrefix(pageSlug, slugPrefix) {
54
+ return slugPrefix == null ? void 0 : pageSlug;
55
+ }
56
+ /**
57
+ * The XQuery counterpart of {@link cleanWebsitePageSlug},
58
+ * {@link prefixSlug} and {@link childSlugPrefix}
59
+ *
60
+ * Declares `local:clean-slug`, `local:page-slug` and
61
+ * `local:page-child-slug-prefix`. "No prefix" is the empty string here.
62
+ * @internal
63
+ */
64
+ const WEBSITE_PAGE_SLUG_DECLARATIONS = `declare function local:clean-slug($slug) {
65
+ replace(string($slug), ${stringLiteral(SEGMENT_UNIQUE_SLUG_PREFIX_PATTERN)}, "")
66
+ };
67
+
68
+ declare function local:page-slug($resource, $slug-prefix) {
69
+ let $slug := local:clean-slug($resource/@slug)
70
+ return
71
+ if ($slug-prefix = "") then $slug
72
+ else if ($slug = "") then $slug-prefix
73
+ else concat($slug-prefix, ${stringLiteral("/")}, $slug)
74
+ };
75
+
76
+ declare function local:page-child-slug-prefix($resource, $slug-prefix) {
77
+ if ($slug-prefix = "") then ""
78
+ else local:page-slug($resource, $slug-prefix)
79
+ };
80
+ `;
81
+ //#endregion
82
+ export { SEGMENT_UNIQUE_SLUG_PREFIX_PATTERN, WEBSITE_PAGE_SLUG_DECLARATIONS, WEBSITE_PAGE_SLUG_SEPARATOR, childSlugPrefix, cleanWebsitePageSlug, prefixSlug };
@@ -0,0 +1,28 @@
1
+ import { ResponsiveStyles, StylesheetItem } from "../../types/website.mjs";
2
+ import { SimplifiedProperty } from "../../types/index.mjs";
3
+ import { XMLWebsiteStyle } from "../../xml/types.mjs";
4
+ //#region src/parsers/website/styles.d.ts
5
+ /**
6
+ * The CSS declarations for every viewport a resource defines
7
+ * @param properties - Array of properties to parse
8
+ * @returns The styles per viewport
9
+ * @internal
10
+ */
11
+ export declare function parseResponsiveCssStyles<T extends ReadonlyArray<string>>(properties: ReadonlyArray<SimplifiedProperty<T>>): ResponsiveStyles;
12
+ /**
13
+ * An empty set of responsive styles, for resources that define none
14
+ * @returns The empty styles
15
+ * @internal
16
+ */
17
+ export declare function emptyResponsiveStyles(): ResponsiveStyles;
18
+ /**
19
+ * The stylesheet a website attaches to a property variable or value
20
+ *
21
+ * OCHRE has no viewport dimension on stylesheet entries, so these only ever
22
+ * carry default styles, unlike the per-element styles above.
23
+ * @param styles - The raw stylesheet entries
24
+ * @returns The parsed stylesheet items
25
+ * @internal
26
+ */
27
+ export declare function parseStylesheets(styles: Array<XMLWebsiteStyle>): Array<StylesheetItem>;
28
+ //#endregion
@@ -0,0 +1,103 @@
1
+ import { getProperty } from "../../getters.mjs";
2
+ //#region src/parsers/website/styles.ts
3
+ const STYLESHEET_NON_STYLE_KEYS = /* @__PURE__ */ new Set([
4
+ "variableUuid",
5
+ "valueUuid",
6
+ "category",
7
+ "payload",
8
+ "content"
9
+ ]);
10
+ /**
11
+ * The CSS declarations a presentation variant carries
12
+ *
13
+ * @param properties - Array of properties to parse
14
+ * @param cssVariant - CSS variant to parse
15
+ * @returns Array of CSS styles
16
+ */
17
+ function parseCssStylesFromProperties(properties, cssVariant) {
18
+ const cssProperties = getProperty(properties, {
19
+ label: "presentation",
20
+ valueContent: cssVariant != null ? `css-${cssVariant}` : "css"
21
+ })?.properties ?? [];
22
+ const styles = [];
23
+ for (const property of cssProperties) {
24
+ const value = property.values[0]?.content.toString();
25
+ if (value != null) styles.push({
26
+ label: property.variable.label,
27
+ value
28
+ });
29
+ }
30
+ return styles;
31
+ }
32
+ /**
33
+ * The CSS declarations for every viewport a resource defines
34
+ * @param properties - Array of properties to parse
35
+ * @returns The styles per viewport
36
+ * @internal
37
+ */
38
+ function parseResponsiveCssStyles(properties) {
39
+ return {
40
+ default: parseCssStylesFromProperties(properties),
41
+ tablet: parseCssStylesFromProperties(properties, "tablet"),
42
+ mobile: parseCssStylesFromProperties(properties, "mobile")
43
+ };
44
+ }
45
+ /**
46
+ * An empty set of responsive styles, for resources that define none
47
+ * @returns The empty styles
48
+ * @internal
49
+ */
50
+ function emptyResponsiveStyles() {
51
+ return {
52
+ default: [],
53
+ tablet: [],
54
+ mobile: []
55
+ };
56
+ }
57
+ /**
58
+ * The stylesheet a website attaches to a property variable or value
59
+ *
60
+ * OCHRE has no viewport dimension on stylesheet entries, so these only ever
61
+ * carry default styles, unlike the per-element styles above.
62
+ * @param styles - The raw stylesheet entries
63
+ * @returns The parsed stylesheet items
64
+ * @internal
65
+ */
66
+ function parseStylesheets(styles) {
67
+ const parsedStyles = [];
68
+ for (const style of styles) {
69
+ const defaultStyles = [];
70
+ for (const [label, value] of Object.entries(style)) {
71
+ if (STYLESHEET_NON_STYLE_KEYS.has(label)) continue;
72
+ const valueString = value?.toString();
73
+ if (valueString != null) defaultStyles.push({
74
+ label,
75
+ value: valueString
76
+ });
77
+ }
78
+ const stylesByViewport = {
79
+ ...emptyResponsiveStyles(),
80
+ default: defaultStyles
81
+ };
82
+ if (style.category === "propertyValue" || style.valueUuid != null) {
83
+ if (style.valueUuid == null) throw new Error(`Stylesheet property value "${style.variableUuid}" is missing a value UUID`, { cause: style });
84
+ parsedStyles.push({
85
+ uuid: style.valueUuid,
86
+ category: "propertyValue",
87
+ variableUuid: style.variableUuid,
88
+ icon: style.lucideIcon ?? null,
89
+ styles: stylesByViewport
90
+ });
91
+ continue;
92
+ }
93
+ parsedStyles.push({
94
+ uuid: style.variableUuid,
95
+ category: "propertyVariable",
96
+ icon: style.lucideIcon ?? null,
97
+ styles: stylesByViewport
98
+ });
99
+ }
100
+ return parsedStyles;
101
+ }
102
+ //#endregion
103
+ export { emptyResponsiveStyles, parseResponsiveCssStyles, parseStylesheets };
@@ -0,0 +1,68 @@
1
+ import { XMLWebsiteResource, XMLWebsiteResourceItem, XMLWebsiteTree } from "../../xml/types.mjs";
2
+ import { ParserOptions } from "../helpers.mjs";
3
+ import { WebsitePageSlugs } from "./slug.mjs";
4
+ //#region src/parsers/website/walk.d.ts
5
+ /**
6
+ * A page in a website, with its slug already resolved
7
+ *
8
+ * OCHRE stores a page's slug relative to the segment holding it, so the full
9
+ * slug depends on the path taken to reach the page. That path is walked once,
10
+ * here, and both the page parser and the slug map read the result instead of
11
+ * each re-deriving it.
12
+ * @internal
13
+ */
14
+ export type WebsitePageNode = {
15
+ resource: XMLWebsiteResource;
16
+ slug: string;
17
+ children: Array<WebsitePageNode>;
18
+ segments: Array<{
19
+ tree: XMLWebsiteTree;
20
+ slugPrefix: string;
21
+ }>;
22
+ };
23
+ /**
24
+ * Flatten the wrapper elements OCHRE nests resources in
25
+ *
26
+ * A `<resource>` holding only more resources is grouping, not content. Note
27
+ * that this drops segment wrappers, which carry neither an identification nor
28
+ * a nested resource list; {@link readWebsitePages} picks those up separately,
29
+ * because a segment becomes its own website rather than a resource.
30
+ * @param resources - The raw resource items
31
+ * @returns The identified resources, in source order
32
+ * @internal
33
+ */
34
+ export declare function normalizeWebsiteResources(resources: Array<XMLWebsiteResourceItem> | undefined): Array<XMLWebsiteResource>;
35
+ /**
36
+ * The pages directly under a set of resources, and everything under them
37
+ *
38
+ * Only pages carry routes, so this descends through pages and the segments
39
+ * they hold and stops at anything else. Blocks and elements are the page's
40
+ * content and are parsed by the page, not walked for further pages.
41
+ * @param resources - The raw resource items to read
42
+ * @param options - Parser options
43
+ * @param slugPrefix - The slug of the enclosing segment, if any
44
+ * @returns The page nodes, in source order
45
+ * @internal
46
+ */
47
+ export declare function readWebsitePages<T extends ReadonlyArray<string>>(resources: Array<XMLWebsiteResourceItem> | undefined, options: ParserOptions<T>, slugPrefix?: string): Array<WebsitePageNode>;
48
+ /**
49
+ * Every page's full slug, keyed by UUID, including pages inside segments
50
+ * @param pages - The page nodes to collect from
51
+ * @param options - Parser options
52
+ * @param pageSlugsByUuid - The map to fill, for the recursive calls
53
+ * @returns The filled map
54
+ * @internal
55
+ */
56
+ export declare function collectWebsitePageSlugs<T extends ReadonlyArray<string>>(pages: ReadonlyArray<WebsitePageNode>, options: ParserOptions<T>, pageSlugsByUuid?: Map<string, string>): WebsitePageSlugs;
57
+ /**
58
+ * The XQuery counterpart of {@link normalizeWebsiteResources} and the page
59
+ * descent in {@link readWebsitePages}
60
+ *
61
+ * Declares `local:resource-items`, `local:presentation` and, from
62
+ * {@link WEBSITE_PAGE_SLUG_DECLARATIONS}, the slug rules. Segment wrappers are
63
+ * kept here rather than dropped, because the caller filters for pages by
64
+ * presentation and reads segments off the same sequence.
65
+ * @internal
66
+ */
67
+ export declare const WEBSITE_WALK_DECLARATIONS: string;
68
+ //#endregion
@@ -0,0 +1,116 @@
1
+ import { parseStringContent } from "../helpers.mjs";
2
+ import { parseSimplifiedProperties } from "../index.mjs";
3
+ import { stringLiteral } from "../../xquery.mjs";
4
+ import { formatXMLWebsiteResourceMetadata } from "./messages.mjs";
5
+ import { websitePresentationReader } from "./reader.mjs";
6
+ import { WEBSITE_PAGE_SLUG_DECLARATIONS, childSlugPrefix, cleanWebsitePageSlug, prefixSlug } from "./slug.mjs";
7
+ //#region src/parsers/website/walk.ts
8
+ /**
9
+ * Flatten the wrapper elements OCHRE nests resources in
10
+ *
11
+ * A `<resource>` holding only more resources is grouping, not content. Note
12
+ * that this drops segment wrappers, which carry neither an identification nor
13
+ * a nested resource list; {@link readWebsitePages} picks those up separately,
14
+ * because a segment becomes its own website rather than a resource.
15
+ * @param resources - The raw resource items
16
+ * @returns The identified resources, in source order
17
+ * @internal
18
+ */
19
+ function normalizeWebsiteResources(resources) {
20
+ const normalized = [];
21
+ const resourcesToNormalize = resources ?? [];
22
+ for (const resource of resourcesToNormalize) {
23
+ if ("identification" in resource) {
24
+ normalized.push(resource);
25
+ continue;
26
+ }
27
+ if ("resource" in resource) normalized.push(...resource.resource);
28
+ }
29
+ return normalized;
30
+ }
31
+ function readWebsiteSegments(resources, options, slugPrefix) {
32
+ const segments = [];
33
+ const segmentResources = resources ?? [];
34
+ for (const resource of segmentResources) {
35
+ if (!("segments" in resource)) continue;
36
+ for (const tree of resource.segments.tree) {
37
+ const segmentSlug = tree.identification.abbreviation == null ? null : parseStringContent(tree.identification.abbreviation, options);
38
+ if (segmentSlug == null) throw new Error(`Slug not found for segment website (website uuid “${tree.uuid}”)`, { cause: tree });
39
+ segments.push({
40
+ tree,
41
+ slugPrefix: prefixSlug(segmentSlug, slugPrefix)
42
+ });
43
+ }
44
+ }
45
+ return segments;
46
+ }
47
+ /**
48
+ * The pages directly under a set of resources, and everything under them
49
+ *
50
+ * Only pages carry routes, so this descends through pages and the segments
51
+ * they hold and stops at anything else. Blocks and elements are the page's
52
+ * content and are parsed by the page, not walked for further pages.
53
+ * @param resources - The raw resource items to read
54
+ * @param options - Parser options
55
+ * @param slugPrefix - The slug of the enclosing segment, if any
56
+ * @returns The page nodes, in source order
57
+ * @internal
58
+ */
59
+ function readWebsitePages(resources, options, slugPrefix) {
60
+ const pages = [];
61
+ for (const resource of normalizeWebsiteResources(resources)) {
62
+ const properties = parseSimplifiedProperties(resource.properties, options);
63
+ if (websitePresentationReader(properties).value("presentation") !== "page") continue;
64
+ const slug = cleanWebsitePageSlug(resource.slug);
65
+ if (slug == null) throw new Error(`Slug not found for page (${formatXMLWebsiteResourceMetadata(resource)})`, { cause: resource });
66
+ const pageSlug = prefixSlug(slug, slugPrefix);
67
+ pages.push({
68
+ resource,
69
+ slug: pageSlug,
70
+ children: readWebsitePages(resource.resource, options, childSlugPrefix(pageSlug, slugPrefix)),
71
+ segments: readWebsiteSegments(resource.resource, options, pageSlug)
72
+ });
73
+ }
74
+ return pages;
75
+ }
76
+ /**
77
+ * Every page's full slug, keyed by UUID, including pages inside segments
78
+ * @param pages - The page nodes to collect from
79
+ * @param options - Parser options
80
+ * @param pageSlugsByUuid - The map to fill, for the recursive calls
81
+ * @returns The filled map
82
+ * @internal
83
+ */
84
+ function collectWebsitePageSlugs(pages, options, pageSlugsByUuid = /* @__PURE__ */ new Map()) {
85
+ for (const page of pages) {
86
+ pageSlugsByUuid.set(page.resource.uuid, page.slug);
87
+ collectWebsitePageSlugs(page.children, options, pageSlugsByUuid);
88
+ for (const segment of page.segments) collectWebsitePageSlugs(readWebsitePages(segment.tree.items?.resource, options, segment.slugPrefix), options, pageSlugsByUuid);
89
+ }
90
+ return pageSlugsByUuid;
91
+ }
92
+ /**
93
+ * The XQuery counterpart of {@link normalizeWebsiteResources} and the page
94
+ * descent in {@link readWebsitePages}
95
+ *
96
+ * Declares `local:resource-items`, `local:presentation` and, from
97
+ * {@link WEBSITE_PAGE_SLUG_DECLARATIONS}, the slug rules. Segment wrappers are
98
+ * kept here rather than dropped, because the caller filters for pages by
99
+ * presentation and reads segments off the same sequence.
100
+ * @internal
101
+ */
102
+ const WEBSITE_WALK_DECLARATIONS = `declare function local:resource-items($resources) {
103
+ for $resource in $resources
104
+ return
105
+ if ($resource/segments) then $resource
106
+ else if ($resource/identification) then $resource
107
+ else local:resource-items($resource/resource)
108
+ };
109
+
110
+ declare function local:presentation($resource) {
111
+ string(($resource/properties/property[label/string() = ${stringLiteral("presentation")}]/value)[1])
112
+ };
113
+
114
+ ${WEBSITE_PAGE_SLUG_DECLARATIONS}`;
115
+ //#endregion
116
+ export { WEBSITE_WALK_DECLARATIONS, collectWebsitePageSlugs, normalizeWebsiteResources, readWebsitePages };
package/dist/query.d.mts CHANGED
@@ -1,24 +1,20 @@
1
- import { Query } from "./types/index.mjs";
1
+ import { PropertyRelation, Query } from "./types/index.mjs";
2
+ import { OchreQueryContext } from "./xquery.mjs";
2
3
  //#region src/query.d.ts
3
- export declare function buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids: Array<string>, belongsToCollectionPropertyVariableUuid: string): string | null;
4
+ export declare function buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids: ReadonlyArray<string>, belongsToCollectionPropertyVariableUuid: string): string | null;
4
5
  /**
5
- * Compile a query tree into the XQuery `let` clauses that bind `$items` to the
6
- * matching Set items
6
+ * Compile a query tree into the clauses that bind the matching Set items
7
7
  *
8
- * Most queries compile to a single `cts:search` over the Set item projections.
9
- * An `ocr` leaf cannot: the projections drop the `<ocr>` layer, so it resolves
10
- * to a search over the Resource documents whose matching UUIDs are joined back
11
- * in as an item path predicate. Path predicates only ever AND, so an `ocr` leaf
12
- * that sits under an `or` becomes its own arm of a node union instead, and one
13
- * that sits under an `and` alongside a union becomes an intersection.
14
- *
15
- * The searchable path has to stay inline in `cts:search`: binding it to a
16
- * variable first makes every query XDMP-UNSEARCHABLE.
17
- * @param parameters - The parameters for the compilation
18
- * @param parameters.queries - Recursive query tree to compile, if any
19
- * @param parameters.baseItemsExpression - The inline XQuery path selecting the items to search
20
- * @param parameters.scopeQueryExpression - An optional CTS query ANDed into every compiled search
21
- * @returns The prolog declaring the query helpers, and the `let` clauses binding `$items`
8
+ * The returned `itemsClause` binds {@link ITEMS_VARIABLE} and has to be placed
9
+ * inside an XQuery body, with `prolog` declared ahead of it.
10
+ * {@link compileSetItemsQuery} does both and is what fetchers should use;
11
+ * this is exposed for tests that assert on the compiled CTS.
12
+ * @param parameters - The plan parameters
13
+ * @param parameters.queries - The query tree to compile, or null to match every item
14
+ * @param parameters.baseItemsExpression - The inline searchable path to filter
15
+ * @param parameters.scopeQueryExpression - An extra query AND-ed into every search
16
+ * @returns The prolog, the clauses binding the items, and the bound CTS queries
17
+ * @internal
22
18
  */
23
19
  export declare function buildQueryPlan(parameters: {
24
20
  queries: Query | null;
@@ -27,5 +23,57 @@ export declare function buildQueryPlan(parameters: {
27
23
  }): {
28
24
  prolog: string;
29
25
  itemsClause: string;
26
+ itemsVariable: string;
27
+ queryBindings: Array<{
28
+ name: string;
29
+ expression: string;
30
+ }>;
30
31
  };
32
+ /**
33
+ * Compile a Set item query into a complete XQuery document
34
+ *
35
+ * Owns everything a caller would otherwise have to know and restate: the
36
+ * version declaration, the Set scope variable, the supplemental-stripping
37
+ * prolog, the inline searchable path, where the compiled helper prolog goes and
38
+ * that it is only declared when non-empty, the `<ochre>` wrapper, and the name
39
+ * of the variable holding the matching items. The body receives that name.
40
+ * @param parameters - The query parameters
41
+ * @param parameters.setScopeUuids - The Set scope UUIDs to search within
42
+ * @param parameters.belongsToCollectionScopeUuids - Collection scope UUIDs to narrow to
43
+ * @param parameters.queries - The query tree to compile, or null to match every item
44
+ * @param parameters.declarations - Extra prolog declarations, placed before the compiled prolog
45
+ * @param parameters.body - Builds the body from the name of the variable holding the items
46
+ * @returns A complete XQuery document
47
+ * @internal
48
+ */
49
+ export declare function compileSetItemsQuery(parameters: {
50
+ setScopeUuids: ReadonlyArray<string>;
51
+ belongsToCollectionScopeUuids: ReadonlyArray<string>;
52
+ queries: Query | null;
53
+ declarations?: ReadonlyArray<string>;
54
+ body: (context: OchreQueryContext & {
55
+ items: string;
56
+ }) => string;
57
+ }): string;
58
+ /**
59
+ * Reduce a property-value facet query tree to the leaves that filter items
60
+ *
61
+ * A facet request carries property leaves that name a variable without naming
62
+ * a value, which select what to aggregate rather than which items to keep.
63
+ * Those are dropped, and groups left with a single child collapse into it.
64
+ * @param queries - The query tree to reduce
65
+ * @returns The reduced tree, or null when nothing filters items
66
+ * @internal
67
+ */
68
+ export declare function getItemFilterQueries(queries: Query | null): Query | null;
69
+ /**
70
+ * Collect the property variables a query tree asks to be aggregated
71
+ * @param queries - The query tree to walk
72
+ * @returns One selector per distinct property variable and relation pair
73
+ * @internal
74
+ */
75
+ export declare function getPropertyFacetSelectors(queries: Query | null): Array<{
76
+ uuid: string;
77
+ relation: PropertyRelation | null;
78
+ }>;
31
79
  //#endregion