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.
@@ -42,19 +42,23 @@ const ITEM_CATEGORIES = [
42
42
  "set"
43
43
  ];
44
44
  const XMLItemCategory = v.picklist(ITEM_CATEGORIES);
45
+ const XMLRichTextEnvelope = {
46
+ links: v.optional(v.lazy(() => XMLLink)),
47
+ properties: v.optional(v.object({ property: v.array(v.lazy(() => XMLProperty), "XMLRichTextEnvelope: properties is array of XMLProperty") })),
48
+ annotation: v.optional(v.string("XMLRichTextEnvelope: annotation is string and optional"))
49
+ };
45
50
  const XMLString = v.lazy(() => v.object({
46
51
  payload: v.optional(v.string("XMLString: payload is string and optional")),
47
52
  rend: v.optional(v.string("XMLString: rend is string and optional")),
48
53
  whitespace: v.optional(v.string("XMLString: whitespace is string and optional")),
49
- links: v.optional(v.lazy(() => XMLLink)),
50
- properties: v.optional(v.object({ property: v.array(v.lazy(() => XMLProperty), "XMLString: properties is array of XMLProperty") })),
51
- annotation: v.optional(v.string("XMLString: annotation is string and optional")),
54
+ ...XMLRichTextEnvelope,
52
55
  string: v.optional(v.array(XMLString, "XMLString: string is array of XMLString"))
53
56
  }, "XMLString: Shape error"));
54
57
  const XMLContent = v.object({ content: v.array(v.object({
55
58
  string: v.array(XMLString),
56
59
  title: v.optional(v.string("XMLContent: title is string and optional")),
57
- lang: v.string("XMLContent: lang is string and required")
60
+ lang: v.string("XMLContent: lang is string and required"),
61
+ ...XMLRichTextEnvelope
58
62
  }), "XMLContent: content is array of object with string, title, and lang") }, "XMLContent: Shape error");
59
63
  const XMLNumber = v.pipe(v.union([v.string("XMLNumber: string is string and required"), XMLString]), v.check(isXMLNumber, "XMLNumber: string is not a number"), v.transform(parseXMLNumber));
60
64
  const XMLOptionalNumber = v.optional(v.pipe(v.union([v.string("XMLNumber: string is string and required"), XMLString]), v.check(isOptionalXMLNumber, "XMLNumber: string is not a number"), v.transform(parseOptionalXMLNumber)));
@@ -220,42 +224,6 @@ const XMLImageMap = v.object({
220
224
  width: XMLNumber,
221
225
  height: XMLNumber
222
226
  }, "XMLImageMap: Shape error");
