dsh-locator 0.8.2 → 0.10.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.
- package/CHANGELOG.md +47 -0
- package/README-es.md +30 -5
- package/README-hi.md +31 -5
- package/README-pt.md +31 -5
- package/README.en.md +31 -5
- package/README.md +31 -5
- package/bin/darwin-arm64/indexd +0 -0
- package/bin/darwin-x64/indexd +0 -0
- package/bin/linux-arm64/indexd +0 -0
- package/bin/linux-x64/indexd +0 -0
- package/bin/win32-arm64/indexd.exe +0 -0
- package/bin/win32-x64/indexd.exe +0 -0
- package/client.js +1198 -117
- package/lib/combined.d.ts +20 -3
- package/lib/combined.d.ts.map +1 -1
- package/lib/combined.js +85 -15
- package/lib/combined.js.map +1 -1
- package/lib/content-search-worker.d.ts +8 -3
- package/lib/content-search-worker.d.ts.map +1 -1
- package/lib/content-search-worker.js +54 -27
- package/lib/content-search-worker.js.map +1 -1
- package/lib/content-search.d.ts +127 -7
- package/lib/content-search.d.ts.map +1 -1
- package/lib/content-search.js +571 -36
- package/lib/content-search.js.map +1 -1
- package/lib/content.d.ts +83 -5
- package/lib/content.d.ts.map +1 -1
- package/lib/content.js +351 -87
- package/lib/content.js.map +1 -1
- package/lib/export.d.ts +61 -0
- package/lib/export.d.ts.map +1 -1
- package/lib/export.js +217 -52
- package/lib/export.js.map +1 -1
- package/lib/find.d.ts.map +1 -1
- package/lib/find.js +73 -16
- package/lib/find.js.map +1 -1
- package/lib/http.d.ts +38 -2
- package/lib/http.d.ts.map +1 -1
- package/lib/http.js +279 -82
- package/lib/http.js.map +1 -1
- package/lib/reveal.d.ts +69 -3
- package/lib/reveal.d.ts.map +1 -1
- package/lib/reveal.js +140 -11
- package/lib/reveal.js.map +1 -1
- package/lib/tools.d.ts +30 -0
- package/lib/tools.d.ts.map +1 -1
- package/lib/tools.js +65 -3
- package/lib/tools.js.map +1 -1
- package/package.json +1 -1
- package/src/combined.ts +107 -17
- package/src/content-search-worker.ts +52 -24
- package/src/content-search.ts +710 -36
- package/src/content.ts +349 -73
- package/src/export.ts +240 -50
- package/src/find.ts +83 -16
- package/src/http.ts +271 -83
- package/src/reveal.ts +148 -10
- package/src/tools.ts +83 -6
package/lib/combined.d.ts
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The name index and the content index live in different stores, so this module
|
|
6
6
|
* owns the orchestration and the merge rules as pure, injectable functions:
|
|
7
|
-
* scope gating, the lazy-build guard for empty queries, per-group degradation
|
|
8
|
-
*
|
|
7
|
+
* scope gating, the lazy-build guard for empty queries, per-group degradation,
|
|
8
|
+
* match-option routing (content vs name engine), page counts and de-duplication
|
|
9
|
+
* of a path that appears both as a body hit and a name hit.
|
|
9
10
|
* @module dsh-locator/combined
|
|
10
11
|
*/
|
|
11
12
|
import type { ContentHit, ContentState } from './content.ts';
|
|
13
|
+
import type { ContentCount, ContentCountMode, ContentMatchMode, ContentSearchOptions, ContentSearchResult } from './content-search.ts';
|
|
12
14
|
import type { SearchArgs, SearchHit, SearchResult } from './runtime.ts';
|
|
13
15
|
/** Which groups a combined request asked for; every scope is on by default. */
|
|
14
16
|
export interface CombinedScopes {
|
|
@@ -24,6 +26,15 @@ export interface CombinedRequest {
|
|
|
24
26
|
sort: string;
|
|
25
27
|
desc: boolean;
|
|
26
28
|
scopes: CombinedScopes;
|
|
29
|
+
/** How the query text is interpreted (`normal` by default). */
|
|
30
|
+
matchMode: ContentMatchMode;
|
|
31
|
+
caseSensitive: boolean;
|
|
32
|
+
wholeWord: boolean;
|
|
33
|
+
/** Restrict the content group to one session (null means every document). */
|
|
34
|
+
sessionId: string | null;
|
|
35
|
+
/** How much counting work the groups may spend. */
|
|
36
|
+
count: ContentCountMode;
|
|
37
|
+
countLimit: number;
|
|
27
38
|
}
|
|
28
39
|
/** One name-index group: files or directories. */
|
|
29
40
|
export interface NameGroup {
|
|
@@ -33,7 +44,10 @@ export interface NameGroup {
|
|
|
33
44
|
truncated: boolean;
|
|
34
45
|
/** Rows dropped because the same path already appears in the content group. */
|
|
35
46
|
deduped: number;
|
|
47
|
+
/** The name engine always returns an exact total. */
|
|
48
|
+
count: ContentCount;
|
|
36
49
|
tookMs: number;
|
|
50
|
+
warnings?: string[];
|
|
37
51
|
error?: string;
|
|
38
52
|
}
|
|
39
53
|
/** The content group: body/session hits plus the index state at query time. */
|
|
@@ -42,6 +56,9 @@ export interface ContentGroup {
|
|
|
42
56
|
returned: number;
|
|
43
57
|
/** True when the group was cut off by its page limit. */
|
|
44
58
|
capped: boolean;
|
|
59
|
+
/** Exact or bounded match count; `unknown` when counting was skipped. */
|
|
60
|
+
count: ContentCount;
|
|
61
|
+
warnings: string[];
|
|
45
62
|
building: boolean;
|
|
46
63
|
files: number;
|
|
47
64
|
sessions: number;
|
|
@@ -64,7 +81,7 @@ export interface CombinedResponse {
|
|
|
64
81
|
/** Dependencies required to run a combined search; the HTTP layer supplies them. */
|
|
65
82
|
export interface CombinedDeps {
|
|
66
83
|
search(args: SearchArgs): Promise<SearchResult>;
|
|
67
|
-
contentSearch?: (query: string, limit: number) => Promise<
|
|
84
|
+
contentSearch?: (query: string, limit: number, options: ContentSearchOptions) => Promise<ContentSearchResult>;
|
|
68
85
|
contentStatus?: () => ContentState;
|
|
69
86
|
ensure?: () => void;
|
|
70
87
|
maxResults: number;
|
package/lib/combined.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combined.d.ts","sourceRoot":"","sources":["../src/combined.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"combined.d.ts","sourceRoot":"","sources":["../src/combined.ts"],"names":[],"mappings":"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"}
|
package/lib/combined.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The name index and the content index live in different stores, so this module
|
|
6
6
|
* owns the orchestration and the merge rules as pure, injectable functions:
|
|
7
|
-
* scope gating, the lazy-build guard for empty queries, per-group degradation
|
|
8
|
-
*
|
|
7
|
+
* scope gating, the lazy-build guard for empty queries, per-group degradation,
|
|
8
|
+
* match-option routing (content vs name engine), page counts and de-duplication
|
|
9
|
+
* of a path that appears both as a body hit and a name hit.
|
|
9
10
|
* @module dsh-locator/combined
|
|
10
11
|
*/
|
|
12
|
+
import { checkContentQuery, decodeExtendedForNames } from "./content-search.js";
|
|
11
13
|
/**
|
|
12
14
|
* Read the three scope flags from a request body. Anything that is not an
|
|
13
15
|
* explicit `false` counts as enabled, so a panel that only sends `{ content:
|
|
@@ -39,6 +41,12 @@ export function readCombinedRequest(body, maxResults) {
|
|
|
39
41
|
sort: sortKey(body.sort),
|
|
40
42
|
desc: body.desc === true,
|
|
41
43
|
scopes: readScopes(body),
|
|
44
|
+
matchMode: matchMode(body.matchMode),
|
|
45
|
+
caseSensitive: body.caseSensitive === true,
|
|
46
|
+
wholeWord: body.wholeWord === true,
|
|
47
|
+
sessionId: typeof body.sessionId === 'string' && body.sessionId.trim() !== '' ? body.sessionId : null,
|
|
48
|
+
count: countMode(body.count),
|
|
49
|
+
countLimit: clamp(body.countLimit, 1, 5000, 1000),
|
|
42
50
|
};
|
|
43
51
|
}
|
|
44
52
|
/**
|
|
@@ -80,9 +88,10 @@ export async function runCombinedSearch(deps, request) {
|
|
|
80
88
|
const { scopes, query } = request;
|
|
81
89
|
const contentEnabled = deps.contentSearch !== undefined;
|
|
82
90
|
const wantsContent = scopes.content && contentEnabled;
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
91
|
+
// Any non-empty query is evaluated against the content store; short or
|
|
92
|
+
// over-limit queries are rejected by the same validator the store uses, so
|
|
93
|
+
// the panel can show `content-query-too-short` without starting a build.
|
|
94
|
+
const askContent = wantsContent && query.trim() !== '';
|
|
86
95
|
const contentPromise = wantsContent ? contentGroup(deps, request, askContent) : Promise.resolve(undefined);
|
|
87
96
|
const filesPromise = scopes.files ? nameGroup(deps, request, true) : Promise.resolve(undefined);
|
|
88
97
|
const dirsPromise = scopes.dirs ? nameGroup(deps, request, false) : Promise.resolve(undefined);
|
|
@@ -111,16 +120,42 @@ export async function runCombinedSearch(deps, request) {
|
|
|
111
120
|
/** Page through a directory-shaped name query. */
|
|
112
121
|
async function nameGroup(deps, request, files) {
|
|
113
122
|
const started = Date.now();
|
|
123
|
+
const empty = (error, warnings) => ({
|
|
124
|
+
hits: [],
|
|
125
|
+
returned: 0,
|
|
126
|
+
total: 0,
|
|
127
|
+
truncated: false,
|
|
128
|
+
deduped: 0,
|
|
129
|
+
count: { value: 0, kind: 'exact' },
|
|
130
|
+
tookMs: Date.now() - started,
|
|
131
|
+
...(warnings === undefined || warnings.length === 0 ? {} : { warnings }),
|
|
132
|
+
...(error === undefined ? {} : { error }),
|
|
133
|
+
});
|
|
134
|
+
// The name engine understands everything except Notepad++-style escapes:
|
|
135
|
+
// decode them here and drop control characters (file names cannot contain
|
|
136
|
+
// them) instead of searching for a literal backslash sequence.
|
|
137
|
+
let nameQuery = request.query;
|
|
138
|
+
let warnings = [];
|
|
139
|
+
if (request.matchMode === 'extended') {
|
|
140
|
+
const decoded = decodeExtendedForNames(request.query);
|
|
141
|
+
warnings = decoded.warnings;
|
|
142
|
+
if (!decoded.ok)
|
|
143
|
+
return empty(decoded.error ?? 'invalid-escape', warnings);
|
|
144
|
+
nameQuery = decoded.text;
|
|
145
|
+
// An all-control-escape query must not degrade into "match everything".
|
|
146
|
+
if (nameQuery.trim() === '')
|
|
147
|
+
return empty(undefined, [...new Set([...warnings, 'name-extended-empty'])]);
|
|
148
|
+
}
|
|
114
149
|
try {
|
|
115
150
|
const result = await deps.search({
|
|
116
|
-
query:
|
|
151
|
+
query: nameQuery,
|
|
117
152
|
limit: request.limit,
|
|
118
153
|
offset: 0,
|
|
119
154
|
sort: request.sort,
|
|
120
155
|
desc: request.desc,
|
|
121
|
-
regex:
|
|
122
|
-
caseSensitive:
|
|
123
|
-
wholeWord:
|
|
156
|
+
regex: request.matchMode === 'regex',
|
|
157
|
+
caseSensitive: request.caseSensitive,
|
|
158
|
+
wholeWord: request.wholeWord,
|
|
124
159
|
filesOnly: files,
|
|
125
160
|
dirsOnly: !files,
|
|
126
161
|
});
|
|
@@ -130,17 +165,19 @@ async function nameGroup(deps, request, files) {
|
|
|
130
165
|
total: result.total,
|
|
131
166
|
truncated: result.truncated,
|
|
132
167
|
deduped: 0,
|
|
168
|
+
count: { value: result.total, kind: 'exact' },
|
|
133
169
|
tookMs: result.tookNs / 1e6,
|
|
170
|
+
...(warnings.length === 0 ? {} : { warnings: [...new Set(warnings)] }),
|
|
134
171
|
};
|
|
135
172
|
}
|
|
136
173
|
catch (error) {
|
|
137
|
-
return {
|
|
174
|
+
return { ...empty(messageOf(error)), tookMs: Date.now() - started };
|
|
138
175
|
}
|
|
139
176
|
}
|
|
140
177
|
/** Query the content store, or report its state without starting a build. */
|
|
141
178
|
async function contentGroup(deps, request, ask) {
|
|
142
179
|
const started = Date.now();
|
|
143
|
-
const empty = () => {
|
|
180
|
+
const empty = (warnings = []) => {
|
|
144
181
|
let state;
|
|
145
182
|
try {
|
|
146
183
|
state = deps.contentStatus?.();
|
|
@@ -152,6 +189,8 @@ async function contentGroup(deps, request, ask) {
|
|
|
152
189
|
hits: [],
|
|
153
190
|
returned: 0,
|
|
154
191
|
capped: false,
|
|
192
|
+
count: { value: 0, kind: 'unknown' },
|
|
193
|
+
warnings,
|
|
155
194
|
building: state?.building ?? false,
|
|
156
195
|
files: state?.files ?? 0,
|
|
157
196
|
sessions: state?.sessions ?? 0,
|
|
@@ -160,22 +199,47 @@ async function contentGroup(deps, request, ask) {
|
|
|
160
199
|
tookMs: 0,
|
|
161
200
|
};
|
|
162
201
|
};
|
|
202
|
+
// 0.9.0 never scans the whole corpus from a panel keystroke: the content
|
|
203
|
+
// group pauses itself in regex mode while the name groups still run.
|
|
204
|
+
if (request.matchMode === 'regex') {
|
|
205
|
+
return { ...empty(['content-regex-paused']), error: 'content-regex-not-supported' };
|
|
206
|
+
}
|
|
163
207
|
if (!ask || deps.contentSearch === undefined)
|
|
164
208
|
return empty();
|
|
209
|
+
// Reject sub-trigram / over-limit / control-character queries here, before
|
|
210
|
+
// deps.ensure() can start a lazy content build the store cannot answer. The
|
|
211
|
+
// store performs the same check; sharing it keeps the error codes identical.
|
|
212
|
+
const check = checkContentQuery(request.query, request.matchMode);
|
|
213
|
+
if (!check.ok) {
|
|
214
|
+
return { ...empty(check.warnings), error: check.error ?? 'content-query-invalid', tookMs: Date.now() - started };
|
|
215
|
+
}
|
|
216
|
+
if (check.terms.length === 0)
|
|
217
|
+
return empty();
|
|
218
|
+
const options = {
|
|
219
|
+
matchMode: request.matchMode,
|
|
220
|
+
caseSensitive: request.caseSensitive,
|
|
221
|
+
wholeWord: request.wholeWord,
|
|
222
|
+
count: request.count,
|
|
223
|
+
countLimit: request.countLimit,
|
|
224
|
+
...(request.sessionId === null ? {} : { sessionId: request.sessionId }),
|
|
225
|
+
};
|
|
165
226
|
try {
|
|
166
227
|
deps.ensure?.();
|
|
167
|
-
const
|
|
228
|
+
const result = await deps.contentSearch(request.query, request.contentLimit, options);
|
|
168
229
|
const state = deps.contentStatus?.();
|
|
169
230
|
return {
|
|
170
|
-
hits,
|
|
171
|
-
returned: hits.length,
|
|
172
|
-
capped: hits.length >= request.contentLimit,
|
|
231
|
+
hits: result.hits,
|
|
232
|
+
returned: result.hits.length,
|
|
233
|
+
capped: result.hits.length >= request.contentLimit,
|
|
234
|
+
count: result.count,
|
|
235
|
+
warnings: result.warnings,
|
|
173
236
|
building: state?.building ?? false,
|
|
174
237
|
files: state?.files ?? 0,
|
|
175
238
|
sessions: state?.sessions ?? 0,
|
|
176
239
|
messages: state?.messages ?? 0,
|
|
177
240
|
truncated: state?.truncated ?? false,
|
|
178
241
|
tookMs: Date.now() - started,
|
|
242
|
+
...(result.error === undefined ? {} : { error: result.error }),
|
|
179
243
|
};
|
|
180
244
|
}
|
|
181
245
|
catch (error) {
|
|
@@ -187,6 +251,12 @@ function clamp(value, min, max, fallback) {
|
|
|
187
251
|
return fallback;
|
|
188
252
|
return Math.min(max, Math.max(min, Math.trunc(value)));
|
|
189
253
|
}
|
|
254
|
+
function matchMode(value) {
|
|
255
|
+
return value === 'extended' || value === 'regex' ? value : 'normal';
|
|
256
|
+
}
|
|
257
|
+
function countMode(value) {
|
|
258
|
+
return value === 'bounded' || value === 'none' ? value : 'exact';
|
|
259
|
+
}
|
|
190
260
|
function sortKey(value) {
|
|
191
261
|
return value === 'name' || value === 'path' || value === 'size' || value === 'mtime' || value === 'ext' ? value : 'mtime';
|
|
192
262
|
}
|
package/lib/combined.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combined.js","sourceRoot":"","sources":["../src/combined.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"combined.js","sourceRoot":"","sources":["../src/combined.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Search worker:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Search worker: content queries run here so a slow query can never stall the
|
|
3
|
+
* DSH host's event loop. A read-only connection makes the build's writes in the
|
|
4
|
+
* main thread safe to observe concurrently (WAL).
|
|
5
|
+
*
|
|
6
|
+
* Every request is answered with a complete {@link ContentSearchResult}; the
|
|
7
|
+
* worker never throws into the channel, because the host treats a timeout or a
|
|
8
|
+
* malformed reply as a hard error rather than re-running the query on the main
|
|
9
|
+
* thread.
|
|
5
10
|
* @module dsh-locator/content-search-worker
|
|
6
11
|
*/
|
|
7
12
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-search-worker.d.ts","sourceRoot":"","sources":["../src/content-search-worker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"content-search-worker.d.ts","sourceRoot":"","sources":["../src/content-search-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Search worker:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Search worker: content queries run here so a slow query can never stall the
|
|
3
|
+
* DSH host's event loop. A read-only connection makes the build's writes in the
|
|
4
|
+
* main thread safe to observe concurrently (WAL).
|
|
5
|
+
*
|
|
6
|
+
* Every request is answered with a complete {@link ContentSearchResult}; the
|
|
7
|
+
* worker never throws into the channel, because the host treats a timeout or a
|
|
8
|
+
* malformed reply as a hard error rather than re-running the query on the main
|
|
9
|
+
* thread.
|
|
5
10
|
* @module dsh-locator/content-search-worker
|
|
6
11
|
*/
|
|
7
12
|
import { parentPort, workerData } from 'node:worker_threads';
|
|
8
13
|
import { DatabaseSync } from 'node:sqlite';
|
|
9
14
|
import { searchContent } from "./content-search.js";
|
|
15
|
+
/** A result shape every failure path can share. */
|
|
16
|
+
function failure(error, warnings = [error]) {
|
|
17
|
+
return { hits: [], count: { value: 0, kind: 'unknown' }, warnings, error };
|
|
18
|
+
}
|
|
10
19
|
let db = null;
|
|
11
20
|
function open(path) {
|
|
12
21
|
try {
|
|
@@ -21,34 +30,52 @@ function open(path) {
|
|
|
21
30
|
db = null;
|
|
22
31
|
}
|
|
23
32
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
parentPort?.on('message', (request) => {
|
|
28
|
-
if (request.open !== undefined) {
|
|
29
|
-
open(request.open);
|
|
30
|
-
parentPort?.postMessage({ id: request.id, ok: true });
|
|
31
|
-
return;
|
|
33
|
+
function post(message) {
|
|
34
|
+
try {
|
|
35
|
+
parentPort?.postMessage(message);
|
|
32
36
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
catch { /* the host is gone; nothing to do */ }
|
|
38
|
+
}
|
|
39
|
+
/** Handle one request; never throws. */
|
|
40
|
+
function handle(request) {
|
|
41
|
+
try {
|
|
42
|
+
if (request.open !== undefined) {
|
|
43
|
+
open(request.open);
|
|
44
|
+
post({ id: request.id, ok: true });
|
|
45
|
+
return;
|
|
36
46
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
if (request.close === true) {
|
|
48
|
+
try {
|
|
49
|
+
db?.close();
|
|
50
|
+
}
|
|
51
|
+
catch { /* ignore */ }
|
|
52
|
+
db = null;
|
|
53
|
+
post({ id: request.id, ok: true });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (typeof request.query !== 'string') {
|
|
57
|
+
post({ id: request.id, result: failure('content-search-worker-protocol-error') });
|
|
58
|
+
return;
|
|
47
59
|
}
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
if (db === null) {
|
|
61
|
+
post({ id: request.id, result: failure('content-index-unavailable') });
|
|
62
|
+
return;
|
|
50
63
|
}
|
|
64
|
+
const result = searchContent(db, request.query, request.limit ?? 30, request.options ?? {});
|
|
65
|
+
post({ id: request.id, result });
|
|
51
66
|
}
|
|
52
|
-
|
|
67
|
+
catch {
|
|
68
|
+
// A thrown SQLite/JS error is reported as a result so the host can show a
|
|
69
|
+
// clear error code instead of silently re-running the query in-process.
|
|
70
|
+
post({ id: request.id, result: failure('content-search-worker-error') });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const initial = workerData?.dbPath;
|
|
74
|
+
if (initial !== undefined)
|
|
75
|
+
open(initial);
|
|
76
|
+
parentPort?.on('message', (request) => {
|
|
77
|
+
if (typeof request !== 'object' || request === null || typeof request.id !== 'number')
|
|
78
|
+
return;
|
|
79
|
+
handle(request);
|
|
53
80
|
});
|
|
54
81
|
//# sourceMappingURL=content-search-worker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-search-worker.js","sourceRoot":"","sources":["../src/content-search-worker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"content-search-worker.js","sourceRoot":"","sources":["../src/content-search-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAuD,MAAM,qBAAqB,CAAA;AAWxG,mDAAmD;AACnD,SAAS,OAAO,CAAC,KAAa,EAAE,WAAqB,CAAC,KAAK,CAAC;IAC1D,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;AAC5E,CAAC;AAED,IAAI,EAAE,GAAwB,IAAI,CAAA;AAElC,SAAS,IAAI,CAAC,IAAY;IACxB,IAAI,CAAC;QAAC,EAAE,EAAE,KAAK,EAAE,CAAA;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC1C,EAAE,GAAG,IAAI,CAAA;IACT,IAAI,CAAC;QAAC,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,EAAE,GAAG,IAAI,CAAA;IAAC,CAAC;AAC7E,CAAC;AAED,SAAS,IAAI,CAAC,OAAgC;IAC5C,IAAI,CAAC;QAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,qCAAqC,CAAC,CAAC;AAC1F,CAAC;AAED,wCAAwC;AACxC,SAAS,MAAM,CAAC,OAAgB;IAC9B,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAClB,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;YAClC,OAAM;QACR,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YAC3B,IAAI,CAAC;gBAAC,EAAE,EAAE,KAAK,EAAE,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YAC1C,EAAE,GAAG,IAAI,CAAA;YACT,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;YAClC,OAAM;QACR,CAAC;QACD,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACtC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,sCAAsC,CAAC,EAAE,CAAC,CAAA;YACjF,OAAM;QACR,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAA;YACtE,OAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;QAC3F,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,wEAAwE;QACxE,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAA;IAC1E,CAAC;AACH,CAAC;AAED,MAAM,OAAO,GAAI,UAA8C,EAAE,MAAM,CAAA;AACvE,IAAI,OAAO,KAAK,SAAS;IAAE,IAAI,CAAC,OAAO,CAAC,CAAA;AAExC,UAAU,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,OAAgB,EAAE,EAAE;IAC7C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ;QAAE,OAAM;IAC7F,MAAM,CAAC,OAAO,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
package/lib/content-search.d.ts
CHANGED
|
@@ -1,15 +1,135 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Content FTS5 query
|
|
2
|
+
* Content FTS5 query engine: normal substring search, Notepad++-style extended
|
|
3
|
+
* escaping, case-sensitive GLOB matching and whole-word post-filtering, with
|
|
4
|
+
* exact or bounded counts.
|
|
5
|
+
*
|
|
6
|
+
* Shared by the host thread, the search worker and the combined panel search.
|
|
7
|
+
* The page path is deliberately two-phase: rank the rowids first, then read the
|
|
8
|
+
* few bodies and build the snippet in JS. Putting `snippet()` in the same
|
|
9
|
+
* `ORDER BY rank` statement made a SQLite sorter evaluate it for every match
|
|
10
|
+
* (measured 2.7 s for "the" on a 1 GB corpus; 60 ms with this split).
|
|
11
|
+
*
|
|
12
|
+
* Every unbounded-looking path has an explicit budget: queries shorter than a
|
|
13
|
+
* trigram are rejected instead of degrading into a full-table LIKE/GLOB scan,
|
|
14
|
+
* and the whole-word post-filter stops after a fixed number of rows, body bytes
|
|
15
|
+
* or milliseconds and reports a bounded count.
|
|
3
16
|
* @module dsh-locator/content-search
|
|
4
17
|
*/
|
|
5
18
|
import type { DatabaseSync } from 'node:sqlite';
|
|
6
19
|
import type { ContentHit } from './content.ts';
|
|
20
|
+
/** How the query text is interpreted. */
|
|
21
|
+
export type ContentMatchMode = 'normal' | 'extended' | 'regex';
|
|
22
|
+
/** How much work the caller wants the count to spend. */
|
|
23
|
+
export type ContentCountMode = 'exact' | 'bounded' | 'none';
|
|
24
|
+
/** A match count, with its confidence. `bounded` means "at least value". */
|
|
25
|
+
export interface ContentCount {
|
|
26
|
+
value: number;
|
|
27
|
+
kind: 'exact' | 'bounded' | 'unknown';
|
|
28
|
+
limit?: number;
|
|
29
|
+
}
|
|
7
30
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @returns hits, ordered by relevance.
|
|
31
|
+
* Hard budget for the whole-word post-filter, which has to read candidate
|
|
32
|
+
* bodies in JS. Exceeding any cap stops the scan and keeps the hits found so
|
|
33
|
+
* far; the count then becomes bounded. Exposed mainly so embedders and tests
|
|
34
|
+
* can shrink the caps.
|
|
13
35
|
*/
|
|
14
|
-
export
|
|
36
|
+
export interface ContentScanBudget {
|
|
37
|
+
maxRows?: number;
|
|
38
|
+
maxBytes?: number;
|
|
39
|
+
maxMs?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Options accepted by {@link searchContent} and the content search worker. */
|
|
42
|
+
export interface ContentSearchOptions {
|
|
43
|
+
matchMode?: ContentMatchMode;
|
|
44
|
+
caseSensitive?: boolean;
|
|
45
|
+
wholeWord?: boolean;
|
|
46
|
+
/** Resolved by ContentIndex from the request's sessionId. */
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
/** Session log path; set by ContentIndex after resolving sessionId. */
|
|
49
|
+
sessionPath?: string | null;
|
|
50
|
+
count?: ContentCountMode;
|
|
51
|
+
countLimit?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Keep only file/session documents whose stored path lives under this
|
|
54
|
+
* directory prefix (the locator_find `scope: workspace` filter). Session
|
|
55
|
+
* documents are unaffected: they are scoped by `sessionPath`/`sessionId`.
|
|
56
|
+
*/
|
|
57
|
+
pathPrefix?: string | null;
|
|
58
|
+
/** Override for the whole-word scan caps; production uses the defaults. */
|
|
59
|
+
scanBudget?: ContentScanBudget;
|
|
60
|
+
}
|
|
61
|
+
/** Result of one content query. */
|
|
62
|
+
export interface ContentSearchResult {
|
|
63
|
+
hits: ContentHit[];
|
|
64
|
+
count: ContentCount;
|
|
65
|
+
warnings: string[];
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
/** Text escaped for an FTS5 quoted phrase (`"` doubled). */
|
|
69
|
+
export declare function quoteFts(text: string): string;
|
|
70
|
+
/** Escape a literal for SQLite GLOB (`*`, `?`, `[`, `]`). */
|
|
71
|
+
export declare function escapeGlob(text: string): string;
|
|
72
|
+
/** Escape a literal for SQL LIKE with `ESCAPE '\'`. */
|
|
73
|
+
export declare function escapeLike(text: string): string;
|
|
74
|
+
/** Decoded extended-mode query text. */
|
|
75
|
+
export interface DecodedExtended {
|
|
76
|
+
ok: boolean;
|
|
77
|
+
text: string;
|
|
78
|
+
warnings: string[];
|
|
79
|
+
/** `invalid-escape` when `\\xNN` is malformed. */
|
|
80
|
+
error?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Decode Notepad++-style extended escapes: `\n`, `\r`, `\t`, `\0`, `\\`,
|
|
84
|
+
* `\xNN`. Unknown escapes are kept literally with a warning.
|
|
85
|
+
*/
|
|
86
|
+
export declare function decodeExtended(input: string): DecodedExtended;
|
|
87
|
+
/**
|
|
88
|
+
* Extended decoding for file names: names cannot contain control characters, so
|
|
89
|
+
* they are dropped from the pattern and reported instead of silently matching
|
|
90
|
+
* nothing.
|
|
91
|
+
*/
|
|
92
|
+
export declare function decodeExtendedForNames(input: string): DecodedExtended;
|
|
93
|
+
/** Result of the query-entry checks shared by searchContent and the panel. */
|
|
94
|
+
export interface ContentQueryCheck {
|
|
95
|
+
ok: boolean;
|
|
96
|
+
/** Literal terms for normal mode; one phrase for extended mode. */
|
|
97
|
+
terms: string[];
|
|
98
|
+
phrase: boolean;
|
|
99
|
+
warnings: string[];
|
|
100
|
+
error?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validate a content query before it reaches SQL.
|
|
104
|
+
*
|
|
105
|
+
* Content search only supports substrings the FTS5 trigram tokenizer can
|
|
106
|
+
* serve. A 1–2 character term used to fall back to a whole-corpus `LIKE` scan,
|
|
107
|
+
* which is both slow and unbounded; it is now a hard, visible error instead.
|
|
108
|
+
* The same limits apply after extended-mode escapes are decoded, and lengths
|
|
109
|
+
* are measured in Unicode code points so emoji are not mistaken for long text.
|
|
110
|
+
* @param query - raw query text.
|
|
111
|
+
* @param matchMode - `normal` (whitespace-separated terms) or `extended`.
|
|
112
|
+
* @returns normalized terms, or the first entry error.
|
|
113
|
+
*/
|
|
114
|
+
export declare function checkContentQuery(query: string, matchMode: ContentMatchMode): ContentQueryCheck;
|
|
115
|
+
/** Whether `index` starts a whole-word occurrence of `length` bytes. */
|
|
116
|
+
export declare function isWholeWord(hay: string, index: number, length: number): boolean;
|
|
117
|
+
/** One located match: byte offset and length. */
|
|
118
|
+
interface Located {
|
|
119
|
+
index: number;
|
|
120
|
+
length: number;
|
|
121
|
+
}
|
|
122
|
+
/** First whole-word (or plain) occurrence of any term, or null. */
|
|
123
|
+
export declare function locateMatch(body: string, terms: readonly string[], caseSensitive: boolean, wholeWord: boolean): Located | null;
|
|
124
|
+
/** 1-based line number of a byte offset. */
|
|
125
|
+
export declare function lineOf(body: string, index: number): number;
|
|
126
|
+
/**
|
|
127
|
+
* Run one content query.
|
|
128
|
+
*
|
|
129
|
+
* Modes: `normal` (whitespace-separated substring terms), `extended`
|
|
130
|
+
* (Notepad++ escapes, one literal phrase), `regex` (not executed server-side in
|
|
131
|
+
* 0.9.0: returns `content-regex-not-supported`).
|
|
132
|
+
*/
|
|
133
|
+
export declare function searchContent(db: DatabaseSync, query: string, limit: number, options?: ContentSearchOptions): ContentSearchResult;
|
|
134
|
+
export {};
|
|
15
135
|
//# sourceMappingURL=content-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-search.d.ts","sourceRoot":"","sources":["../src/content-search.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"content-search.d.ts","sourceRoot":"","sources":["../src/content-search.ts"],"names":[],"mappings":"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"}
|