@plebeiantech/lat.md 0.12.3-fork.17 → 0.12.3-fork.20
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/README.md +1 -1
- package/dist/src/cli/agent-invocation.d.ts +6 -0
- package/dist/src/cli/agent-invocation.js +23 -0
- package/dist/src/cli/fork-instructions.js +1 -1
- package/dist/src/cli/fork-scaffold.js +2 -2
- package/dist/src/cli/graph.d.ts +1 -1
- package/dist/src/cli/hook.js +11 -11
- package/dist/src/cli/index.js +41 -59
- package/dist/src/cli/init-preferences.js +5 -4
- package/dist/src/cli/init.d.ts +1 -1
- package/dist/src/cli/init.js +54 -45
- package/dist/src/cli/reindex.d.ts +1 -1
- package/dist/src/cli/reindex.js +2 -2
- package/dist/src/cli/search.d.ts +4 -4
- package/dist/src/cli/search.js +41 -28
- package/dist/src/cli/ui-build-server.d.ts +1 -1
- package/dist/src/cli/ui-build.d.ts +1 -1
- package/dist/src/cli/ui.d.ts +1 -1
- package/dist/src/fork/antigravity-hook.js +6 -6
- package/dist/src/fork/antigravity-init.d.ts +2 -2
- package/dist/src/fork/antigravity-init.js +2 -2
- package/dist/src/graph-export.js +7 -7
- package/dist/src/knowledge/index.js +2 -2
- package/dist/src/mcp/server.js +8 -8
- package/dist/src/search/cache.d.ts +1 -1
- package/dist/src/search/cache.js +40 -7
- package/dist/src/search/chunks.d.ts +2 -2
- package/dist/src/search/db.d.ts +5 -4
- package/dist/src/search/db.js +36 -40
- package/dist/src/search/embedder.js +1 -1
- package/dist/src/search/index.d.ts +1 -1
- package/dist/src/search/index.js +2 -2
- package/dist/src/search/lock.d.ts +4 -1
- package/dist/src/search/lock.js +30 -8
- package/dist/src/search/query.d.ts +5 -2
- package/dist/src/search/query.js +55 -38
- package/dist/src/search/search.d.ts +3 -1
- package/dist/src/search/search.js +21 -12
- package/dist/src/search/types.d.ts +2 -18
- package/dist/src/view/client/assets/{GraphView-BWlRdQM4.js → GraphView-Dij8mAA_.js} +1 -1
- package/dist/src/view/client/assets/{MarkdownEditor-MW-Ck1O0.js → MarkdownEditor-88Wbt0M1.js} +1 -1
- package/dist/src/view/client/assets/{abnfDiagram-VCTEODGH-Bn3RshMV.js → abnfDiagram-VCTEODGH-y0DlYa8E.js} +1 -1
- package/dist/src/view/client/assets/{arc-En9tdFQF.js → arc-FlMu8dnO.js} +1 -1
- package/dist/src/view/client/assets/{architectureDiagram-5GKGNRK7-B-9J1VAM.js → architectureDiagram-5GKGNRK7-DA_uWZW1.js} +1 -1
- package/dist/src/view/client/assets/{blockDiagram-I7D4REHJ-QlznwalP.js → blockDiagram-I7D4REHJ-BHQ5LU85.js} +1 -1
- package/dist/src/view/client/assets/{c4Diagram-7LVT6UL2-Bp2y8B4_.js → c4Diagram-7LVT6UL2-BRucKCt2.js} +1 -1
- package/dist/src/view/client/assets/channel-BgDNA_Ay.js +1 -0
- package/dist/src/view/client/assets/{chunk-2Q5K7J3B-CnBKL-lm.js → chunk-2Q5K7J3B-BJPcFivt.js} +1 -1
- package/dist/src/view/client/assets/{chunk-5VM5RSS4-LLYMi_n6.js → chunk-5VM5RSS4-v2q99bGz.js} +1 -1
- package/dist/src/view/client/assets/{chunk-F27PBJKO-VIeO7VF0.js → chunk-F27PBJKO-CcHOuJCs.js} +1 -1
- package/dist/src/view/client/assets/{chunk-IMKFNOWR-D5i67DVD.js → chunk-IMKFNOWR-DNxYuJNq.js} +1 -1
- package/dist/src/view/client/assets/{chunk-JWPE2WC7-D4qAtoL3.js → chunk-JWPE2WC7-C0eHKvLW.js} +1 -1
- package/dist/src/view/client/assets/{chunk-POPQ4Y6H-BMzOyiW_.js → chunk-POPQ4Y6H-57CWp2Ve.js} +1 -1
- package/dist/src/view/client/assets/{chunk-SVP7TREG-DWy5nbfp.js → chunk-SVP7TREG-BqcoxIJw.js} +1 -1
- package/dist/src/view/client/assets/{chunk-TICWLB2K-CVqCANFB.js → chunk-TICWLB2K-DRUJR5zH.js} +1 -1
- package/dist/src/view/client/assets/{chunk-XXDRQBXY-Bp23h6Tf.js → chunk-XXDRQBXY-Dez0oRoM.js} +1 -1
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-CFZ_Sgh5.js +1 -0
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-CFZ_Sgh5.js +1 -0
- package/dist/src/view/client/assets/{cose-bilkent-JH36ORCC-BCw7fCC2.js → cose-bilkent-JH36ORCC-BHtud6Hg.js} +1 -1
- package/dist/src/view/client/assets/{cynefin-OW5HDTMX--lYN4N9m.js → cynefin-OW5HDTMX-BjBW8VGk.js} +1 -1
- package/dist/src/view/client/assets/{cynefinDiagram-5FMLGOSQ-9sK5EV7v.js → cynefinDiagram-5FMLGOSQ-DwGuD7oF.js} +1 -1
- package/dist/src/view/client/assets/{dagre-GXQ25YYZ-CoMdEFCq.js → dagre-GXQ25YYZ-C5zSlGK9.js} +1 -1
- package/dist/src/view/client/assets/{diagram-S7CK7UJ4-D13dyWdd.js → diagram-S7CK7UJ4-C_NhA3bj.js} +1 -1
- package/dist/src/view/client/assets/{diagram-UQ7AKVKN-PdwLPPPA.js → diagram-UQ7AKVKN-az7QPYNb.js} +1 -1
- package/dist/src/view/client/assets/{diagram-VSXAHHWV-CtWWa_A7.js → diagram-VSXAHHWV-BRvDuM8_.js} +1 -1
- package/dist/src/view/client/assets/{diagram-VX7I27RA-BeCYcSU-.js → diagram-VX7I27RA-BWLgF6S-.js} +1 -1
- package/dist/src/view/client/assets/{diagram-Z3DM3KII-DiWabOBM.js → diagram-Z3DM3KII-DV3d6mL5.js} +1 -1
- package/dist/src/view/client/assets/{ebnfDiagram-PWID7BFC-CZQ3MD_K.js → ebnfDiagram-PWID7BFC-yUXrEAvK.js} +1 -1
- package/dist/src/view/client/assets/{erDiagram-RLTQ6QDP-B1AhXIrP.js → erDiagram-RLTQ6QDP-D35FN1Pt.js} +1 -1
- package/dist/src/view/client/assets/{flowDiagram-HODETNUW-Bhr0JM6P.js → flowDiagram-HODETNUW-BciZqsjT.js} +1 -1
- package/dist/src/view/client/assets/{ganttDiagram-EL5Y4UJY-CbP8iAkg.js → ganttDiagram-EL5Y4UJY-BysPGgXS.js} +1 -1
- package/dist/src/view/client/assets/{gitGraphDiagram-WWUBYQGX-B9uuGdLF.js → gitGraphDiagram-WWUBYQGX-C3DdJ5xG.js} +1 -1
- package/dist/src/view/client/assets/index-CJokoRSp.js +13 -0
- package/dist/src/view/client/assets/{infoDiagram-27XIBGKW-BrfwQ_Cq.js → infoDiagram-27XIBGKW-D3HZrDRb.js} +1 -1
- package/dist/src/view/client/assets/{ishikawaDiagram-5VMMS53U-CHtJ36re.js → ishikawaDiagram-5VMMS53U-BXyVIXTU.js} +1 -1
- package/dist/src/view/client/assets/{journeyDiagram-3NMN7TZE-BChcgX1s.js → journeyDiagram-3NMN7TZE-D4-1NxVU.js} +1 -1
- package/dist/src/view/client/assets/{kanban-definition-UXKFOSKX-BnwFsKiB.js → kanban-definition-UXKFOSKX-4Gu_5yBA.js} +1 -1
- package/dist/src/view/client/assets/{layout-Dnd0pLdb.js → layout-Jf1K3TMM.js} +1 -1
- package/dist/src/view/client/assets/{linear-CFhW2ozn.js → linear-DWfwzUT_.js} +1 -1
- package/dist/src/view/client/assets/{mermaid.core--OP6lXtW.js → mermaid.core-DMx_DAl8.js} +5 -5
- package/dist/src/view/client/assets/{mindmap-definition-YA3MSWOX-D51y2zbm.js → mindmap-definition-YA3MSWOX-8pxY_RWi.js} +1 -1
- package/dist/src/view/client/assets/{pegDiagram-XKGWAZYB--zyQJDvI.js → pegDiagram-XKGWAZYB-DNAw_O5t.js} +1 -1
- package/dist/src/view/client/assets/{pieDiagram-E7YTZNPT-Cj8mvpJy.js → pieDiagram-E7YTZNPT-uuvRCDkC.js} +1 -1
- package/dist/src/view/client/assets/{quadrantDiagram-AXDQQJYC-qvhJrnBf.js → quadrantDiagram-AXDQQJYC-BER2O4Aq.js} +1 -1
- package/dist/src/view/client/assets/{railroadDiagram-O6MQD6OU-CtWvxpSo.js → railroadDiagram-O6MQD6OU-CxEsJ_0n.js} +1 -1
- package/dist/src/view/client/assets/{requirementDiagram-BXWQKSXE-CBxen_KG.js → requirementDiagram-BXWQKSXE-yS9rspca.js} +1 -1
- package/dist/src/view/client/assets/{sankeyDiagram-P5KCCOFB-B_fUkmfc.js → sankeyDiagram-P5KCCOFB-Bqt70a2j.js} +1 -1
- package/dist/src/view/client/assets/{sequenceDiagram-WJ2MYXX4-Dclv4p68.js → sequenceDiagram-WJ2MYXX4-BvOOEJNr.js} +1 -1
- package/dist/src/view/client/assets/{sizeCapture-INFHLROL-GfG0sMTZ.js → sizeCapture-INFHLROL-CvO_Pjjo.js} +1 -1
- package/dist/src/view/client/assets/{stateDiagram-D77RDMKH-YQWPnzxU.js → stateDiagram-D77RDMKH-KLmVOZkq.js} +1 -1
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-KCS8Mdzu.js +1 -0
- package/dist/src/view/client/assets/{swimlanes-42K2YHIH-5yiCveVS.js → swimlanes-42K2YHIH-DC1r4_sq.js} +1 -1
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-B2TcAwis.js +8 -0
- package/dist/src/view/client/assets/{timeline-definition-24CTP7MA-BvAbB5Zs.js → timeline-definition-24CTP7MA-ChXLOuBL.js} +1 -1
- package/dist/src/view/client/assets/{vennDiagram-4TSXK5OY-guC-r60n.js → vennDiagram-4TSXK5OY-D9_h25-Y.js} +1 -1
- package/dist/src/view/client/assets/{wardleyDiagram-VM6X3IG4-C0yXcU2x.js → wardleyDiagram-VM6X3IG4-CsQJGGW0.js} +1 -1
- package/dist/src/view/client/assets/{xychartDiagram-S5SC5T6Z-RPmkPNfK.js → xychartDiagram-S5SC5T6Z-DGMuPMlm.js} +1 -1
- package/dist/src/view/client/index.html +1 -1
- package/dist/src/view/diagnostics.d.ts +2 -2
- package/dist/src/view/diagnostics.js +5 -5
- package/dist/src/view/external-document-tree.js +1 -1
- package/dist/src/view/git-diff.js +1 -1
- package/dist/src/view/git.js +1 -1
- package/dist/src/view/graph.d.ts +2 -2
- package/dist/src/view/graph.js +2 -2
- package/dist/src/view/markdown.js +1 -1
- package/dist/src/view/preindexed-search.d.ts +1 -1
- package/dist/src/view/protocol.d.ts +2 -18
- package/dist/src/view/publication.d.ts +2 -0
- package/dist/src/view/publication.js +78 -0
- package/dist/src/view/references.d.ts +3 -3
- package/dist/src/view/references.js +3 -3
- package/dist/src/view/repository.d.ts +6 -6
- package/dist/src/view/repository.js +28 -14
- package/dist/src/view/request-security.d.ts +5 -0
- package/dist/src/view/request-security.js +46 -0
- package/dist/src/view/search-result.d.ts +1 -1
- package/dist/src/view/search-result.js +1 -1
- package/dist/src/view/search.d.ts +1 -1
- package/dist/src/view/server-build.d.ts +2 -2
- package/dist/src/view/server-build.js +2 -2
- package/dist/src/view/server-deployment.d.ts +1 -2
- package/dist/src/view/server-deployment.js +6 -46
- package/dist/src/view/server-index-worker.d.ts +1 -1
- package/dist/src/view/server.d.ts +1 -1
- package/dist/src/view/server.js +13 -3
- package/dist/src/view/source-target.js +2 -2
- package/dist/src/view/static-build.d.ts +1 -1
- package/dist/src/view/static-build.js +49 -17
- package/dist/src/view/store.d.ts +4 -2
- package/dist/src/view/store.js +31 -19
- package/dist/src/view/table-of-contents.d.ts +1 -1
- package/dist/src/view/table-of-contents.js +1 -1
- package/dist/src/view/vercel-server-build.d.ts +1 -1
- package/package.json +19 -29
- package/templates/AGENTS.md +2 -2
- package/templates/cursor-rules.md +2 -2
- package/templates/opencode-plugin.ts +9 -9
- package/templates/pi-extension.ts +11 -11
- package/templates/skill/SKILL.md +2 -1
- package/dist/src/cli/check-context.d.ts +0 -56
- package/dist/src/cli/check-context.js +0 -215
- package/dist/src/cli/check-coverage.d.ts +0 -2
- package/dist/src/cli/check-coverage.js +0 -75
- package/dist/src/cli/check-frontmatter.d.ts +0 -12
- package/dist/src/cli/check-frontmatter.js +0 -44
- package/dist/src/cli/check-mode.d.ts +0 -21
- package/dist/src/cli/check-mode.js +0 -394
- package/dist/src/cli/check-status.d.ts +0 -74
- package/dist/src/cli/check-status.js +0 -217
- package/dist/src/cli/check.d.ts +0 -52
- package/dist/src/cli/check.js +0 -834
- package/dist/src/cli/context.d.ts +0 -11
- package/dist/src/cli/context.js +0 -58
- package/dist/src/cli/expand.d.ts +0 -7
- package/dist/src/cli/expand.js +0 -141
- package/dist/src/cli/external.d.ts +0 -11
- package/dist/src/cli/external.js +0 -185
- package/dist/src/cli/gen-index.d.ts +0 -45
- package/dist/src/cli/gen-index.js +0 -235
- package/dist/src/cli/link-scheme.d.ts +0 -54
- package/dist/src/cli/link-scheme.js +0 -101
- package/dist/src/cli/locate.d.ts +0 -2
- package/dist/src/cli/locate.js +0 -18
- package/dist/src/cli/refs.d.ts +0 -23
- package/dist/src/cli/refs.js +0 -274
- package/dist/src/cli/section.d.ts +0 -45
- package/dist/src/cli/section.js +0 -328
- package/dist/src/cli/select-menu.d.ts +0 -13
- package/dist/src/cli/select-menu.js +0 -100
- package/dist/src/code-refs.d.ts +0 -29
- package/dist/src/code-refs.js +0 -479
- package/dist/src/config.d.ts +0 -29
- package/dist/src/config.js +0 -83
- package/dist/src/context.d.ts +0 -26
- package/dist/src/context.js +0 -11
- package/dist/src/document-formats.d.ts +0 -7
- package/dist/src/document-formats.js +0 -39
- package/dist/src/extensions/alert-marker.d.ts +0 -25
- package/dist/src/extensions/alert-marker.js +0 -55
- package/dist/src/extensions/wiki-link/from-markdown.d.ts +0 -5
- package/dist/src/extensions/wiki-link/from-markdown.js +0 -34
- package/dist/src/extensions/wiki-link/index.d.ts +0 -4
- package/dist/src/extensions/wiki-link/index.js +0 -3
- package/dist/src/extensions/wiki-link/syntax.d.ts +0 -14
- package/dist/src/extensions/wiki-link/syntax.js +0 -121
- package/dist/src/extensions/wiki-link/to-markdown.d.ts +0 -5
- package/dist/src/extensions/wiki-link/to-markdown.js +0 -28
- package/dist/src/extensions/wiki-link/types.d.ts +0 -34
- package/dist/src/extensions/wiki-link/types.js +0 -1
- package/dist/src/external-documents.d.ts +0 -56
- package/dist/src/external-documents.js +0 -404
- package/dist/src/external-sources.d.ts +0 -136
- package/dist/src/external-sources.js +0 -1098
- package/dist/src/fork/frontmatter-fields.d.ts +0 -36
- package/dist/src/fork/frontmatter-fields.js +0 -23
- package/dist/src/format.d.ts +0 -16
- package/dist/src/format.js +0 -76
- package/dist/src/init-version.d.ts +0 -10
- package/dist/src/init-version.js +0 -49
- package/dist/src/lattice-model.d.ts +0 -84
- package/dist/src/lattice-model.js +0 -467
- package/dist/src/lattice.d.ts +0 -14
- package/dist/src/lattice.js +0 -353
- package/dist/src/markdown-analysis-cache.d.ts +0 -20
- package/dist/src/markdown-analysis-cache.js +0 -139
- package/dist/src/markdown-analysis-worker.d.ts +0 -16
- package/dist/src/markdown-analysis-worker.js +0 -33
- package/dist/src/markdown-analysis.d.ts +0 -61
- package/dist/src/markdown-analysis.js +0 -169
- package/dist/src/markdown-analyzer-loader.d.ts +0 -4
- package/dist/src/markdown-analyzer-loader.js +0 -13
- package/dist/src/markdown-validation.d.ts +0 -25
- package/dist/src/markdown-validation.js +0 -104
- package/dist/src/parser-cache.d.ts +0 -17
- package/dist/src/parser-cache.js +0 -65
- package/dist/src/parser-import.d.ts +0 -8
- package/dist/src/parser-import.js +0 -1
- package/dist/src/parser.d.ts +0 -3
- package/dist/src/parser.js +0 -93
- package/dist/src/path.d.ts +0 -2
- package/dist/src/path.js +0 -4
- package/dist/src/profiler.d.ts +0 -16
- package/dist/src/profiler.js +0 -81
- package/dist/src/project-analysis.d.ts +0 -59
- package/dist/src/project-analysis.js +0 -233
- package/dist/src/project-discovery.d.ts +0 -6
- package/dist/src/project-discovery.js +0 -30
- package/dist/src/repository-path.d.ts +0 -11
- package/dist/src/repository-path.js +0 -52
- package/dist/src/source-formats.d.ts +0 -7
- package/dist/src/source-formats.js +0 -30
- package/dist/src/source-parser.d.ts +0 -67
- package/dist/src/source-parser.js +0 -1749
- package/dist/src/untrusted.d.ts +0 -18
- package/dist/src/untrusted.js +0 -93
- package/dist/src/view/client/assets/channel-uy0fFvcW.js +0 -1
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-DbSEqjA5.js +0 -1
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-DbSEqjA5.js +0 -1
- package/dist/src/view/client/assets/index-nBmbYfun.js +0 -13
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-gUj6Hzgl.js +0 -1
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-DR7JhrxL.js +0 -8
- package/dist/src/walk.d.ts +0 -20
- package/dist/src/walk.js +0 -121
package/dist/src/search/cache.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { copyFile, mkdir, rename, rm } from 'node:fs/promises';
|
|
1
|
+
import { copyFile, mkdir, rename, rm, stat } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { acquireSearchLock } from './lock.js';
|
|
3
|
+
import { acquireSearchLock, withSearchAccess } from './lock.js';
|
|
4
4
|
import { SearchDb, hasIndex, INDEX_FILE, ensureMeta, getStoredModel, } from './db.js';
|
|
5
5
|
/** Retry transient Windows handles without deleting the usable index first. */
|
|
6
6
|
async function withFileRetry(work) {
|
|
@@ -11,7 +11,7 @@ async function withFileRetry(work) {
|
|
|
11
11
|
}
|
|
12
12
|
catch (error) {
|
|
13
13
|
const code = error.code;
|
|
14
|
-
// Windows may briefly retain a staging handle or a reader
|
|
14
|
+
// Windows may briefly retain a staging handle or a reader handle.
|
|
15
15
|
if (process.platform !== 'win32' ||
|
|
16
16
|
attempt >= 9 ||
|
|
17
17
|
!['EBUSY', 'EPERM', 'EACCES'].includes(code ?? ''))
|
|
@@ -24,7 +24,7 @@ async function removeStaging(path) {
|
|
|
24
24
|
for (const suffix of ['', '-wal', '-shm', '-journal'])
|
|
25
25
|
await withFileRetry(() => rm(path + suffix, { force: true }));
|
|
26
26
|
}
|
|
27
|
-
/** Build a private staging database;
|
|
27
|
+
/** Build a private staging database; access locks protect copies and publication of search.db. */
|
|
28
28
|
export async function writeIndex(latDir, cacheDir, rebuild, work) {
|
|
29
29
|
const dir = cacheDir ?? join(latDir, '.cache');
|
|
30
30
|
await mkdir(dir, { recursive: true });
|
|
@@ -36,8 +36,19 @@ export async function writeIndex(latDir, cacheDir, rebuild, work) {
|
|
|
36
36
|
// A crashed writer may have left a partial database and journal behind.
|
|
37
37
|
await removeStaging(path);
|
|
38
38
|
const existing = hasIndex(dir);
|
|
39
|
-
if (existing && !rebuild)
|
|
40
|
-
await
|
|
39
|
+
if (existing && !rebuild) {
|
|
40
|
+
await withSearchAccess(dir, 'exclusive', async () => {
|
|
41
|
+
// Recover/checkpoint a WAL left by a process that died during a query.
|
|
42
|
+
const source = new SearchDb(activePath);
|
|
43
|
+
try {
|
|
44
|
+
await source.checkpoint();
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await source.close();
|
|
48
|
+
}
|
|
49
|
+
await copyFile(activePath, path);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
41
52
|
db = new SearchDb(path);
|
|
42
53
|
const model = existing && !rebuild ? await getStoredModel(db) : null;
|
|
43
54
|
await ensureMeta(db);
|
|
@@ -52,7 +63,29 @@ export async function writeIndex(latDir, cacheDir, rebuild, work) {
|
|
|
52
63
|
await db.close();
|
|
53
64
|
db = undefined;
|
|
54
65
|
if (!unchanged)
|
|
55
|
-
await
|
|
66
|
+
await withSearchAccess(dir, 'exclusive', async () => {
|
|
67
|
+
// Recover old sidecars before replacing their database, including after a crash.
|
|
68
|
+
if (existing) {
|
|
69
|
+
const wal = await stat(activePath + '-wal').catch((error) => {
|
|
70
|
+
if (error.code !== 'ENOENT')
|
|
71
|
+
throw error;
|
|
72
|
+
return undefined;
|
|
73
|
+
});
|
|
74
|
+
// A fresh rebuild must also repair an unreadable database with no WAL.
|
|
75
|
+
if (wal && wal.size > 0) {
|
|
76
|
+
const source = new SearchDb(activePath);
|
|
77
|
+
try {
|
|
78
|
+
await source.checkpoint();
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await source.close();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const suffix of ['-wal', '-shm', '-journal'])
|
|
85
|
+
await withFileRetry(() => rm(activePath + suffix, { force: true }));
|
|
86
|
+
}
|
|
87
|
+
await withFileRetry(() => rename(path, activePath));
|
|
88
|
+
});
|
|
56
89
|
return result;
|
|
57
90
|
}
|
|
58
91
|
finally {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SourceSpan } from './types.js';
|
|
2
2
|
import type { Embedder } from './embedder.js';
|
|
3
|
-
import type { MarkdownFileAnalysis } from '
|
|
4
|
-
import type { Section } from '
|
|
3
|
+
import type { MarkdownFileAnalysis } from '@plebeiantech/lat.md-core/markdown-analysis';
|
|
4
|
+
import type { Section } from '@plebeiantech/lat.md-core/lattice-model';
|
|
5
5
|
export declare const CHUNK_POLICY = "owned-blocks-v1";
|
|
6
6
|
export type Passage = {
|
|
7
7
|
id: string;
|
package/dist/src/search/db.d.ts
CHANGED
|
@@ -4,11 +4,12 @@ export declare function hasIndex(cacheDir: string): boolean;
|
|
|
4
4
|
/** Small SQL adapter; callers never depend on a libSQL connection. */
|
|
5
5
|
export declare class SearchDb {
|
|
6
6
|
readonly path: string;
|
|
7
|
-
private readonly
|
|
7
|
+
private readonly published;
|
|
8
8
|
private connection;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
private release;
|
|
10
|
+
constructor(path: string, published?: boolean);
|
|
11
11
|
private get;
|
|
12
|
+
private connect;
|
|
12
13
|
execute(statement: string | {
|
|
13
14
|
sql: string;
|
|
14
15
|
args?: any[];
|
|
@@ -18,7 +19,7 @@ export declare class SearchDb {
|
|
|
18
19
|
close(): Promise<void>;
|
|
19
20
|
checkpoint(): Promise<void>;
|
|
20
21
|
}
|
|
21
|
-
export declare function openDb(latDir: string, requestedCacheDir?: string,
|
|
22
|
+
export declare function openDb(latDir: string, requestedCacheDir?: string, published?: boolean): SearchDb;
|
|
22
23
|
export declare function ensureMeta(db: SearchDb): Promise<void>;
|
|
23
24
|
export declare function getStoredModel(db: SearchDb): Promise<string | null>;
|
|
24
25
|
export declare function setStoredModel(db: SearchDb, value: string): Promise<void>;
|
package/dist/src/search/db.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { connect } from '@tursodatabase/database';
|
|
2
|
-
import { existsSync, mkdirSync
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { join } from 'node:path';
|
|
2
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { acquireSearchAccess } from './lock.js';
|
|
6
5
|
export const INDEX_FILE = 'search.db';
|
|
7
6
|
export const CREATE_PASSAGE_FTS = "CREATE INDEX IF NOT EXISTS chunks_fts ON lexical_chunks USING fts(body,heading,path) WITH (tokenizer='whitespace',weights='body=1.0,heading=2.0,path=0.5')";
|
|
8
7
|
export function hasIndex(cacheDir) {
|
|
@@ -11,26 +10,31 @@ export function hasIndex(cacheDir) {
|
|
|
11
10
|
/** Small SQL adapter; callers never depend on a libSQL connection. */
|
|
12
11
|
export class SearchDb {
|
|
13
12
|
path;
|
|
14
|
-
|
|
13
|
+
published;
|
|
15
14
|
connection;
|
|
16
|
-
|
|
15
|
+
release;
|
|
16
|
+
constructor(path, published = false) {
|
|
17
17
|
this.path = path;
|
|
18
|
-
this.
|
|
18
|
+
this.published = published;
|
|
19
19
|
}
|
|
20
|
-
snapshotDir;
|
|
21
20
|
get() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
return (this.connection ??= this.connect());
|
|
22
|
+
}
|
|
23
|
+
async connect() {
|
|
24
|
+
// Turso 0.7.2 FTS needs writable connections and exclusive process access.
|
|
25
|
+
if (this.published)
|
|
26
|
+
this.release = await acquireSearchAccess(dirname(this.path), 'exclusive');
|
|
27
|
+
try {
|
|
28
|
+
return await connect(this.path, {
|
|
29
|
+
experimental: ['index_method'],
|
|
30
|
+
timeout: 10000,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
await this.release?.();
|
|
35
|
+
this.release = undefined;
|
|
36
|
+
throw error;
|
|
29
37
|
}
|
|
30
|
-
return (this.connection = connect(path, {
|
|
31
|
-
experimental: ['index_method'],
|
|
32
|
-
timeout: 10000,
|
|
33
|
-
}));
|
|
34
38
|
}
|
|
35
39
|
async execute(statement) {
|
|
36
40
|
const { sql, args = [] } = typeof statement === 'string' ? { sql: statement } : statement;
|
|
@@ -47,39 +51,31 @@ export class SearchDb {
|
|
|
47
51
|
try {
|
|
48
52
|
if (this.connection) {
|
|
49
53
|
const db = await this.connection;
|
|
50
|
-
await db.close();
|
|
51
|
-
this.connection = undefined;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
finally {
|
|
55
|
-
if (this.snapshotDir) {
|
|
56
54
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
maxRetries: 3,
|
|
61
|
-
retryDelay: 20,
|
|
62
|
-
});
|
|
55
|
+
// Leave a self-contained file for incremental copies and publication.
|
|
56
|
+
if (this.published)
|
|
57
|
+
await this.checkpoint();
|
|
63
58
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (process.platform !== 'win32' ||
|
|
67
|
-
!['EBUSY', 'EPERM', 'ENOTEMPTY'].includes(code ?? ''))
|
|
68
|
-
throw error;
|
|
69
|
-
// Native handles can outlive close on Windows; this is only an owned temp copy.
|
|
59
|
+
finally {
|
|
60
|
+
await db.close();
|
|
70
61
|
}
|
|
71
|
-
this.snapshotDir = undefined;
|
|
72
62
|
}
|
|
73
63
|
}
|
|
64
|
+
finally {
|
|
65
|
+
this.connection = undefined;
|
|
66
|
+
const release = this.release;
|
|
67
|
+
this.release = undefined;
|
|
68
|
+
await release?.();
|
|
69
|
+
}
|
|
74
70
|
}
|
|
75
71
|
async checkpoint() {
|
|
76
72
|
await this.execute('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
77
73
|
}
|
|
78
74
|
}
|
|
79
|
-
export function openDb(latDir, requestedCacheDir,
|
|
75
|
+
export function openDb(latDir, requestedCacheDir, published = false) {
|
|
80
76
|
const cacheDir = requestedCacheDir ?? join(latDir, '.cache');
|
|
81
77
|
mkdirSync(cacheDir, { recursive: true });
|
|
82
|
-
return new SearchDb(join(cacheDir, INDEX_FILE),
|
|
78
|
+
return new SearchDb(join(cacheDir, INDEX_FILE), published);
|
|
83
79
|
}
|
|
84
80
|
export async function ensureMeta(db) {
|
|
85
81
|
await db.execute('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createEmbedder, EmbeddingAuthError, } from '@lat.md/embed';
|
|
2
2
|
import minilm from '@lat.md/embed-minilm-fp16';
|
|
3
|
-
import { getLlmKey, getRepoEmbedding } from '
|
|
3
|
+
import { getLlmKey, getRepoEmbedding } from '@plebeiantech/lat.md-core/config';
|
|
4
4
|
export { EmbeddingAuthError };
|
|
5
5
|
const defaultCreateSearchEngine = (key) => key ? createEmbedder({ key }) : createEmbedder({ model: minilm });
|
|
6
6
|
/** `meta.embedding_model` value for an embedder, e.g. `local:minilm-l6-v2:384`. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SearchDb } from './db.js';
|
|
2
|
-
import { type MarkdownProjectAnalysis } from '
|
|
2
|
+
import { type MarkdownProjectAnalysis } from '@plebeiantech/lat.md-core/project-analysis';
|
|
3
3
|
import type { Embedder } from './embedder.js';
|
|
4
4
|
export declare function projectFingerprint(project: MarkdownProjectAnalysis): string;
|
|
5
5
|
export type IndexStats = {
|
package/dist/src/search/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PARSER_CACHE_VERSION } from '
|
|
1
|
+
import { PARSER_CACHE_VERSION } from '@plebeiantech/lat.md-core/parser-cache';
|
|
2
2
|
import { synchronizeLexical } from './lexical.js';
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
4
|
import { CREATE_PASSAGE_FTS } from './db.js';
|
|
5
|
-
import { analyzeMarkdownProject, } from '
|
|
5
|
+
import { analyzeMarkdownProject, } from '@plebeiantech/lat.md-core/project-analysis';
|
|
6
6
|
import { chunkFile, digest, embeddingFingerprint } from './chunks.js';
|
|
7
7
|
export function projectFingerprint(project) {
|
|
8
8
|
return digest(`parser:${PARSER_CACHE_VERSION}\n` +
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
export type LockMode = 'shared' | 'exclusive';
|
|
2
2
|
export declare function acquireSearchLock(cacheDir: string, timeoutMs?: number): Promise<() => Promise<void>>;
|
|
3
|
+
/** A turnstile prevents new readers bypassing a writer waiting for readers to drain. */
|
|
4
|
+
export declare function acquireSearchAccess(cacheDir: string, mode: LockMode, timeoutMs?: number): Promise<() => Promise<void>>;
|
|
5
|
+
export declare function withSearchAccess<T>(cacheDir: string, mode: LockMode, work: () => Promise<T>): Promise<T>;
|
package/dist/src/search/lock.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { open } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { setTimeout } from 'node:timers/promises';
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/** Lock files stay in place so every process locks the same inode. */
|
|
5
|
+
async function acquireFileLock(path, mode, deadline, message) {
|
|
6
6
|
const { tryLock } = await import('fs-native-extensions');
|
|
7
|
-
const handle = await open(
|
|
8
|
-
const deadline = performance.now() + timeoutMs;
|
|
7
|
+
const handle = await open(path, 'a+');
|
|
9
8
|
try {
|
|
10
|
-
while (!tryLock(handle.fd)) {
|
|
9
|
+
while (!tryLock(handle.fd, { shared: mode === 'shared' })) {
|
|
11
10
|
const remaining = deadline - performance.now();
|
|
12
11
|
if (remaining <= 0)
|
|
13
|
-
throw new Error(
|
|
14
|
-
await setTimeout(Math.min(
|
|
12
|
+
throw new Error(message);
|
|
13
|
+
await setTimeout(Math.min(10, remaining));
|
|
15
14
|
}
|
|
16
|
-
// Closing releases the kernel lock, including on abnormal process exit.
|
|
17
15
|
return () => handle.close();
|
|
18
16
|
}
|
|
19
17
|
catch (error) {
|
|
@@ -21,3 +19,27 @@ export async function acquireSearchLock(cacheDir, timeoutMs = 120_000) {
|
|
|
21
19
|
throw error;
|
|
22
20
|
}
|
|
23
21
|
}
|
|
22
|
+
export function acquireSearchLock(cacheDir, timeoutMs = 120_000) {
|
|
23
|
+
return acquireFileLock(join(cacheDir, 'search-write.lock'), 'exclusive', performance.now() + timeoutMs, 'Search index writer is busy; retry shortly.');
|
|
24
|
+
}
|
|
25
|
+
/** A turnstile prevents new readers bypassing a writer waiting for readers to drain. */
|
|
26
|
+
export async function acquireSearchAccess(cacheDir, mode, timeoutMs = 120_000) {
|
|
27
|
+
const deadline = performance.now() + timeoutMs;
|
|
28
|
+
const message = 'Search database is busy; retry shortly.';
|
|
29
|
+
const releaseGate = await acquireFileLock(join(cacheDir, 'search-access-gate.lock'), 'exclusive', deadline, message);
|
|
30
|
+
try {
|
|
31
|
+
return await acquireFileLock(join(cacheDir, 'search-access.lock'), mode, deadline, message);
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await releaseGate();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function withSearchAccess(cacheDir, mode, work) {
|
|
38
|
+
const release = await acquireSearchAccess(cacheDir, mode);
|
|
39
|
+
try {
|
|
40
|
+
return await work();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await release();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Section, SectionMatch } from '
|
|
1
|
+
import type { Section, SectionMatch } from '@plebeiantech/lat.md-core/lattice-model';
|
|
2
|
+
import { type SearchDb } from './db.js';
|
|
2
3
|
import { type CreateSearchEngine } from './embedder.js';
|
|
3
4
|
import { type SearchResult } from './search.js';
|
|
4
5
|
export type IndexedSearchResult = {
|
|
@@ -11,7 +12,7 @@ export type IndexedSearchSession = {
|
|
|
11
12
|
};
|
|
12
13
|
/** Resolve scored index rows against one already-analyzed project snapshot. */
|
|
13
14
|
export declare function resolveSearchMatches(results: readonly SearchResult[], sectionById: ReadonlyMap<string, Section>): SectionMatch[];
|
|
14
|
-
/**
|
|
15
|
+
/** Reuse an embedder with scoped database access for a sequence of index queries. */
|
|
15
16
|
export declare function openIndexedSearchSession(latDir: string, options?: {
|
|
16
17
|
cacheDir?: string;
|
|
17
18
|
createSearchEngine?: CreateSearchEngine;
|
|
@@ -21,3 +22,5 @@ export declare function searchIndexedSections(latDir: string, query: string, lim
|
|
|
21
22
|
cacheDir?: string;
|
|
22
23
|
minSimilarity?: number;
|
|
23
24
|
}): Promise<IndexedSearchResult>;
|
|
25
|
+
export declare function indexMetadata(db: SearchDb): Promise<Map<string, string>>;
|
|
26
|
+
export declare function sameIndexMetadata(a: Map<string, string>, b: Map<string, string>): boolean;
|
package/dist/src/search/query.js
CHANGED
|
@@ -3,9 +3,9 @@ import { ReindexRequiredError } from './embedder.js';
|
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { hasIndex } from './db.js';
|
|
6
|
-
import { closeDb,
|
|
6
|
+
import { closeDb, openDb } from './db.js';
|
|
7
7
|
import { embedderForIndex } from './embedder.js';
|
|
8
|
-
import { searchSections } from './search.js';
|
|
8
|
+
import { searchSections, prepareSearchQuery, } from './search.js';
|
|
9
9
|
/** Resolve scored index rows against one already-analyzed project snapshot. */
|
|
10
10
|
export function resolveSearchMatches(results, sectionById) {
|
|
11
11
|
return results.flatMap((result) => {
|
|
@@ -27,48 +27,56 @@ export function resolveSearchMatches(results, sectionById) {
|
|
|
27
27
|
: [];
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
30
|
+
/** Reuse an embedder with scoped database access for a sequence of index queries. */
|
|
31
31
|
export async function openIndexedSearchSession(latDir, options = {}) {
|
|
32
32
|
const cacheDir = options.cacheDir ?? join(latDir, '.cache');
|
|
33
33
|
if (!existsSync(cacheDir) || !hasIndex(cacheDir))
|
|
34
34
|
return { search: async () => [], close: async () => { } };
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
const readMetadata = async () => {
|
|
36
|
+
const db = openDb(latDir, options.cacheDir, true);
|
|
37
|
+
try {
|
|
38
|
+
return await indexMetadata(db);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
await closeDb(db);
|
|
42
|
+
}
|
|
42
43
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
const initial = await readMetadata();
|
|
45
|
+
const stored = initial.get('embedding_model') ?? null;
|
|
46
|
+
if (stored && initial.get('lexical_version') !== LEXICAL_VERSION)
|
|
47
|
+
throw new ReindexRequiredError('Search lexical index changed; run lat search to update it.');
|
|
48
|
+
const embedder = stored
|
|
49
|
+
? await embedderForIndex(stored, latDir, options.createSearchEngine)
|
|
50
|
+
: null;
|
|
51
|
+
let closed = false;
|
|
52
|
+
const active = new Set();
|
|
53
|
+
return {
|
|
54
|
+
search(query, limit, minSimilarity) {
|
|
55
|
+
if (closed)
|
|
56
|
+
return Promise.reject(new Error('Search session is closed'));
|
|
57
|
+
const operation = (async () => {
|
|
58
|
+
if (!embedder)
|
|
50
59
|
return [];
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
60
|
+
const vector = await prepareSearchQuery(query, embedder, limit, minSimilarity);
|
|
61
|
+
const db = openDb(latDir, options.cacheDir, true);
|
|
62
|
+
try {
|
|
63
|
+
if (!sameIndexMetadata(initial, await indexMetadata(db)))
|
|
64
|
+
throw new ReindexRequiredError('Search index changed; reopen the search session to refresh its documents and model.');
|
|
65
|
+
return await searchSections(db, query, embedder, limit, minSimilarity, vector);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await closeDb(db);
|
|
69
|
+
}
|
|
70
|
+
})();
|
|
71
|
+
active.add(operation);
|
|
72
|
+
void operation.finally(() => active.delete(operation)).catch(() => { });
|
|
73
|
+
return operation;
|
|
74
|
+
},
|
|
75
|
+
async close() {
|
|
76
|
+
closed = true;
|
|
77
|
+
await Promise.allSettled([...active]);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
72
80
|
}
|
|
73
81
|
/** Query a finished index and resolve its ids from precomputed section data. */
|
|
74
82
|
export async function searchIndexedSections(latDir, query, limit, sectionById, options = {}) {
|
|
@@ -84,3 +92,12 @@ export async function searchIndexedSections(latDir, query, limit, sectionById, o
|
|
|
84
92
|
await session.close();
|
|
85
93
|
}
|
|
86
94
|
}
|
|
95
|
+
export async function indexMetadata(db) {
|
|
96
|
+
return new Map((await db.execute('SELECT key,value FROM meta')).rows.map((row) => [
|
|
97
|
+
row.key,
|
|
98
|
+
row.value,
|
|
99
|
+
]));
|
|
100
|
+
}
|
|
101
|
+
export function sameIndexMetadata(a, b) {
|
|
102
|
+
return (a.size === b.size && [...a].every(([key, value]) => b.get(key) === value));
|
|
103
|
+
}
|
|
@@ -14,4 +14,6 @@ export declare function collapse(candidates: Candidate[]): Map<string, Candidate
|
|
|
14
14
|
rank: number;
|
|
15
15
|
}>;
|
|
16
16
|
export declare function literalFtsQuery(query: string): string;
|
|
17
|
-
export declare function searchSections(db: SearchDb, query: string, embedder: Embedder, limit?: number, minSimilarity?: number): Promise<SearchResult[]>;
|
|
17
|
+
export declare function searchSections(db: SearchDb, query: string, embedder: Embedder, limit?: number, minSimilarity?: number, preparedVector?: number[]): Promise<SearchResult[]>;
|
|
18
|
+
/** Prepare embeddings before acquiring a database access lock. */
|
|
19
|
+
export declare function prepareSearchQuery(query: string, embedder: Embedder, limit?: number, minSimilarity?: number): Promise<number[]>;
|
|
@@ -24,21 +24,12 @@ export function literalFtsQuery(query) {
|
|
|
24
24
|
.map((t) => `"${t.replaceAll('"', '\\"')}"`)
|
|
25
25
|
.join(' OR ');
|
|
26
26
|
}
|
|
27
|
-
export async function searchSections(db, query, embedder, limit = DEFAULT_SEARCH_LIMIT, minSimilarity = DEFAULT_MIN_SIMILARITY) {
|
|
27
|
+
export async function searchSections(db, query, embedder, limit = DEFAULT_SEARCH_LIMIT, minSimilarity = DEFAULT_MIN_SIMILARITY, preparedVector) {
|
|
28
28
|
query = query.trim();
|
|
29
29
|
if (!query)
|
|
30
30
|
return [];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!Number.isFinite(minSimilarity) || minSimilarity < 0 || minSimilarity > 1)
|
|
34
|
-
throw new Error('min-similarity must be a number from 0 to 1');
|
|
35
|
-
if (embedder.countTokens(query) > embedder.maxInputTokens)
|
|
36
|
-
throw new Error('Search query exceeds the embedding model token limit; shorten the query.');
|
|
37
|
-
const [vector] = await embedder.embed([query]);
|
|
38
|
-
if (!vector ||
|
|
39
|
-
vector.length !== embedder.dimensions ||
|
|
40
|
-
vector.some((n) => !Number.isFinite(n)))
|
|
41
|
-
throw new Error('Embedding backend returned an invalid query vector');
|
|
31
|
+
const vector = preparedVector ??
|
|
32
|
+
(await prepareSearchQuery(query, embedder, limit, minSimilarity));
|
|
42
33
|
const vectorJson = JSON.stringify(vector), fts = literalFtsQuery(query);
|
|
43
34
|
const target = Math.max(50, limit), cap = 10 * target;
|
|
44
35
|
const owners = new Map((await db.execute('SELECT id,section_id FROM chunks')).rows.map((r) => [
|
|
@@ -155,3 +146,21 @@ export async function searchSections(db, query, embedder, limit = DEFAULT_SEARCH
|
|
|
155
146
|
}
|
|
156
147
|
return results;
|
|
157
148
|
}
|
|
149
|
+
/** Prepare embeddings before acquiring a database access lock. */
|
|
150
|
+
export async function prepareSearchQuery(query, embedder, limit = DEFAULT_SEARCH_LIMIT, minSimilarity = DEFAULT_MIN_SIMILARITY) {
|
|
151
|
+
query = query.trim();
|
|
152
|
+
if (!query)
|
|
153
|
+
return [];
|
|
154
|
+
if (!Number.isInteger(limit) || limit < 1)
|
|
155
|
+
throw new Error('limit must be a positive integer');
|
|
156
|
+
if (!Number.isFinite(minSimilarity) || minSimilarity < 0 || minSimilarity > 1)
|
|
157
|
+
throw new Error('min-similarity must be a number from 0 to 1');
|
|
158
|
+
if (embedder.countTokens(query) > embedder.maxInputTokens)
|
|
159
|
+
throw new Error('Search query exceeds the embedding model token limit; shorten the query.');
|
|
160
|
+
const [vector] = await embedder.embed([query]);
|
|
161
|
+
if (!vector ||
|
|
162
|
+
vector.length !== embedder.dimensions ||
|
|
163
|
+
vector.some((n) => !Number.isFinite(n)))
|
|
164
|
+
throw new Error('Embedding backend returned an invalid query vector');
|
|
165
|
+
return vector;
|
|
166
|
+
}
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
end: number;
|
|
4
|
-
startLine: number;
|
|
5
|
-
endLine: number;
|
|
6
|
-
};
|
|
7
|
-
export type SearchEvidence = {
|
|
8
|
-
chunkId: string;
|
|
9
|
-
text: string;
|
|
10
|
-
spans: SourceSpan[];
|
|
11
|
-
channel: 'lexical' | 'semantic';
|
|
12
|
-
};
|
|
13
|
-
export type SearchDiagnostics = {
|
|
14
|
-
lexicalCapped: boolean;
|
|
15
|
-
semanticCapped: boolean;
|
|
16
|
-
lexicalCandidates: number;
|
|
17
|
-
semanticCandidates: number;
|
|
18
|
-
};
|
|
1
|
+
import type { SearchEvidence, SearchDiagnostics } from '@plebeiantech/lat.md-core/search-metadata';
|
|
2
|
+
export type { SourceSpan, SearchEvidence, SearchDiagnostics, } from '@plebeiantech/lat.md-core/search-metadata';
|
|
19
3
|
export type SearchResult = {
|
|
20
4
|
id: string;
|
|
21
5
|
file: string;
|