@prose-reader/enhancer-search 1.264.0 → 1.266.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.
@@ -0,0 +1,13 @@
1
+ import { Reader } from '@prose-reader/core';
2
+ import { SearchResult } from './search';
3
+ import { ResultItem, SearchEnhancerAPI } from './types';
4
+ export type { SearchEnhancerAPI, ResultItem, SearchResult };
5
+ /**
6
+ * Contract of search enhancer.
7
+ *
8
+ * - At best a result match should be navigable. It means the search needs to
9
+ * be done on a rendered document. This is because rendering can differ from the original
10
+ * item resource. A resource can be something non digestible and very specific (.pdf). The search
11
+ * enhancer is agnostic and can only search into documents.
12
+ */
13
+ export declare const searchEnhancer: <InheritOptions, InheritOutput extends Reader>(next: (options: InheritOptions) => InheritOutput) => (options: InheritOptions) => InheritOutput & SearchEnhancerAPI;
package/dist/index.js ADDED
@@ -0,0 +1,172 @@
1
+ import { deferIdle } from '@prose-reader/core';
2
+ import { of, defer, forkJoin } from 'rxjs';
3
+ import { map, switchMap, finalize, catchError } from 'rxjs/operators';
4
+
5
+ const h = () => {
6
+ if (!(typeof window > "u"))
7
+ return window;
8
+ };
9
+ function $() {
10
+ const o = h()?.__PROSE_READER_DEBUG;
11
+ return o === true || o === "true";
12
+ }
13
+ const u = (o, n = $(), e) => ({
14
+ namespace: (t, l) => u(`${o} [${t}]`, l, e),
15
+ debug: n ? o ? Function.prototype.bind.call(
16
+ console.debug,
17
+ console,
18
+ o,
19
+ `%c${o}`,
20
+ e?.color ? `color: ${e.color}` : void 0
21
+ ) : Function.prototype.bind.call(
22
+ console.debug,
23
+ console,
24
+ `%c${o}`,
25
+ e?.color ? `color: ${e.color}` : void 0
26
+ ) : () => {
27
+ },
28
+ info: n ? o ? Function.prototype.bind.call(
29
+ console.info,
30
+ console,
31
+ `%c${o}`,
32
+ e?.color ? `color: ${e.color}` : void 0
33
+ ) : Function.prototype.bind.call(
34
+ console.info,
35
+ console,
36
+ `%c${o}`,
37
+ e?.color ? `color: ${e.color}` : void 0
38
+ ) : () => {
39
+ },
40
+ log: n ? o ? Function.prototype.bind.call(
41
+ console.log,
42
+ console,
43
+ `%c${o}`,
44
+ e?.color ? `color: ${e.color}` : void 0
45
+ ) : Function.prototype.bind.call(console.log, console) : () => {
46
+ },
47
+ warn: n ? o ? Function.prototype.bind.call(
48
+ console.warn,
49
+ console,
50
+ `%c${o}`,
51
+ e?.color ? `color: ${e.color}` : void 0
52
+ ) : Function.prototype.bind.call(
53
+ console.warn,
54
+ console,
55
+ `%c${o}`,
56
+ e?.color ? `color: ${e.color}` : void 0
57
+ ) : () => {
58
+ },
59
+ error: n ? o ? Function.prototype.bind.call(
60
+ console.error,
61
+ console,
62
+ `%c${o}`,
63
+ e?.color ? `color: ${e.color}` : void 0
64
+ ) : Function.prototype.bind.call(
65
+ console.error,
66
+ console,
67
+ `%c${o}`,
68
+ e?.color ? `color: ${e.color}` : void 0
69
+ ) : () => {
70
+ }
71
+ }), _ = {
72
+ ...u(),
73
+ namespace: (o, n, e) => u(`[${o}]`, n, e)
74
+ };
75
+
76
+ const name = "@prose-reader/enhancer-search";
77
+
78
+ const IS_DEBUG_ENABLED = true;
79
+ const report = _.namespace(name, IS_DEBUG_ENABLED);
80
+
81
+ const searchNodeContainingText = (node, text) => {
82
+ const nodeList = node.childNodes;
83
+ if (node.nodeName === `head`) return [];
84
+ const rangeList = [];
85
+ for (let i = 0; i < nodeList.length; i++) {
86
+ const subNode = nodeList[i];
87
+ if (!subNode) {
88
+ continue;
89
+ }
90
+ if (subNode?.hasChildNodes()) {
91
+ rangeList.push(...searchNodeContainingText(subNode, text));
92
+ }
93
+ if (subNode.nodeType === 3) {
94
+ const content = subNode.data.toLowerCase();
95
+ if (content) {
96
+ let match = null;
97
+ const regexp = RegExp(`(${text})`, `gi`);
98
+ while ((match = regexp.exec(content)) !== null) {
99
+ if (match.index >= 0 && subNode.ownerDocument) {
100
+ const range = subNode.ownerDocument.createRange();
101
+ range.setStart(subNode, match.index);
102
+ range.setEnd(subNode, match.index + text.length);
103
+ rangeList.push(range);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ return rangeList;
110
+ };
111
+ const searchInDocument = (reader, item, doc, text) => {
112
+ const ranges = searchNodeContainingText(doc, text);
113
+ const newResults = ranges.map((range) => {
114
+ const cfi = reader.cfi.generateCfiFromRange(range, item.item);
115
+ return {
116
+ cfi
117
+ };
118
+ });
119
+ return of(newResults);
120
+ };
121
+
122
+ const searchEnhancer = (next) => (options) => {
123
+ const reader = next(options);
124
+ const searchForItem = (index, text) => {
125
+ const item = reader.spineItemsManager.get(index);
126
+ if (!item) {
127
+ return of([]);
128
+ }
129
+ return deferIdle(() => reader.renderHeadless(index)).pipe(
130
+ switchMap((result) => {
131
+ const { doc, release } = result || {};
132
+ if (!doc) return of([]);
133
+ return deferIdle(
134
+ () => searchInDocument(reader, item, doc, text)
135
+ ).pipe(
136
+ finalize(() => {
137
+ release?.();
138
+ }),
139
+ catchError((e) => {
140
+ report.error(e);
141
+ return of([]);
142
+ })
143
+ );
144
+ })
145
+ );
146
+ };
147
+ const search = (text) => defer(() => {
148
+ if (text === ``) {
149
+ return of([]);
150
+ }
151
+ const searches$ = reader.context.manifest?.spineItems.map(
152
+ (_, index) => searchForItem(index, text)
153
+ ) || [];
154
+ return forkJoin([...searches$, of([])]);
155
+ }).pipe(
156
+ map((results) => {
157
+ const flattenedResults = results.flat();
158
+ report.debug("results", flattenedResults);
159
+ return flattenedResults;
160
+ })
161
+ );
162
+ return {
163
+ ...reader,
164
+ __PROSE_READER_ENHANCER_SEARCH: true,
165
+ search: {
166
+ search
167
+ }
168
+ };
169
+ };
170
+
171
+ export { searchEnhancer };
172
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../shared/dist/index.js","../src/report.ts","../src/search.ts","../src/index.ts"],"sourcesContent":["const a = (o, n) => o === n ? !0 : o.length !== n.length ? !1 : o.every((e, t) => e === n[t]), f = (o) => o.split(/[#?]/)[0]?.split(\".\").pop()?.trim() || \"\";\nfunction s(o) {\n const n = [];\n if (o.length === 0)\n return \"\";\n if (typeof o[0] != \"string\")\n throw new TypeError(`Url must be a string. Received ${o[0]}`);\n o[0].match(/^[^/:]+:\\/*$/) && o.length > 1 && (o[0] = o.shift() + o[0]), o[0].match(/^file:\\/\\/\\//) ? o[0] = o[0].replace(/^([^/:]+):\\/*/, \"$1:///\") : o[0] = o[0].replace(/^([^/:]+):\\/*/, \"$1://\");\n for (let l = 0; l < o.length; l++) {\n let r = o[l];\n if (typeof r != \"string\")\n throw new TypeError(`Url must be a string. Received ${r}`);\n r !== \"\" && (l > 0 && (r = r.replace(/^[/]+/, \"\")), l < o.length - 1 ? r = r.replace(/[/]+$/, \"\") : r = r.replace(/[/]+$/, \"/\"), n.push(r));\n }\n let e = n.join(\"/\");\n e = e.replace(/\\/(\\?|&|#[^!])/g, \"$1\");\n const t = e.split(\"?\");\n return e = t.shift() + (t.length > 0 ? \"?\" : \"\") + t.join(\"&\"), e;\n}\nfunction E(...o) {\n const n = Array.from(Array.isArray(o[0]) ? o[0] : o);\n return s(n);\n}\nfunction y(o) {\n const n = o.split(\"/\");\n return n.pop(), n.join(\"/\");\n}\nconst p = (o) => {\n switch (f(o)) {\n case \"png\":\n return \"image/png\";\n case \"jpg\":\n return \"image/jpg\";\n case \"jpeg\":\n return \"image/jpeg\";\n case \"txt\":\n return \"text/plain\";\n case \"webp\":\n return \"image/webp\";\n case \"xhtml\":\n return \"application/xhtml+xml\";\n }\n}, w = ({\n mimeType: o,\n uri: n\n}) => (o ?? p(n ?? \"\"))?.startsWith(\"application/xhtml+xml\"), R = (o) => o.length ? o.indexOf(\";\") ? o.substring(0, o.indexOf(\";\")) : o : void 0, g = Object.prototype.hasOwnProperty, d = (o, n) => o === n ? o !== 0 || n !== 0 || 1 / o === 1 / n : !1, m = (o, n, e) => {\n if (o === n)\n return !0;\n if (typeof o != \"object\" || o === null || typeof n != \"object\" || n === null)\n return !1;\n const t = Object.keys(o), l = Object.keys(n);\n if (t.length !== l.length)\n return !1;\n const r = e && typeof e.customEqual == \"function\" ? e.customEqual : d;\n for (let c = 0; c < t.length; c++) {\n const i = t[c] || \"\";\n if (!g.call(n, i) || !r(o[i], n[i]))\n return !1;\n }\n return !0;\n}, v = (o, n) => o.reduce(\n (e, t) => {\n const l = n(t);\n return e[l] || (e[l] = []), e[l].push(t), e;\n },\n {}\n);\nfunction O(o, n) {\n const e = { ...o };\n for (const t in n)\n if (Object.hasOwn(n, t)) {\n const l = n[t];\n (l !== void 0 || !(t in o)) && (e[t] = l);\n }\n return e;\n}\nconst h = () => {\n if (!(typeof window > \"u\"))\n return window;\n};\nfunction $() {\n const o = h()?.__PROSE_READER_DEBUG;\n return o === !0 || o === \"true\";\n}\nconst u = (o, n = $(), e) => ({\n namespace: (t, l) => u(`${o} [${t}]`, l, e),\n debug: n ? o ? Function.prototype.bind.call(\n console.debug,\n console,\n o,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.debug,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n },\n info: n ? o ? Function.prototype.bind.call(\n console.info,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.info,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n },\n log: n ? o ? Function.prototype.bind.call(\n console.log,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(console.log, console) : () => {\n },\n warn: n ? o ? Function.prototype.bind.call(\n console.warn,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.warn,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n },\n error: n ? o ? Function.prototype.bind.call(\n console.error,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.error,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n }\n}), _ = {\n ...u(),\n namespace: (o, n, e) => u(`[${o}]`, n, e)\n}, x = \"prose-reader-resource-error\";\nexport {\n x as PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME,\n _ as Report,\n a as arrayEqual,\n p as detectMimeTypeFromName,\n y as getParentPath,\n f as getUrlExtension,\n v as groupBy,\n d as is,\n m as isShallowEqual,\n w as isXmlBasedMimeType,\n R as parseContentType,\n O as shallowMergeIfDefined,\n E as urlJoin\n};\n//# sourceMappingURL=index.js.map\n","import { Report } from \"@prose-reader/shared\"\nimport { name } from \"../package.json\"\n\nconst IS_DEBUG_ENABLED = true\n\nexport const report = Report.namespace(name, IS_DEBUG_ENABLED)\n","import type { Reader, SpineItem } from \"@prose-reader/core\"\nimport { type Observable, of } from \"rxjs\"\nimport type { ResultItem } from \"./types\"\n\nexport type SearchResult = ResultItem[]\n\nconst searchNodeContainingText = (node: Node, text: string) => {\n const nodeList = node.childNodes\n\n if (node.nodeName === `head`) return []\n\n const rangeList: Range[] = []\n\n for (let i = 0; i < nodeList.length; i++) {\n const subNode = nodeList[i]\n\n if (!subNode) {\n continue\n }\n\n if (subNode?.hasChildNodes()) {\n rangeList.push(...searchNodeContainingText(subNode, text))\n }\n\n if (subNode.nodeType === 3) {\n const content = (subNode as Text).data.toLowerCase()\n if (content) {\n let match: RegExpExecArray | null = null\n const regexp = RegExp(`(${text})`, `gi`)\n\n // biome-ignore lint/suspicious/noAssignInExpressions: TODO\n while ((match = regexp.exec(content)) !== null) {\n if (match.index >= 0 && subNode.ownerDocument) {\n const range = subNode.ownerDocument.createRange()\n range.setStart(subNode, match.index)\n range.setEnd(subNode, match.index + text.length)\n\n rangeList.push(range)\n }\n }\n }\n }\n }\n\n return rangeList\n}\n\nexport const searchInDocument = (\n reader: Reader,\n item: SpineItem,\n doc: Document,\n text: string,\n): Observable<SearchResult> => {\n const ranges = searchNodeContainingText(doc, text)\n\n const newResults = ranges.map((range) => {\n const cfi = reader.cfi.generateCfiFromRange(range, item.item)\n\n return {\n cfi,\n } satisfies ResultItem\n })\n\n return of(newResults)\n}\n","import { deferIdle, type Reader } from \"@prose-reader/core\"\nimport { defer, forkJoin, of } from \"rxjs\"\nimport { catchError, finalize, map, switchMap } from \"rxjs/operators\"\nimport { report } from \"./report\"\nimport type { SearchResult } from \"./search\"\nimport { searchInDocument } from \"./search\"\nimport type { ResultItem, SearchEnhancerAPI } from \"./types\"\n\nexport type { SearchEnhancerAPI, ResultItem, SearchResult }\n\n/**\n * Contract of search enhancer.\n *\n * - At best a result match should be navigable. It means the search needs to\n * be done on a rendered document. This is because rendering can differ from the original\n * item resource. A resource can be something non digestible and very specific (.pdf). The search\n * enhancer is agnostic and can only search into documents.\n */\nexport const searchEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & SearchEnhancerAPI => {\n const reader = next(options)\n\n const searchForItem = (index: number, text: string) => {\n const item = reader.spineItemsManager.get(index)\n\n if (!item) {\n return of([])\n }\n\n return deferIdle(() => reader.renderHeadless(index)).pipe(\n switchMap((result) => {\n const { doc, release } = result || {}\n\n if (!doc) return of([])\n\n return deferIdle(() =>\n searchInDocument(reader, item, doc, text),\n ).pipe(\n finalize(() => {\n release?.()\n }),\n catchError((e) => {\n report.error(e)\n\n return of([])\n }),\n )\n }),\n )\n }\n\n const search = (text: string) =>\n defer(() => {\n if (text === ``) {\n return of([])\n }\n\n const searches$ =\n reader.context.manifest?.spineItems.map((_, index) =>\n searchForItem(index, text),\n ) || []\n\n return forkJoin([...searches$, of([])])\n }).pipe(\n map((results) => {\n const flattenedResults = results.flat()\n\n report.debug(\"results\", flattenedResults)\n\n return flattenedResults\n }),\n )\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_SEARCH: true,\n search: {\n search,\n },\n }\n }\n"],"names":["Report"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,178 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@prose-reader/core'), require('rxjs'), require('rxjs/operators')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@prose-reader/core', 'rxjs', 'rxjs/operators'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["prose-reader-enhancer-search"] = {}, global.core, global.rxjs, global.operators));
5
+ })(this, (function (exports, core, rxjs, operators) { 'use strict';
6
+
7
+ const h = () => {
8
+ if (!(typeof window > "u"))
9
+ return window;
10
+ };
11
+ function $() {
12
+ const o = h()?.__PROSE_READER_DEBUG;
13
+ return o === true || o === "true";
14
+ }
15
+ const u = (o, n = $(), e) => ({
16
+ namespace: (t, l) => u(`${o} [${t}]`, l, e),
17
+ debug: n ? o ? Function.prototype.bind.call(
18
+ console.debug,
19
+ console,
20
+ o,
21
+ `%c${o}`,
22
+ e?.color ? `color: ${e.color}` : void 0
23
+ ) : Function.prototype.bind.call(
24
+ console.debug,
25
+ console,
26
+ `%c${o}`,
27
+ e?.color ? `color: ${e.color}` : void 0
28
+ ) : () => {
29
+ },
30
+ info: n ? o ? Function.prototype.bind.call(
31
+ console.info,
32
+ console,
33
+ `%c${o}`,
34
+ e?.color ? `color: ${e.color}` : void 0
35
+ ) : Function.prototype.bind.call(
36
+ console.info,
37
+ console,
38
+ `%c${o}`,
39
+ e?.color ? `color: ${e.color}` : void 0
40
+ ) : () => {
41
+ },
42
+ log: n ? o ? Function.prototype.bind.call(
43
+ console.log,
44
+ console,
45
+ `%c${o}`,
46
+ e?.color ? `color: ${e.color}` : void 0
47
+ ) : Function.prototype.bind.call(console.log, console) : () => {
48
+ },
49
+ warn: n ? o ? Function.prototype.bind.call(
50
+ console.warn,
51
+ console,
52
+ `%c${o}`,
53
+ e?.color ? `color: ${e.color}` : void 0
54
+ ) : Function.prototype.bind.call(
55
+ console.warn,
56
+ console,
57
+ `%c${o}`,
58
+ e?.color ? `color: ${e.color}` : void 0
59
+ ) : () => {
60
+ },
61
+ error: n ? o ? Function.prototype.bind.call(
62
+ console.error,
63
+ console,
64
+ `%c${o}`,
65
+ e?.color ? `color: ${e.color}` : void 0
66
+ ) : Function.prototype.bind.call(
67
+ console.error,
68
+ console,
69
+ `%c${o}`,
70
+ e?.color ? `color: ${e.color}` : void 0
71
+ ) : () => {
72
+ }
73
+ }), _ = {
74
+ ...u(),
75
+ namespace: (o, n, e) => u(`[${o}]`, n, e)
76
+ };
77
+
78
+ const name = "@prose-reader/enhancer-search";
79
+
80
+ const IS_DEBUG_ENABLED = true;
81
+ const report = _.namespace(name, IS_DEBUG_ENABLED);
82
+
83
+ const searchNodeContainingText = (node, text) => {
84
+ const nodeList = node.childNodes;
85
+ if (node.nodeName === `head`) return [];
86
+ const rangeList = [];
87
+ for (let i = 0; i < nodeList.length; i++) {
88
+ const subNode = nodeList[i];
89
+ if (!subNode) {
90
+ continue;
91
+ }
92
+ if (subNode?.hasChildNodes()) {
93
+ rangeList.push(...searchNodeContainingText(subNode, text));
94
+ }
95
+ if (subNode.nodeType === 3) {
96
+ const content = subNode.data.toLowerCase();
97
+ if (content) {
98
+ let match = null;
99
+ const regexp = RegExp(`(${text})`, `gi`);
100
+ while ((match = regexp.exec(content)) !== null) {
101
+ if (match.index >= 0 && subNode.ownerDocument) {
102
+ const range = subNode.ownerDocument.createRange();
103
+ range.setStart(subNode, match.index);
104
+ range.setEnd(subNode, match.index + text.length);
105
+ rangeList.push(range);
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ return rangeList;
112
+ };
113
+ const searchInDocument = (reader, item, doc, text) => {
114
+ const ranges = searchNodeContainingText(doc, text);
115
+ const newResults = ranges.map((range) => {
116
+ const cfi = reader.cfi.generateCfiFromRange(range, item.item);
117
+ return {
118
+ cfi
119
+ };
120
+ });
121
+ return rxjs.of(newResults);
122
+ };
123
+
124
+ const searchEnhancer = (next) => (options) => {
125
+ const reader = next(options);
126
+ const searchForItem = (index, text) => {
127
+ const item = reader.spineItemsManager.get(index);
128
+ if (!item) {
129
+ return rxjs.of([]);
130
+ }
131
+ return core.deferIdle(() => reader.renderHeadless(index)).pipe(
132
+ operators.switchMap((result) => {
133
+ const { doc, release } = result || {};
134
+ if (!doc) return rxjs.of([]);
135
+ return core.deferIdle(
136
+ () => searchInDocument(reader, item, doc, text)
137
+ ).pipe(
138
+ operators.finalize(() => {
139
+ release?.();
140
+ }),
141
+ operators.catchError((e) => {
142
+ report.error(e);
143
+ return rxjs.of([]);
144
+ })
145
+ );
146
+ })
147
+ );
148
+ };
149
+ const search = (text) => rxjs.defer(() => {
150
+ if (text === ``) {
151
+ return rxjs.of([]);
152
+ }
153
+ const searches$ = reader.context.manifest?.spineItems.map(
154
+ (_, index) => searchForItem(index, text)
155
+ ) || [];
156
+ return rxjs.forkJoin([...searches$, rxjs.of([])]);
157
+ }).pipe(
158
+ operators.map((results) => {
159
+ const flattenedResults = results.flat();
160
+ report.debug("results", flattenedResults);
161
+ return flattenedResults;
162
+ })
163
+ );
164
+ return {
165
+ ...reader,
166
+ __PROSE_READER_ENHANCER_SEARCH: true,
167
+ search: {
168
+ search
169
+ }
170
+ };
171
+ };
172
+
173
+ exports.searchEnhancer = searchEnhancer;
174
+
175
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
176
+
177
+ }));
178
+ //# sourceMappingURL=index.umd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.cjs","sources":["../../shared/dist/index.js","../src/report.ts","../src/search.ts","../src/index.ts"],"sourcesContent":["const a = (o, n) => o === n ? !0 : o.length !== n.length ? !1 : o.every((e, t) => e === n[t]), f = (o) => o.split(/[#?]/)[0]?.split(\".\").pop()?.trim() || \"\";\nfunction s(o) {\n const n = [];\n if (o.length === 0)\n return \"\";\n if (typeof o[0] != \"string\")\n throw new TypeError(`Url must be a string. Received ${o[0]}`);\n o[0].match(/^[^/:]+:\\/*$/) && o.length > 1 && (o[0] = o.shift() + o[0]), o[0].match(/^file:\\/\\/\\//) ? o[0] = o[0].replace(/^([^/:]+):\\/*/, \"$1:///\") : o[0] = o[0].replace(/^([^/:]+):\\/*/, \"$1://\");\n for (let l = 0; l < o.length; l++) {\n let r = o[l];\n if (typeof r != \"string\")\n throw new TypeError(`Url must be a string. Received ${r}`);\n r !== \"\" && (l > 0 && (r = r.replace(/^[/]+/, \"\")), l < o.length - 1 ? r = r.replace(/[/]+$/, \"\") : r = r.replace(/[/]+$/, \"/\"), n.push(r));\n }\n let e = n.join(\"/\");\n e = e.replace(/\\/(\\?|&|#[^!])/g, \"$1\");\n const t = e.split(\"?\");\n return e = t.shift() + (t.length > 0 ? \"?\" : \"\") + t.join(\"&\"), e;\n}\nfunction E(...o) {\n const n = Array.from(Array.isArray(o[0]) ? o[0] : o);\n return s(n);\n}\nfunction y(o) {\n const n = o.split(\"/\");\n return n.pop(), n.join(\"/\");\n}\nconst p = (o) => {\n switch (f(o)) {\n case \"png\":\n return \"image/png\";\n case \"jpg\":\n return \"image/jpg\";\n case \"jpeg\":\n return \"image/jpeg\";\n case \"txt\":\n return \"text/plain\";\n case \"webp\":\n return \"image/webp\";\n case \"xhtml\":\n return \"application/xhtml+xml\";\n }\n}, w = ({\n mimeType: o,\n uri: n\n}) => (o ?? p(n ?? \"\"))?.startsWith(\"application/xhtml+xml\"), R = (o) => o.length ? o.indexOf(\";\") ? o.substring(0, o.indexOf(\";\")) : o : void 0, g = Object.prototype.hasOwnProperty, d = (o, n) => o === n ? o !== 0 || n !== 0 || 1 / o === 1 / n : !1, m = (o, n, e) => {\n if (o === n)\n return !0;\n if (typeof o != \"object\" || o === null || typeof n != \"object\" || n === null)\n return !1;\n const t = Object.keys(o), l = Object.keys(n);\n if (t.length !== l.length)\n return !1;\n const r = e && typeof e.customEqual == \"function\" ? e.customEqual : d;\n for (let c = 0; c < t.length; c++) {\n const i = t[c] || \"\";\n if (!g.call(n, i) || !r(o[i], n[i]))\n return !1;\n }\n return !0;\n}, v = (o, n) => o.reduce(\n (e, t) => {\n const l = n(t);\n return e[l] || (e[l] = []), e[l].push(t), e;\n },\n {}\n);\nfunction O(o, n) {\n const e = { ...o };\n for (const t in n)\n if (Object.hasOwn(n, t)) {\n const l = n[t];\n (l !== void 0 || !(t in o)) && (e[t] = l);\n }\n return e;\n}\nconst h = () => {\n if (!(typeof window > \"u\"))\n return window;\n};\nfunction $() {\n const o = h()?.__PROSE_READER_DEBUG;\n return o === !0 || o === \"true\";\n}\nconst u = (o, n = $(), e) => ({\n namespace: (t, l) => u(`${o} [${t}]`, l, e),\n debug: n ? o ? Function.prototype.bind.call(\n console.debug,\n console,\n o,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.debug,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n },\n info: n ? o ? Function.prototype.bind.call(\n console.info,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.info,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n },\n log: n ? o ? Function.prototype.bind.call(\n console.log,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(console.log, console) : () => {\n },\n warn: n ? o ? Function.prototype.bind.call(\n console.warn,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.warn,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n },\n error: n ? o ? Function.prototype.bind.call(\n console.error,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : Function.prototype.bind.call(\n console.error,\n console,\n `%c${o}`,\n e?.color ? `color: ${e.color}` : void 0\n ) : () => {\n }\n}), _ = {\n ...u(),\n namespace: (o, n, e) => u(`[${o}]`, n, e)\n}, x = \"prose-reader-resource-error\";\nexport {\n x as PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME,\n _ as Report,\n a as arrayEqual,\n p as detectMimeTypeFromName,\n y as getParentPath,\n f as getUrlExtension,\n v as groupBy,\n d as is,\n m as isShallowEqual,\n w as isXmlBasedMimeType,\n R as parseContentType,\n O as shallowMergeIfDefined,\n E as urlJoin\n};\n//# sourceMappingURL=index.js.map\n","import { Report } from \"@prose-reader/shared\"\nimport { name } from \"../package.json\"\n\nconst IS_DEBUG_ENABLED = true\n\nexport const report = Report.namespace(name, IS_DEBUG_ENABLED)\n","import type { Reader, SpineItem } from \"@prose-reader/core\"\nimport { type Observable, of } from \"rxjs\"\nimport type { ResultItem } from \"./types\"\n\nexport type SearchResult = ResultItem[]\n\nconst searchNodeContainingText = (node: Node, text: string) => {\n const nodeList = node.childNodes\n\n if (node.nodeName === `head`) return []\n\n const rangeList: Range[] = []\n\n for (let i = 0; i < nodeList.length; i++) {\n const subNode = nodeList[i]\n\n if (!subNode) {\n continue\n }\n\n if (subNode?.hasChildNodes()) {\n rangeList.push(...searchNodeContainingText(subNode, text))\n }\n\n if (subNode.nodeType === 3) {\n const content = (subNode as Text).data.toLowerCase()\n if (content) {\n let match: RegExpExecArray | null = null\n const regexp = RegExp(`(${text})`, `gi`)\n\n // biome-ignore lint/suspicious/noAssignInExpressions: TODO\n while ((match = regexp.exec(content)) !== null) {\n if (match.index >= 0 && subNode.ownerDocument) {\n const range = subNode.ownerDocument.createRange()\n range.setStart(subNode, match.index)\n range.setEnd(subNode, match.index + text.length)\n\n rangeList.push(range)\n }\n }\n }\n }\n }\n\n return rangeList\n}\n\nexport const searchInDocument = (\n reader: Reader,\n item: SpineItem,\n doc: Document,\n text: string,\n): Observable<SearchResult> => {\n const ranges = searchNodeContainingText(doc, text)\n\n const newResults = ranges.map((range) => {\n const cfi = reader.cfi.generateCfiFromRange(range, item.item)\n\n return {\n cfi,\n } satisfies ResultItem\n })\n\n return of(newResults)\n}\n","import { deferIdle, type Reader } from \"@prose-reader/core\"\nimport { defer, forkJoin, of } from \"rxjs\"\nimport { catchError, finalize, map, switchMap } from \"rxjs/operators\"\nimport { report } from \"./report\"\nimport type { SearchResult } from \"./search\"\nimport { searchInDocument } from \"./search\"\nimport type { ResultItem, SearchEnhancerAPI } from \"./types\"\n\nexport type { SearchEnhancerAPI, ResultItem, SearchResult }\n\n/**\n * Contract of search enhancer.\n *\n * - At best a result match should be navigable. It means the search needs to\n * be done on a rendered document. This is because rendering can differ from the original\n * item resource. A resource can be something non digestible and very specific (.pdf). The search\n * enhancer is agnostic and can only search into documents.\n */\nexport const searchEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & SearchEnhancerAPI => {\n const reader = next(options)\n\n const searchForItem = (index: number, text: string) => {\n const item = reader.spineItemsManager.get(index)\n\n if (!item) {\n return of([])\n }\n\n return deferIdle(() => reader.renderHeadless(index)).pipe(\n switchMap((result) => {\n const { doc, release } = result || {}\n\n if (!doc) return of([])\n\n return deferIdle(() =>\n searchInDocument(reader, item, doc, text),\n ).pipe(\n finalize(() => {\n release?.()\n }),\n catchError((e) => {\n report.error(e)\n\n return of([])\n }),\n )\n }),\n )\n }\n\n const search = (text: string) =>\n defer(() => {\n if (text === ``) {\n return of([])\n }\n\n const searches$ =\n reader.context.manifest?.spineItems.map((_, index) =>\n searchForItem(index, text),\n ) || []\n\n return forkJoin([...searches$, of([])])\n }).pipe(\n map((results) => {\n const flattenedResults = results.flat()\n\n report.debug(\"results\", flattenedResults)\n\n return flattenedResults\n }),\n )\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_SEARCH: true,\n search: {\n search,\n },\n }\n }\n"],"names":["Report","of","deferIdle","switchMap","finalize","catchError","defer","forkJoin","map"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Report } from '@prose-reader/shared';
2
+ export declare const report: Report;
@@ -0,0 +1,5 @@
1
+ import { Reader, SpineItem } from '@prose-reader/core';
2
+ import { Observable } from 'rxjs';
3
+ import { ResultItem } from './types';
4
+ export type SearchResult = ResultItem[];
5
+ export declare const searchInDocument: (reader: Reader, item: SpineItem, doc: Document, text: string) => Observable<SearchResult>;
@@ -0,0 +1,11 @@
1
+ import { Observable } from 'rxjs';
2
+ import { SearchResult } from './search';
3
+ export type ResultItem = {
4
+ cfi: string;
5
+ };
6
+ export type SearchEnhancerAPI = {
7
+ readonly __PROSE_READER_ENHANCER_SEARCH: boolean;
8
+ search: {
9
+ search: (text: string) => Observable<SearchResult>;
10
+ };
11
+ };
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@prose-reader/enhancer-search",
3
- "version": "1.264.0",
3
+ "version": "1.266.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.umd.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "/dist"
10
+ ],
8
11
  "exports": {
9
12
  ".": {
10
13
  "import": "./dist/index.js",
@@ -17,10 +20,10 @@
17
20
  "test": "vitest run --coverage"
18
21
  },
19
22
  "dependencies": {
20
- "@prose-reader/core": "^1.264.0"
23
+ "@prose-reader/core": "^1.266.0"
21
24
  },
22
25
  "peerDependencies": {
23
26
  "rxjs": "*"
24
27
  },
25
- "gitHead": "78b503f7bab79a35702145b9ec285cc94a80d626"
28
+ "gitHead": "38f1d2d8e946de1e8a7d7359d22e67fe766ae352"
26
29
  }
package/src/index.test.ts DELETED
@@ -1,5 +0,0 @@
1
- import { expect, test } from "vitest"
2
-
3
- test("it works", () => {
4
- expect(true).toBe(true)
5
- })
package/src/index.ts DELETED
@@ -1,84 +0,0 @@
1
- import { deferIdle, type Reader } from "@prose-reader/core"
2
- import { defer, forkJoin, of } from "rxjs"
3
- import { catchError, finalize, map, switchMap } from "rxjs/operators"
4
- import { report } from "./report"
5
- import type { SearchResult } from "./search"
6
- import { searchInDocument } from "./search"
7
- import type { ResultItem, SearchEnhancerAPI } from "./types"
8
-
9
- export type { SearchEnhancerAPI, ResultItem, SearchResult }
10
-
11
- /**
12
- * Contract of search enhancer.
13
- *
14
- * - At best a result match should be navigable. It means the search needs to
15
- * be done on a rendered document. This is because rendering can differ from the original
16
- * item resource. A resource can be something non digestible and very specific (.pdf). The search
17
- * enhancer is agnostic and can only search into documents.
18
- */
19
- export const searchEnhancer =
20
- <InheritOptions, InheritOutput extends Reader>(
21
- next: (options: InheritOptions) => InheritOutput,
22
- ) =>
23
- (options: InheritOptions): InheritOutput & SearchEnhancerAPI => {
24
- const reader = next(options)
25
-
26
- const searchForItem = (index: number, text: string) => {
27
- const item = reader.spineItemsManager.get(index)
28
-
29
- if (!item) {
30
- return of([])
31
- }
32
-
33
- return deferIdle(() => reader.renderHeadless(index)).pipe(
34
- switchMap((result) => {
35
- const { doc, release } = result || {}
36
-
37
- if (!doc) return of([])
38
-
39
- return deferIdle(() =>
40
- searchInDocument(reader, item, doc, text),
41
- ).pipe(
42
- finalize(() => {
43
- release?.()
44
- }),
45
- catchError((e) => {
46
- report.error(e)
47
-
48
- return of([])
49
- }),
50
- )
51
- }),
52
- )
53
- }
54
-
55
- const search = (text: string) =>
56
- defer(() => {
57
- if (text === ``) {
58
- return of([])
59
- }
60
-
61
- const searches$ =
62
- reader.context.manifest?.spineItems.map((_, index) =>
63
- searchForItem(index, text),
64
- ) || []
65
-
66
- return forkJoin([...searches$, of([])])
67
- }).pipe(
68
- map((results) => {
69
- const flattenedResults = results.flat()
70
-
71
- report.debug("results", flattenedResults)
72
-
73
- return flattenedResults
74
- }),
75
- )
76
-
77
- return {
78
- ...reader,
79
- __PROSE_READER_ENHANCER_SEARCH: true,
80
- search: {
81
- search,
82
- },
83
- }
84
- }
package/src/report.ts DELETED
@@ -1,6 +0,0 @@
1
- import { Report } from "@prose-reader/shared"
2
- import { name } from "../package.json"
3
-
4
- const IS_DEBUG_ENABLED = true
5
-
6
- export const report = Report.namespace(name, IS_DEBUG_ENABLED)
package/src/search.ts DELETED
@@ -1,65 +0,0 @@
1
- import type { Reader, SpineItem } from "@prose-reader/core"
2
- import { type Observable, of } from "rxjs"
3
- import type { ResultItem } from "./types"
4
-
5
- export type SearchResult = ResultItem[]
6
-
7
- const searchNodeContainingText = (node: Node, text: string) => {
8
- const nodeList = node.childNodes
9
-
10
- if (node.nodeName === `head`) return []
11
-
12
- const rangeList: Range[] = []
13
-
14
- for (let i = 0; i < nodeList.length; i++) {
15
- const subNode = nodeList[i]
16
-
17
- if (!subNode) {
18
- continue
19
- }
20
-
21
- if (subNode?.hasChildNodes()) {
22
- rangeList.push(...searchNodeContainingText(subNode, text))
23
- }
24
-
25
- if (subNode.nodeType === 3) {
26
- const content = (subNode as Text).data.toLowerCase()
27
- if (content) {
28
- let match: RegExpExecArray | null = null
29
- const regexp = RegExp(`(${text})`, `gi`)
30
-
31
- // biome-ignore lint/suspicious/noAssignInExpressions: TODO
32
- while ((match = regexp.exec(content)) !== null) {
33
- if (match.index >= 0 && subNode.ownerDocument) {
34
- const range = subNode.ownerDocument.createRange()
35
- range.setStart(subNode, match.index)
36
- range.setEnd(subNode, match.index + text.length)
37
-
38
- rangeList.push(range)
39
- }
40
- }
41
- }
42
- }
43
- }
44
-
45
- return rangeList
46
- }
47
-
48
- export const searchInDocument = (
49
- reader: Reader,
50
- item: SpineItem,
51
- doc: Document,
52
- text: string,
53
- ): Observable<SearchResult> => {
54
- const ranges = searchNodeContainingText(doc, text)
55
-
56
- const newResults = ranges.map((range) => {
57
- const cfi = reader.cfi.generateCfiFromRange(range, item.item)
58
-
59
- return {
60
- cfi,
61
- } satisfies ResultItem
62
- })
63
-
64
- return of(newResults)
65
- }
package/src/types.ts DELETED
@@ -1,13 +0,0 @@
1
- import type { Observable } from "rxjs"
2
- import type { SearchResult } from "./search"
3
-
4
- export type ResultItem = {
5
- cfi: string
6
- }
7
-
8
- export type SearchEnhancerAPI = {
9
- readonly __PROSE_READER_ENHANCER_SEARCH: boolean
10
- search: {
11
- search: (text: string) => Observable<SearchResult>
12
- }
13
- }
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "useDefineForClassFields": true,
5
- "module": "ESNext",
6
- "lib": ["ESNext", "DOM"],
7
- "moduleResolution": "Node",
8
- "strict": true,
9
- "resolveJsonModule": true,
10
- "isolatedModules": true,
11
- "esModuleInterop": true,
12
- "noEmit": true,
13
- "noUnusedLocals": true,
14
- "noUnusedParameters": true,
15
- "noImplicitReturns": true,
16
- "skipLibCheck": true
17
- },
18
- "include": ["src"]
19
- }
package/vite.config.ts DELETED
@@ -1,31 +0,0 @@
1
- import { resolve } from "node:path"
2
- import externals from "rollup-plugin-node-externals"
3
- import { defineConfig } from "vite"
4
- import dts from "vite-plugin-dts"
5
- import { name } from "./package.json"
6
-
7
- const libName = name.replace(`@`, ``).replace(`/`, `-`)
8
-
9
- export default defineConfig(({ mode }) => ({
10
- build: {
11
- minify: false,
12
- target: "esnext",
13
- lib: {
14
- entry: resolve(__dirname, `src/index.ts`),
15
- name: libName,
16
- fileName: `index`,
17
- },
18
- emptyOutDir: mode !== `development`,
19
- sourcemap: true,
20
- },
21
- plugins: [
22
- externals({
23
- peerDeps: true,
24
- deps: true,
25
- devDeps: true,
26
- }),
27
- dts({
28
- entryRoot: "src",
29
- }),
30
- ],
31
- }))
package/vitest.config.ts DELETED
@@ -1,9 +0,0 @@
1
- import { defineConfig } from "vitest/config"
2
-
3
- export default defineConfig(() => ({
4
- test: {
5
- coverage: {
6
- reportsDirectory: `./.test/coverage`,
7
- },
8
- },
9
- }))