dsh-locator 0.7.1 → 0.7.4

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 (49) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README-es.md +35 -5
  3. package/README-hi.md +33 -5
  4. package/README-pt.md +31 -5
  5. package/README.en.md +32 -4
  6. package/README.md +25 -5
  7. package/client.js +117 -17
  8. package/cordis.patch.yml +13 -0
  9. package/lib/config.d.ts +20 -0
  10. package/lib/config.d.ts.map +1 -1
  11. package/lib/config.js +38 -4
  12. package/lib/config.js.map +1 -1
  13. package/lib/content-search-worker.d.ts +8 -0
  14. package/lib/content-search-worker.d.ts.map +1 -0
  15. package/lib/content-search-worker.js +54 -0
  16. package/lib/content-search-worker.js.map +1 -0
  17. package/lib/content-search.d.ts +15 -0
  18. package/lib/content-search.d.ts.map +1 -0
  19. package/lib/content-search.js +38 -0
  20. package/lib/content-search.js.map +1 -0
  21. package/lib/content.d.ts +70 -4
  22. package/lib/content.d.ts.map +1 -1
  23. package/lib/content.js +420 -77
  24. package/lib/content.js.map +1 -1
  25. package/lib/find.d.ts +1 -1
  26. package/lib/find.d.ts.map +1 -1
  27. package/lib/find.js +8 -3
  28. package/lib/find.js.map +1 -1
  29. package/lib/http.d.ts +1 -1
  30. package/lib/http.d.ts.map +1 -1
  31. package/lib/http.js +4 -2
  32. package/lib/http.js.map +1 -1
  33. package/lib/index.d.ts +5 -1
  34. package/lib/index.d.ts.map +1 -1
  35. package/lib/index.js +102 -27
  36. package/lib/index.js.map +1 -1
  37. package/lib/reindex.d.ts +1 -1
  38. package/lib/reindex.d.ts.map +1 -1
  39. package/lib/reindex.js +2 -1
  40. package/lib/reindex.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/config.ts +58 -4
  43. package/src/content-search-worker.ts +52 -0
  44. package/src/content-search.ts +39 -0
  45. package/src/content.ts +387 -71
  46. package/src/find.ts +8 -3
  47. package/src/http.ts +4 -2
  48. package/src/index.ts +102 -25
  49. package/src/reindex.ts +2 -1
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Search worker: the FTS5 query runs here so a slow content query can never
3
+ * stall the DSH host's event loop. A read-only connection makes the build's
4
+ * writes in the main thread safe to observe concurrently (WAL).
5
+ * @module dsh-locator/content-search-worker
6
+ */
7
+
8
+ import { parentPort, workerData } from 'node:worker_threads'
9
+ import { DatabaseSync } from 'node:sqlite'
10
+ import { searchContent } from './content-search.ts'
11
+ import type { ContentHit } from './content.ts'
12
+
13
+ interface Request {
14
+ id: number
15
+ open?: string
16
+ close?: boolean
17
+ query?: string
18
+ limit?: number
19
+ }
20
+
21
+ let db: DatabaseSync | null = null
22
+
23
+ function open(path: string): void {
24
+ try { db?.close() } catch { /* ignore */ }
25
+ db = null
26
+ try { db = new DatabaseSync(path, { readOnly: true }) } catch { db = null }
27
+ }
28
+
29
+ const initial = (workerData as { dbPath?: string } | undefined)?.dbPath
30
+ if (initial !== undefined) open(initial)
31
+
32
+ parentPort?.on('message', (request: Request) => {
33
+ if (request.open !== undefined) {
34
+ open(request.open)
35
+ parentPort?.postMessage({ id: request.id, ok: true })
36
+ return
37
+ }
38
+ if (request.close === true) {
39
+ try { db?.close() } catch { /* ignore */ }
40
+ db = null
41
+ parentPort?.postMessage({ id: request.id, ok: true })
42
+ return
43
+ }
44
+ let hits: ContentHit[] = []
45
+ let error = ''
46
+ if (db !== null) {
47
+ try { hits = searchContent(db, request.query ?? '', request.limit ?? 30) } catch (cause) {
48
+ error = cause instanceof Error ? cause.message : String(cause)
49
+ }
50
+ }
51
+ parentPort?.postMessage({ id: request.id, hits, error })
52
+ })
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Content FTS5 query shared by the host thread and the search worker.
3
+ * @module dsh-locator/content-search
4
+ */
5
+
6
+ import type { DatabaseSync } from 'node:sqlite'
7
+ import type { ContentHit } from './content.ts'
8
+
9
+ /**
10
+ * Run one content query against an open SQLite handle.
11
+ * @param db - open database (read-only in the worker).
12
+ * @param query - user query; shorter than three characters falls back to LIKE.
13
+ * @param limit - maximum hits.
14
+ * @returns hits, ordered by relevance.
15
+ */
16
+ export function searchContent(db: DatabaseSync, query: string, limit: number): ContentHit[] {
17
+ const text = query.trim()
18
+ if (text === '') return []
19
+ const capped = Math.max(1, Math.min(100, limit))
20
+ let rows: Array<Record<string, unknown>>
21
+ if ([...text].length < 3) {
22
+ rows = db.prepare(`SELECT kind, path, title, session_id, message_id, body, 0 AS score
23
+ FROM docs WHERE body LIKE '%' || ? || '%' LIMIT ?`).all(text, capped) as Array<Record<string, unknown>>
24
+ } else {
25
+ const match = text.split(/\s+/).filter(Boolean).map(term => '"' + term.replace(/"/g, '') + '"').join(' AND ')
26
+ rows = db.prepare(`SELECT kind, path, title, session_id, message_id,
27
+ snippet(docs, 0, char(1), char(2), '…', 16) AS snippet, bm25(docs) AS score
28
+ FROM docs WHERE docs MATCH ? ORDER BY score LIMIT ?`).all(match, capped) as Array<Record<string, unknown>>
29
+ }
30
+ return rows.map(row => ({
31
+ kind: row.kind === 'session' ? 'session' : 'file',
32
+ path: String(row.path ?? ''),
33
+ title: String(row.title ?? ''),
34
+ snippet: String(row.snippet ?? row.body ?? '').replace(/[\u0001\u0002]/g, ''),
35
+ ...(row.session_id ? { sessionId: String(row.session_id) } : {}),
36
+ ...(row.message_id ? { messageId: String(row.message_id) } : {}),
37
+ score: Number(row.score ?? 0),
38
+ }))
39
+ }