223
- const XMLOcrString = v.object({
224
- HPOS: XMLNumber,
225
- VPOS: XMLNumber,
226
- WIDTH: XMLNumber,
227
- HEIGHT: XMLNumber,
228
- CONTENT: v.string("XMLOcrString: CONTENT is string and required"),
229
- VERTICES: v.optional(v.string("XMLOcrString: VERTICES is string and optional"))
230
- }, "XMLOcrString: Shape error");
231
- const XMLOcrTextLine = v.object({ string: v.optional(v.array(XMLOcrString, "XMLOcrTextLine: string is array of XMLOcrString")) }, "XMLOcrTextLine: Shape error");
232
- const XMLOcrTextBlock = v.object({ TextLine: v.optional(v.array(XMLOcrTextLine, "XMLOcrTextBlock: TextLine is array of XMLOcrTextLine")) }, "XMLOcrTextBlock: Shape error");
233
- const XMLOcrPage = v.object({
234
- n: XMLOptionalNumber,
235
- fileName: v.optional(v.string("XMLOcrPage: fileName is string and optional")),
236
- WIDTH: XMLOptionalNumber,
237
- HEIGHT: XMLOptionalNumber,
238
- TextBlock: v.optional(v.array(XMLOcrTextBlock, "XMLOcrPage: TextBlock is array of XMLOcrTextBlock"))
239
- }, "XMLOcrPage: Shape error");
240
- const XMLOcr = v.object({ Page: v.optional(v.array(XMLOcrPage, "XMLOcr: Page is array of XMLOcrPage")) }, "XMLOcr: Shape error");
241
- const XMLOcrMatch = v.object({
242
- resourceUuid: v.optional(v.string("XMLOcrMatch: resourceUuid is string and optional")),
243
- n: XMLOptionalNumber,
244
- fileName: v.optional(v.string("XMLOcrMatch: fileName is string and optional")),
245
- WIDTH: XMLOptionalNumber,
246
- HEIGHT: XMLOptionalNumber,
247
- string: v.optional(v.array(XMLOcrString, "XMLOcrMatch: string is array of XMLOcrString"))
248
- }, "XMLOcrMatch: Shape error");
249
- const XMLOcrMatchItem = v.object({
250
- uuid: v.pipe(v.string("XMLOcrMatchItem: uuid is string and required"), v.check(isPseudoUuid, "XMLOcrMatchItem: uuid is not a valid pseudo-UUID")),
251
- matchCount: XMLNumber,
252
- ocrMatch: v.optional(v.array(XMLOcrMatch, "XMLOcrMatchItem: ocrMatch is array of XMLOcrMatch"))
253
- }, "XMLOcrMatchItem: Shape error");
254
- /**
255
- * Schema for validating OCR matches fetched from the OCHRE API
256
- * @internal
257
- */
258
- const XMLOcrMatchesData = v.object({ result: v.object({ ochre: v.object({ ocrMatches: v.optional(v.object({ ocrItem: v.optional(v.array(XMLOcrMatchItem, "XMLOcrMatchesData: ocrItem is array of XMLOcrMatchItem")) })) }) }) }, "XMLOcrMatchesData: Shape error");
259
227
  const XMLNote = v.object({
260
228
  content: v.optional(XMLContent.entries.content),
261
229
  payload: v.optional(v.string("XMLNote: payload is string and optional")),
@@ -676,7 +644,6 @@ const XMLResource = v.object({
676
644
  width: XMLOptionalNumber,
677
645
  image: v.optional(XMLImage),
678
646
  imagemap: v.optional(XMLImageMap),
679
- ocr: v.optional(XMLOcr),
680
647
  document: v.optional(XMLContent),
681
648
  coordinates: v.optional(XMLCoordinates),
682
649
  periods: v.optional(v.object({ period: v.array(XMLPeriod) })),
@@ -828,7 +795,6 @@ const XMLWebsiteResource = v.lazy(() => v.object({
828
795
  width: XMLOptionalNumber,
829
796
  image: v.optional(XMLImage),
830
797
  imagemap: v.optional(XMLImageMap),
831
- ocr: v.optional(XMLOcr),
832
798
  document: v.optional(XMLContent),
833
799
  coordinates: v.optional(XMLCoordinates),
834
800
  periods: v.optional(v.object({ period: v.array(XMLPeriod) })),
@@ -962,4 +928,4 @@ const XMLWebsiteData = v.object({ result: v.object({ ochre: v.object({
962
928
  tree: v.array(XMLWebsiteTree)
963
929
  }, "XMLWebsiteData: ochre is object with website tree") }, "XMLWebsiteData: result is object with ochre") }, "XMLWebsiteData: Shape error");
964
930
  //#endregion
965
- export { XMLData, XMLDataItem, XMLGalleryData, XMLItemLinksData, XMLLink, XMLOcrMatchesData, XMLSetItemsData, XMLWebsiteData };
931
+ export { XMLData, XMLDataItem, XMLGalleryData, XMLItemLinksData, XMLLink, XMLSetItemsData, XMLWebsiteData };
@@ -2,19 +2,26 @@
2
2
  type XMLItemCategory = "tree" | "bibliography" | "spatialUnit" | "concept" | "period" | "person" | "propertyVariable" | "variable" | "propertyValue" | "value" | "text" | "resource" | "set";
3
3
  type XMLHeadingItemCategory = Exclude<XMLItemCategory, "tree" | "bibliography" | "spatialUnit" | "concept" | "period">;
4
4
  type XMLRecursiveItemCategory = Exclude<XMLItemCategory, "tree" | "person" | "propertyVariable" | "propertyValue" | "set">;
5
- type XMLString = {
6
- payload?: string;
7
- rend?: string;
8
- whitespace?: string;
5
+ /**
6
+ * Rich text envelope shared by `<string>` and `<content>` nodes, carrying the
7
+ * inline links, presentation properties and annotation identifier that turn a
8
+ * span of text into an annotation.
9
+ */
10
+ type XMLRichTextEnvelope = {
9
11
  links?: XMLLink;
10
12
  properties?: {
11
13
  property: Array<XMLProperty>;
12
14
  };
13
15
  annotation?: string;
16
+ };
17
+ type XMLString = XMLRichTextEnvelope & {
18
+ payload?: string;
19
+ rend?: string;
20
+ whitespace?: string;
14
21
  string?: Array<XMLString>;
15
22
  };
16
23
  type XMLContent = {
17
- content: Array<{
24
+ content: Array<XMLRichTextEnvelope & {
18
25
  string: Array<XMLString>;
19
26
  lang: string;
20
27
  title?: string;
@@ -175,48 +182,6 @@ type XMLImageMap = {
175
182
  width: XMLNumber;
176
183
  height: XMLNumber;
177
184
  };
178
- type XMLOcrString = {
179
- HPOS: XMLNumber;
180
- VPOS: XMLNumber;
181
- WIDTH: XMLNumber;
182
- HEIGHT: XMLNumber;
183
- CONTENT: string;
184
- VERTICES?: string;
185
- };
186
- type XMLOcrTextLine = {
187
- string?: Array<XMLOcrString>;
188
- };
189
- type XMLOcrTextBlock = {
190
- TextLine?: Array<XMLOcrTextLine>;
191
- };
192
- type XMLOcrPage = {
193
- n?: XMLNumber;
194
- fileName?: string;
195
- WIDTH?: XMLNumber;
196
- HEIGHT?: XMLNumber;
197
- TextBlock?: Array<XMLOcrTextBlock>;
198
- };
199
- type XMLOcr = {
200
- Page?: Array<XMLOcrPage>;
201
- };
202
- type XMLOcrMatch = Omit<XMLOcrPage, "TextBlock"> & {
203
- resourceUuid?: string;
204
- string?: Array<XMLOcrString>;
205
- };
206
- type XMLOcrMatchItem = {
207
- uuid: string;
208
- matchCount: XMLNumber;
209
- ocrMatch?: Array<XMLOcrMatch>;
210
- };
211
- type XMLOcrMatchesData = {
212
- result: {
213
- ochre: {
214
- ocrMatches?: {
215
- ocrItem?: Array<XMLOcrMatchItem>;
216
- };
217
- };
218
- };
219
- };
220
185
  type XMLNote = Partial<XMLContent> & XMLString & {
221
186
  noteNo?: XMLNumber;
222
187
  title?: string;
@@ -598,7 +563,6 @@ type XMLResource = XMLBaseItem & {
598
563
  width?: XMLNumber;
599
564
  image?: XMLImage;
600
565
  imagemap?: XMLImageMap;
601
- ocr?: XMLOcr;
602
566
  document?: XMLContent;
603
567
  coordinates?: XMLCoordinates;
604
568
  periods?: {
@@ -956,4 +920,4 @@ type XMLWebsiteData = {
956
920
  };
957
921
  };
958
922
  //#endregion
959
- export { XMLBaseItem, XMLBibliography, XMLBoolean, XMLConcept, XMLContent, XMLContext, XMLContextGroup, XMLContextItem, XMLContextValue, XMLCoordinate, XMLCoordinates, XMLCoordinatesSource, XMLData, XMLDataItem, XMLDictionaryUnit, XMLEmptyContext, XMLEvent, XMLGallery, XMLGalleryData, XMLHeading, XMLHeadingItemCategory, XMLIdentification, XMLImage, XMLImageMap, XMLImageMapArea, XMLInterpretation, XMLItemCategory, XMLItemLinks, XMLItemLinksData, XMLLicense, XMLLink, XMLLinkedBaseItem, XMLLinkedBibliography, XMLLinkedConcept, XMLLinkedPeriod, XMLLinkedPerson, XMLLinkedPropertyValue, XMLLinkedPropertyVariable, XMLLinkedResource, XMLLinkedSet, XMLLinkedSpatialUnit, XMLLinkedText, XMLLinkedTree, XMLMetadata, XMLNote, XMLNumber, XMLObservation, XMLOcr, XMLOcrMatch, XMLOcrMatchItem, XMLOcrMatchesData, XMLOcrPage, XMLOcrString, XMLOcrTextBlock, XMLOcrTextLine, XMLPeriod, XMLPerson, XMLProperty, XMLPropertyRelation, XMLPropertyValue, XMLPropertyVariable, XMLRecursiveItemCategory, XMLResource, XMLSection, XMLSet, XMLSetItems, XMLSetItemsData, XMLSimplifiedProperty, XMLSpatialUnit, XMLString, XMLText, XMLTree, XMLWebsiteContext, XMLWebsiteContextItem, XMLWebsiteContextLevel, XMLWebsiteData, XMLWebsiteFilterContext, XMLWebsiteFilterContextItem, XMLWebsiteOptions, XMLWebsiteProperties, XMLWebsiteResource, XMLWebsiteResourceGroup, XMLWebsiteResourceItem, XMLWebsiteScope, XMLWebsiteSegment, XMLWebsiteStyle, XMLWebsiteTree };
923
+ export { XMLBaseItem, XMLBibliography, XMLBoolean, XMLConcept, XMLContent, XMLContext, XMLContextGroup, XMLContextItem, XMLContextValue, XMLCoordinate, XMLCoordinates, XMLCoordinatesSource, XMLData, XMLDataItem, XMLDictionaryUnit, XMLEmptyContext, XMLEvent, XMLGallery, XMLGalleryData, XMLHeading, XMLHeadingItemCategory, XMLIdentification, XMLImage, XMLImageMap, XMLImageMapArea, XMLInterpretation, XMLItemCategory, XMLItemLinks, XMLItemLinksData, XMLLicense, XMLLink, XMLLinkedBaseItem, XMLLinkedBibliography, XMLLinkedConcept, XMLLinkedPeriod, XMLLinkedPerson, XMLLinkedPropertyValue, XMLLinkedPropertyVariable, XMLLinkedResource, XMLLinkedSet, XMLLinkedSpatialUnit, XMLLinkedText, XMLLinkedTree, XMLMetadata, XMLNote, XMLNumber, XMLObservation, XMLPeriod, XMLPerson, XMLProperty, XMLPropertyRelation, XMLPropertyValue, XMLPropertyVariable, XMLRecursiveItemCategory, XMLResource, XMLRichTextEnvelope, XMLSection, XMLSet, XMLSetItems, XMLSetItemsData, XMLSimplifiedProperty, XMLSpatialUnit, XMLString, XMLText, XMLTree, XMLWebsiteContext, XMLWebsiteContextItem, XMLWebsiteContextLevel, XMLWebsiteData, XMLWebsiteFilterContext, XMLWebsiteFilterContextItem, XMLWebsiteOptions, XMLWebsiteProperties, XMLWebsiteResource, XMLWebsiteResourceGroup, XMLWebsiteResourceItem, XMLWebsiteScope, XMLWebsiteSegment, XMLWebsiteStyle, XMLWebsiteTree };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "1.0.71",
3
+ "version": "1.0.73",
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",
@@ -53,10 +53,10 @@
53
53
  "devDependencies": {
54
54
  "@antfu/eslint-config": "^9.2.0",
55
55
  "@types/node": "^24.13.3",
56
- "bumpp": "^12.1.1",
56
+ "bumpp": "^12.2.0",
57
57
  "eslint": "^10.8.0",
58
58
  "knip": "^6.31.0",
59
- "oxfmt": "^0.61.0",
59
+ "oxfmt": "^0.62.0",
60
60
  "tsdown": "^0.22.14",
61
61
  "typescript": "^6.0.3",
62
62
  "vitest": "^4.1.10"
@@ -1,44 +0,0 @@
1
- import { OcrMatch } from "../types/index.mjs";
2
- import { FetchFunction } from "../parsers/helpers.mjs";
3
- //#region src/fetchers/ocr-matches.d.ts
4
- /**
5
- * Fetches the locations of OCR text matches within OCHRE resources
6
- *
7
- * Matching mirrors the `ocr` Set item query target, so the same value and match
8
- * mode that selected an item will locate its hits. `matchCountsByUuid` reports
9
- * the untruncated count, which can exceed the returned matches when
10
- * `maxMatchesPerItem` caps them.
11
- *
12
- * @param parameters - The parameters for the fetch
13
- * @param parameters.uuids - The resource UUIDs to search, typically from a filtered Set item fetch
14
- * @param parameters.value - The search value
15
- * @param parameters.matchMode - Whether to match loosely (stemming and wildcards) or on whole OCR words, defaults to "includes"
16
- * @param parameters.isCaseSensitive - Whether matching is case sensitive, defaults to false
17
- * @param parameters.maxMatchesPerItem - The cap on returned matches per requested UUID, defaults to 50
18
- * @param options - Options for the fetch
19
- * @param options.fetch - The fetch function to use
20
- * @returns The OCR matches, or null if the fetch/parse fails
21
- */
22
- declare function fetchOcrMatches(parameters: {
23
- uuids: Array<string>;
24
- value: string;
25
- matchMode?: "includes" | "exact";
26
- isCaseSensitive?: boolean;
27
- maxMatchesPerItem?: number;
28
- }, options?: {
29
- fetch?: FetchFunction;
30
- }): Promise<{
31
- matches: Array<OcrMatch>;
32
- matchesByUuid: Record<string, Array<OcrMatch>>;
33
- matchCountsByUuid: Record<string, number>;
34
- error: null;
35
- detailedError: null;
36
- } | {
37
- matches: null;
38
- matchesByUuid: null;
39
- matchCountsByUuid: null;
40
- error: string;
41
- detailedError: string;
42
- }>;
43
- //#endregion
44
- export { fetchOcrMatches };
@@ -1,134 +0,0 @@
1
- import { XML_PARSER_OPTIONS } from "../constants.mjs";
2
- import { createSchemaValidationError, getErrorOutput, stringLiteral } from "../utilities.mjs";
3
- import { buildOcrTermQueryExpressions } from "../query.mjs";
4
- import { ocrMatchesParametersSchema } from "../schemas.mjs";
5
- import { restoreXMLMetadata } from "../xml/metadata.mjs";
6
- import { parseOcrMatches } from "../parsers/index.mjs";
7
- import { XMLOcrMatchesData } from "../xml/schemas.mjs";
8
- import * as v from "valibot";
9
- import { XMLParser } from "fast-xml-parser";
10
- //#region src/fetchers/ocr-matches.ts
11
- /**
12
- * Build an XQuery string to fetch OCR match locations from the OCHRE API
13
- *
14
- * Each OCR word is matched with `cts:contains` against the same per-term CTS
15
- * queries the Set item filter compiles, so hit locations always agree with what
16
- * the filter matched — including stemming and wildcards, which cannot be
17
- * reproduced outside MarkLogic.
18
- *
19
- * @param parameters - The parameters for the fetch
20
- * @param parameters.uuids - The resource UUIDs to search
21
- * @param parameters.termQueryExpressions - One CTS query expression per search term, in word order
22
- * @param parameters.maxMatchesPerItem - The cap on returned matches per requested UUID
23
- * @returns An XQuery string
24
- */
25
- function buildXQuery(parameters) {
26
- const { uuids, termQueryExpressions, maxMatchesPerItem } = parameters;
27
- return `xquery version "1.0-ml";
28
-
29
- declare variable $uuids := (${Array.from(uuids, (uuid) => stringLiteral(uuid)).join(", ")});
30
-
31
- declare variable $termQueries := (
32
- ${termQueryExpressions.join(",\n ")}
33
- );
34
-
35
- declare variable $termCount := ${termQueryExpressions.length};
36
-
37
- <ochre>{
38
- <ocrMatches>{
39
- for $uuid in $uuids
40
- let $matches :=
41
- for $page in doc($uuid)//ocr/Page
42
- let $words := $page//TextLine/string
43
- let $wordCount := count($words)
44
- let $resourceUuid := string($page/ancestor::resource[1]/@uuid)
45
- for $word at $index in $words
46
- where $index + $termCount - 1 le $wordCount
47
- and (every $offset in (1 to $termCount)
48
- satisfies cts:contains($words[$index + $offset - 1], $termQueries[$offset]))
49
- return <ocrMatch resourceUuid="{$resourceUuid}">{
50
- $page/@n, $page/@fileName, $page/@WIDTH, $page/@HEIGHT,
51
- subsequence($words, $index, $termCount)
52
- }</ocrMatch>
53
- return <ocrItem uuid="{$uuid}" matchCount="{count($matches)}">{
54
- subsequence($matches, 1, ${maxMatchesPerItem})
55
- }</ocrItem>
56
- }</ocrMatches>
57
- }</ochre>`;
58
- }
59
- /**
60
- * Fetches the locations of OCR text matches within OCHRE resources
61
- *
62
- * Matching mirrors the `ocr` Set item query target, so the same value and match
63
- * mode that selected an item will locate its hits. `matchCountsByUuid` reports
64
- * the untruncated count, which can exceed the returned matches when
65
- * `maxMatchesPerItem` caps them.
66
- *
67
- * @param parameters - The parameters for the fetch
68
- * @param parameters.uuids - The resource UUIDs to search, typically from a filtered Set item fetch
69
- * @param parameters.value - The search value
70
- * @param parameters.matchMode - Whether to match loosely (stemming and wildcards) or on whole OCR words, defaults to "includes"
71
- * @param parameters.isCaseSensitive - Whether matching is case sensitive, defaults to false
72
- * @param parameters.maxMatchesPerItem - The cap on returned matches per requested UUID, defaults to 50
73
- * @param options - Options for the fetch
74
- * @param options.fetch - The fetch function to use
75
- * @returns The OCR matches, or null if the fetch/parse fails
76
- */
77
- async function fetchOcrMatches(parameters, options) {
78
- try {
79
- const { uuids, value, matchMode, isCaseSensitive, maxMatchesPerItem } = v.parse(ocrMatchesParametersSchema, parameters);
80
- const termQueryExpressions = buildOcrTermQueryExpressions({
81
- value,
82
- matchMode,
83
- isCaseSensitive
84
- });
85
- if (termQueryExpressions.length === 0) return {
86
- matches: [],
87
- matchesByUuid: {},
88
- matchCountsByUuid: {},
89
- error: null,
90
- detailedError: null
91
- };
92
- const response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&xsl=none&lang=\"*\"", {
93
- method: "POST",
94
- body: buildXQuery({
95
- uuids,
96
- termQueryExpressions,
97
- maxMatchesPerItem
98
- }),
99
- headers: { "Content-Type": "application/xquery" }
100
- });
101
- if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`, { cause: response.statusText });
102
- const dataRaw = await response.text();
103
- const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
104
- const { success, issues, output } = v.safeParse(XMLOcrMatchesData, data);
105
- if (!success) throw createSchemaValidationError("Failed to parse OCHRE OCR matches", issues);
106
- restoreXMLMetadata(output, data);
107
- const rawOcrItems = output.result.ochre.ocrMatches?.ocrItem ?? [];
108
- const matches = parseOcrMatches(rawOcrItems);
109
- const matchesByUuid = {};
110
- const matchCountsByUuid = {};
111
- for (const uuid of uuids) {
112
- matchesByUuid[uuid] = [];
113
- matchCountsByUuid[uuid] = 0;
114
- }
115
- for (const rawOcrItem of rawOcrItems) matchCountsByUuid[rawOcrItem.uuid] = rawOcrItem.matchCount;
116
- for (const match of matches) matchesByUuid[match.uuid]?.push(match);
117
- return {
118
- matches,
119
- matchesByUuid,
120
- matchCountsByUuid,
121
- error: null,
122
- detailedError: null
123
- };
124
- } catch (error) {
125
- return {
126
- matches: null,
127
- matchesByUuid: null,
128
- matchCountsByUuid: null,
129
- ...getErrorOutput(error, "Unknown error")
130
- };
131
- }
132
- }
133
- //#endregion
134
- export { fetchOcrMatches };