@ohos-cpf/3rdloop 0.0.8 → 0.0.9

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 (219) hide show
  1. package/README.md +25 -0
  2. package/lib/cli.js +22 -5
  3. package/lib/doctor.js +111 -10
  4. package/lib/mcp.js +498 -0
  5. package/package.json +6 -2
  6. package/vendor/Archive/data/.index/meta.json +7 -0
  7. package/vendor/Archive/data/.index/search.db +0 -0
  8. package/vendor/Archive/data/docs/experiences/arkts-check-tool-pitfalls-baseline-comparison.md +13 -0
  9. package/vendor/Archive/data/docs/experiences/arkts-deep-path-import-recognition.md +31 -0
  10. package/vendor/Archive/data/docs/experiences/arkts-demo-coverage-entry-file-case-mismatch.md +25 -0
  11. package/vendor/Archive/data/docs/experiences/arkts-demo-coverage-import-trailing-slash.md +20 -0
  12. package/vendor/Archive/data/docs/experiences/arkts-library-source-files-may-be-ts.md +18 -0
  13. package/vendor/Archive/data/docs/experiences/arkts-typedarray-buffer-interface-cast-compile-fixes.md +36 -0
  14. package/vendor/Archive/data/docs/experiences/arkts-xts-compile-error-clustering-fix.md +24 -0
  15. package/vendor/Archive/data/docs/experiences/arkts-xts-doc-expected-value-runtime-probe.md +35 -0
  16. package/vendor/Archive/data/docs/experiences/arkts-xts-fixture-rawfile-testability-injection.md +33 -0
  17. package/vendor/Archive/data/docs/experiences/code-linter-chinese-comment-false-positive.md +16 -0
  18. package/vendor/Archive/data/docs/experiences/esobject-new-expression-variable-tracking.md +33 -0
  19. package/vendor/Archive/data/docs/experiences/filesystem-case-sensitivity-module-resolution.md +31 -0
  20. package/vendor/Archive/data/docs/experiences/gitcode-clone-redirect-warning-normal.md +13 -0
  21. package/vendor/Archive/data/docs/experiences/kn_msgvq1w585ec8319.md +77 -0
  22. package/vendor/Archive/data/docs/experiences/kn_msovnm6rd46de72c.md +98 -0
  23. package/vendor/Archive/data/docs/experiences/kn_mspt8vntfe708576.md +87 -0
  24. package/vendor/Archive/data/docs/experiences/kn_mspu1ei77adbbb48.md +133 -0
  25. package/vendor/Archive/data/docs/experiences/kn_mspubgak8d30bfce.md +128 -0
  26. package/vendor/Archive/data/docs/experiences/kn_mspug2de3cb1f7c3.md +115 -0
  27. package/vendor/Archive/data/docs/experiences/kn_skill_opt_001.md +38 -0
  28. package/vendor/Archive/data/docs/experiences/loop-artifacts-verification-evidence-triangle.md +19 -0
  29. package/vendor/Archive/data/docs/experiences/macos-bash-replace-build-ps1.md +19 -0
  30. package/vendor/Archive/data/docs/experiences/macos-bsd-diff-q-illegal-option.md +15 -0
  31. package/vendor/Archive/data/docs/experiences/migrate-cjs-compatible-api-version-map-limit.md +17 -0
  32. package/vendor/Archive/data/docs/experiences/mp3agic-arkts-library-behavior-quirks.md +21 -0
  33. package/vendor/Archive/data/docs/experiences/ohos-face-detection-scan-api-mapping.md +15 -0
  34. package/vendor/Archive/data/docs/experiences/pr-metadata-and-diff-integrity-verification.md +12 -0
  35. package/vendor/Archive/data/docs/experiences/rn-coverage-index-signature-pseudo-member-false-negative.md +32 -0
  36. package/vendor/Archive/data/docs/experiences/rn-memory-check-severity-calibration.md +21 -0
  37. package/vendor/Archive/data/docs/experiences/rn-platform-specific-api-exclusion-evidence.md +24 -0
  38. package/vendor/Archive/data/docs/experiences/rn-upstream-version-diff-pitfalls.md +17 -0
  39. package/vendor/Archive/data/docs/experiences/rn-version-gap-risk-rating-rules.md +13 -0
  40. package/vendor/Archive/data/docs/experiences/rn-view-lib-native-contract-diff-fabric-spec.md +16 -0
  41. package/vendor/Archive/data/docs/experiences/rnoh-adapter-package-version-field-semantics.md +18 -0
  42. package/vendor/Archive/data/docs/experiences/rnoh-arkts-sqlite-rows-array-format.md +18 -0
  43. package/vendor/Archive/data/docs/experiences/rnoh-dependency-residue-scan-strategy.md +18 -0
  44. package/vendor/Archive/data/docs/experiences/rnoh-example-local-lib-dep-use-tgz.md +53 -0
  45. package/vendor/Archive/data/docs/experiences/rnoh-example-prereserve-rntesterlist.md +17 -0
  46. package/vendor/Archive/data/docs/experiences/rnoh-example-template-compatiblesdkversion-upgrade.md +29 -0
  47. package/vendor/Archive/data/docs/experiences/rnoh-example-turbomodule-shell-adaptation-checklist.md +41 -0
  48. package/vendor/Archive/data/docs/experiences/rnoh-mcp-build-project-tool-risk.md +18 -0
  49. package/vendor/Archive/data/docs/experiences/rnoh-npm-devdependencies-version-precheck.md +29 -0
  50. package/vendor/Archive/data/docs/experiences/rnoh-repo-baseline-version-verification.md +13 -0
  51. package/vendor/Archive/data/docs/experiences/rnoh-repo-upstream-directory-mapping.md +18 -0
  52. package/vendor/Archive/data/docs/experiences/rnoh-stateless-library-zero-leak-verification.md +31 -0
  53. package/vendor/Archive/data/docs/experiences/rnoh-template-npmrc-credential-removal.md +15 -0
  54. package/vendor/Archive/data/docs/experiences/rnoh-turbomodule-codegen-wiring-verification.md +19 -0
  55. package/vendor/Archive/data/docs/experiences/rnoh-turbomodule-cpp-methodmap-compatibility-check.md +20 -0
  56. package/vendor/Archive/data/docs/experiences/scenario-doc-vs-source-signature-diff-four-patterns.md +24 -0
  57. package/vendor/Archive/data/docs/experiences/static-analysis-grep-cross-validation.md +29 -0
  58. package/vendor/Archive/demo.js +489 -0
  59. package/vendor/Archive/index.js +43 -0
  60. package/vendor/Archive/package.json +16 -0
  61. package/vendor/Archive/src/constants.js +83 -0
  62. package/vendor/Archive/src/data/stopwords.txt +110 -0
  63. package/vendor/Archive/src/data/synonyms.json +25 -0
  64. package/vendor/Archive/src/data/terms.txt +59 -0
  65. package/vendor/Archive/src/db/sqlite-backend.js +312 -0
  66. package/vendor/Archive/src/db/sqlite-schema.js +64 -0
  67. package/vendor/Archive/src/engine.js +501 -0
  68. package/vendor/Archive/src/index/index-manager.js +203 -0
  69. package/vendor/Archive/src/index/index-state.js +94 -0
  70. package/vendor/Archive/src/index/search-text-builder.js +106 -0
  71. package/vendor/Archive/src/index.js +22 -0
  72. package/vendor/Archive/src/parser/api-identifiers.js +121 -0
  73. package/vendor/Archive/src/parser/api-suffixes.js +48 -0
  74. package/vendor/Archive/src/parser/heading-normalize.js +40 -0
  75. package/vendor/Archive/src/parser/markdown-splitter.js +215 -0
  76. package/vendor/Archive/src/search/catalog-routing.js +87 -0
  77. package/vendor/Archive/src/search/fts-search.js +289 -0
  78. package/vendor/Archive/src/search/query-normalizer.js +159 -0
  79. package/vendor/Archive/src/search/query-rewriter.js +72 -0
  80. package/vendor/Archive/src/search/search-engine.js +72 -0
  81. package/vendor/Archive/src/search/snippet.js +64 -0
  82. package/vendor/Archive/src/store/dir-store.js +103 -0
  83. package/vendor/Archive/src/store/doc-store.js +36 -0
  84. package/vendor/Archive/src/tokenizer/lexicon.js +151 -0
  85. package/vendor/Archive/src/tokenizer/tokenizer.js +167 -0
  86. package/vendor/Archive/src/types.js +86 -0
  87. package/vendor/Archive/src/utils/hash.js +16 -0
  88. package/vendor/Archive/src/utils/lock.js +73 -0
  89. package/vendor/Archive/src/utils/path-safety.js +96 -0
  90. package/vendor/Archive/src/utils/paths.js +39 -0
  91. package/vendor/BadCase/rn/INDEX.md +127 -0
  92. package/vendor/BadCase/rn/cases/KI-001-webview/350/247/206/351/242/221/346/227/240/346/263/225/346/250/252/345/261/217.md +52 -0
  93. package/vendor/BadCase/rn/cases/KI-002-fastImage/347/203/255/346/233/264/346/226/260/345/233/276/347/211/207/345/244/261/350/264/245.md +37 -0
  94. package/vendor/BadCase/rn/cases/KI-003-wechat/347/274/251/347/225/245/345/233/276/346/234/254/345/234/260/345/233/276/347/211/207.md +62 -0
  95. package/vendor/BadCase/rn/cases/KI-004-wechat/345/233/236/350/260/203/346/227/266/345/272/217.md +47 -0
  96. package/vendor/BadCase/rn/cases/KI-005-geolocation/345/257/274/345/205/245/350/267/257/345/276/204/345/220/216/347/274/200.md +31 -0
  97. package/vendor/BadCase/rn/cases/KI-006-SmartRefreshLayout/346/231/272/350/203/275/346/214/207/351/222/210/345/264/251/346/272/203.md +55 -0
  98. package/vendor/BadCase/rn/cases/KI-007-orientation-locker/344/274/240/346/204/237/345/231/250/345/210/235/345/247/213/345/214/226.md +58 -0
  99. package/vendor/BadCase/rn/cases/KI-008-orientation-locker/344/274/240/346/204/237/345/231/250/345/274/225/347/224/250/350/256/241/346/225/260.md +47 -0
  100. package/vendor/BadCase/rn/cases/KI-009-pager-view/345/265/214/345/245/227/346/273/221/345/212/250.md +38 -0
  101. package/vendor/BadCase/rn/cases/KI-010-screens/346/211/213/345/212/277/345/206/262/347/252/201.md +48 -0
  102. package/vendor/BadCase/rn/cases/KI-011-spring-scrollview/346/211/213/345/212/277/351/207/215/345/244/215/347/273/221/345/256/232.md +49 -0
  103. package/vendor/BadCase/rn/cases/KI-012-qr-camera/346/250/252/345/261/217/351/200/202/351/205/215.md +59 -0
  104. package/vendor/BadCase/rn/cases/KI-013-webview/346/267/261/350/211/262/346/250/241/345/274/217/350/203/214/346/231/257/350/211/262.md +37 -0
  105. package/vendor/BadCase/rn/cases/KI-014-amap-geolocation/345/233/236/350/260/203/347/274/272/345/244/261.md +42 -0
  106. package/vendor/BadCase/rn/cases/KI-015-reanimated/350/277/220/350/241/214/346/227/266/346/236/220/346/236/204/345/264/251/346/272/203.md +54 -0
  107. package/vendor/BadCase/rn/cases/KI-016-fs/346/226/207/344/273/266/350/257/273/345/217/226/347/274/226/347/240/201.md +58 -0
  108. package/vendor/BadCase/rn/cases/KI-017-blob-util/345/217/202/346/225/260/347/274/226/347/240/201/351/224/231/350/257/257.md +39 -0
  109. package/vendor/BadCase/rn/cases/KI-018-modal/345/274/271/347/252/227/351/227/252/347/203/201.md +53 -0
  110. package/vendor/BadCase/rn/cases/KI-019-code-push/351/207/215/345/220/257/351/200/273/350/276/221/345/244/261/346/225/210.md +79 -0
  111. package/vendor/BadCase/rn/cases/KI-020-netinfo/346/225/217/346/204/237/344/277/241/346/201/257/346/263/204/351/234/262.md +60 -0
  112. package/vendor/BadCase/rn/cases/KI-021-video/346/234/254/345/234/260/350/247/206/351/242/221/346/222/255/346/224/276/345/274/202/345/270/270.md +79 -0
  113. package/vendor/BadCase/rn/cases/KI-022-audio-recorder-player/346/236/232/344/270/276/347/274/272/345/244/261.md +38 -0
  114. package/vendor/BadCase/rn/cases/KI-023-svg/346/270/220/345/217/230/347/273/247/346/211/277/345/244/261/346/225/210.md +62 -0
  115. package/vendor/BadCase/rn/generalization-log.md +55 -0
  116. package/vendor/BadCase/rn/pr-info/rntpc_react-native-SmartRefreshLayout-32.json +328 -0
  117. package/vendor/BadCase/rn/pr-info/rntpc_react-native-SmartRefreshLayout-32.md +382 -0
  118. package/vendor/BadCase/rn/pr-info/rntpc_react-native-amap-geolocation-27.json +1351 -0
  119. package/vendor/BadCase/rn/pr-info/rntpc_react-native-amap-geolocation-27.md +1176 -0
  120. package/vendor/BadCase/rn/pr-info/rntpc_react-native-audio-recorder-player-22.json +538 -0
  121. package/vendor/BadCase/rn/pr-info/rntpc_react-native-audio-recorder-player-22.md +1488 -0
  122. package/vendor/BadCase/rn/pr-info/rntpc_react-native-blob-util-23.json +211 -0
  123. package/vendor/BadCase/rn/pr-info/rntpc_react-native-blob-util-23.md +35 -0
  124. package/vendor/BadCase/rn/pr-info/rntpc_react-native-code-push-17.json +2048 -0
  125. package/vendor/BadCase/rn/pr-info/rntpc_react-native-code-push-17.md +3866 -0
  126. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fast-image-102.json +198 -0
  127. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fast-image-102.md +40 -0
  128. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fs-38.json +751 -0
  129. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fs-38.md +883 -0
  130. package/vendor/BadCase/rn/pr-info/rntpc_react-native-geolocation-24.json +1544 -0
  131. package/vendor/BadCase/rn/pr-info/rntpc_react-native-geolocation-24.md +2598 -0
  132. package/vendor/BadCase/rn/pr-info/rntpc_react-native-modal-20.json +246 -0
  133. package/vendor/BadCase/rn/pr-info/rntpc_react-native-modal-20.md +554 -0
  134. package/vendor/BadCase/rn/pr-info/rntpc_react-native-netinfo-31.json +211 -0
  135. package/vendor/BadCase/rn/pr-info/rntpc_react-native-netinfo-31.md +54 -0
  136. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-45.json +1626 -0
  137. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-45.md +2213 -0
  138. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-51.json +208 -0
  139. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-51.md +77 -0
  140. package/vendor/BadCase/rn/pr-info/rntpc_react-native-pager-view-32.json +377 -0
  141. package/vendor/BadCase/rn/pr-info/rntpc_react-native-pager-view-32.md +174 -0
  142. package/vendor/BadCase/rn/pr-info/rntpc_react-native-qr-decode-image-camera-37.json +232 -0
  143. package/vendor/BadCase/rn/pr-info/rntpc_react-native-qr-decode-image-camera-37.md +194 -0
  144. package/vendor/BadCase/rn/pr-info/rntpc_react-native-reanimated-82.json +198 -0
  145. package/vendor/BadCase/rn/pr-info/rntpc_react-native-reanimated-82.md +67 -0
  146. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-87.json +198 -0
  147. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-87.md +38 -0
  148. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-93.json +208 -0
  149. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-93.md +34 -0
  150. package/vendor/BadCase/rn/pr-info/rntpc_react-native-spring-scrollview-38.json +222 -0
  151. package/vendor/BadCase/rn/pr-info/rntpc_react-native-spring-scrollview-38.md +56 -0
  152. package/vendor/BadCase/rn/pr-info/rntpc_react-native-svg-117.json +463 -0
  153. package/vendor/BadCase/rn/pr-info/rntpc_react-native-svg-117.md +383 -0
  154. package/vendor/BadCase/rn/pr-info/rntpc_react-native-video-66.json +464 -0
  155. package/vendor/BadCase/rn/pr-info/rntpc_react-native-video-66.md +565 -0
  156. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-112.json +198 -0
  157. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-112.md +47 -0
  158. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-121.json +232 -0
  159. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-121.md +422 -0
  160. package/vendor/BadCase/rn/pr-info/rntpc_react-native-wechat-lib-15.json +295 -0
  161. package/vendor/BadCase/rn/pr-info/rntpc_react-native-wechat-lib-15.md +1038 -0
  162. package/vendor/BadCase/rn/rules/R-01-/345/205/250/345/261/217/347/252/227/345/217/243/347/212/266/346/200/201/347/256/241/347/220/206/345/256/214/346/225/264/346/200/247.md +20 -0
  163. package/vendor/BadCase/rn/rules/R-02-/350/265/204/346/272/220URI/345/275/242/346/200/201/345/210/206/346/224/257/350/246/206/347/233/226.md +21 -0
  164. package/vendor/BadCase/rn/rules/R-03-/351/231/204/345/261/236/346/223/215/344/275/234/345/244/261/350/264/245/351/230/273/346/226/255/344/270/273/346/265/201/347/250/213.md +20 -0
  165. package/vendor/BadCase/rn/rules/R-04-/345/274/202/346/255/245/345/233/236/350/260/203/346/263/250/345/206/214/346/227/266/345/272/217.md +20 -0
  166. package/vendor/BadCase/rn/rules/R-05-/346/250/241/345/235/227/345/257/274/345/205/245/350/267/257/345/276/204/350/247/204/350/214/203.md +19 -0
  167. package/vendor/BadCase/rn/rules/R-06-/344/274/240/346/204/237/345/231/250/347/241/254/344/273/266/346/235/203/351/231/220/346/214/211/351/234/200/350/216/267/345/217/226.md +21 -0
  168. package/vendor/BadCase/rn/rules/R-07-/346/231/272/350/203/275/346/214/207/351/222/210/347/224/237/345/221/275/345/221/250/346/234/237/347/256/241/347/220/206.md +20 -0
  169. package/vendor/BadCase/rn/rules/R-08-/346/211/213/345/212/277/344/272/213/344/273/266/346/263/250/345/206/214/344/270/216/345/206/262/347/252/201/345/244/204/347/220/206.md +22 -0
  170. package/vendor/BadCase/rn/rules/R-09-/346/250/252/347/253/226/345/261/217/345/244/232/345/275/242/346/200/201/345/270/203/345/261/200/351/200/202/351/205/215.md +20 -0
  171. package/vendor/BadCase/rn/rules/R-10-/346/267/261/350/211/262/346/250/241/345/274/217/344/270/273/351/242/230/351/242/234/350/211/262/351/200/202/351/205/215.md +19 -0
  172. package/vendor/BadCase/rn/rules/R-11-/346/216/245/345/217/243/345/233/236/350/260/203/345/256/236/347/216/260/345/256/214/346/225/264/346/200/247.md +20 -0
  173. package/vendor/BadCase/rn/rules/R-12-/345/216/237/347/224/237/350/277/220/350/241/214/346/227/266/350/265/204/346/272/220/346/236/220/346/236/204/346/227/266/345/272/217.md +20 -0
  174. package/vendor/BadCase/rn/rules/R-13-/346/226/207/344/273/266/346/225/260/346/215/256/347/274/226/347/240/201/344/270/216/346/240/274/345/274/217/345/205/274/345/256/271.md +21 -0
  175. package/vendor/BadCase/rn/rules/R-14-/345/212/250/347/224/273/345/210/235/345/247/213/345/214/226/346/227/266/345/272/217/344/270/216/347/212/266/346/200/201/345/220/214/346/255/245.md +20 -0
  176. package/vendor/BadCase/rn/rules/R-15-/347/212/266/346/200/201/346/214/201/344/271/205/345/214/226/344/270/216/351/207/215/345/220/257/351/200/273/350/276/221/344/270/200/350/207/264/346/200/247.md +20 -0
  177. package/vendor/BadCase/rn/rules/R-16-/346/225/217/346/204/237/344/277/241/346/201/257/346/227/245/345/277/227/346/263/204/351/234/262.md +19 -0
  178. package/vendor/BadCase/rn/rules/R-17-/345/252/222/344/275/223/350/265/204/346/272/220/347/224/237/345/221/275/345/221/250/346/234/237/344/270/216/351/224/231/350/257/257/345/244/204/347/220/206.md +21 -0
  179. package/vendor/BadCase/rn/rules/R-18-/350/203/275/345/212/233/346/236/232/344/270/276/351/205/215/347/275/256/345/256/214/346/225/264/346/200/247.md +19 -0
  180. package/vendor/BadCase/rn/rules/R-19-/345/233/276/345/275/242/346/270/262/346/237/223/345/261/236/346/200/247/347/273/247/346/211/277/344/270/216/351/207/215/347/275/256.md +19 -0
  181. package/vendor/MCP/README.md +490 -0
  182. package/vendor/MCP/config.json +29 -0
  183. package/vendor/MCP/package.json +23 -0
  184. package/vendor/MCP/scripts/analyze-demo-coverage.cjs +1203 -0
  185. package/vendor/MCP/scripts/analyze-xts-coverage.cjs +1245 -0
  186. package/vendor/MCP/scripts/deveco_docs.js +247 -0
  187. package/vendor/MCP/scripts/extract-interfaces.cjs +1487 -0
  188. package/vendor/MCP/scripts/flutter-analyze-demo-coverage.cjs +666 -0
  189. package/vendor/MCP/scripts/flutter-analyze-xts-coverage.cjs +684 -0
  190. package/vendor/MCP/scripts/flutter-extract-interfaces.cjs +1251 -0
  191. package/vendor/MCP/scripts/flutter-render-spec-doc.cjs +351 -0
  192. package/vendor/MCP/scripts/render-spec-doc.cjs +347 -0
  193. package/vendor/MCP/scripts/rn-analyze-demo-coverage.cjs +645 -0
  194. package/vendor/MCP/scripts/rn-analyze-test-coverage.cjs +614 -0
  195. package/vendor/MCP/scripts/rn-extract-interfaces.cjs +1462 -0
  196. package/vendor/MCP/scripts/rn-render-spec-doc.cjs +327 -0
  197. package/vendor/MCP/scripts/rn-scan-exports.cjs +1336 -0
  198. package/vendor/MCP/src/client/MCPClientPool.js +220 -0
  199. package/vendor/MCP/src/config/ConfigLoader.js +189 -0
  200. package/vendor/MCP/src/executor/ScriptExecutor.js +110 -0
  201. package/vendor/MCP/src/gateway/Gateway.js +451 -0
  202. package/vendor/MCP/src/index.js +87 -0
  203. package/vendor/MCP/src/library/KBToolProvider.js +229 -0
  204. package/vendor/MCP/src/library/logger.js +60 -0
  205. package/vendor/MCP/src/registry/ScriptRegistry.js +238 -0
  206. package/vendor/MCP/src/registry/ToolRegistry.js +111 -0
  207. package/vendor/MCP/src/types.js +115 -0
  208. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +4 -2
  209. package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +3 -3
  210. package/vendor/Server/Skills/knowledge-import/SKILL.md +1 -1
  211. package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +12 -4
  212. package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +5 -4
  213. package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +2 -2
  214. package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +2 -2
  215. package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +1 -2
  216. package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +1 -1
  217. package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +1 -2
  218. package/vendor/Server/package.json +5 -1
  219. package/vendor/VERSION +3 -3
@@ -0,0 +1,501 @@
1
+ /**
2
+ * engine.js — DocSearchEngine 核心门面
3
+ *
4
+ * 统一编排存储层、索引层、搜索层、词典分词层和安全层,
5
+ * 对外提供 search / addDocument / removeDocument / rebuildIndex / getStatus 等接口。
6
+ *
7
+ * 设计原则:
8
+ * - 高内聚:所有文档检索相关逻辑集中于此门面。
9
+ * - 低耦合:各子模块通过构造注入,可独立替换/测试。
10
+ * - 写入串行:通过文件锁保证写入安全;查询无锁。
11
+ */
12
+
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+
16
+ import { DEFAULT_CATALOGS, INDEX_VERSION, DEFAULT_DATA_DIR, GIT_WARN_BYTES } from './constants.js';
17
+ import { resolveRuntimePaths } from './utils/paths.js';
18
+ import PathSafety, { PathSafetyError } from './utils/path-safety.js';
19
+ import { acquireLock } from './utils/lock.js';
20
+ import SqliteBackend from './db/sqlite-backend.js';
21
+ import DirectoryDocStore from './store/dir-store.js';
22
+ import Lexicon from './tokenizer/lexicon.js';
23
+ import Tokenizer from './tokenizer/tokenizer.js';
24
+ import IndexManager from './index/index-manager.js';
25
+ import IndexState from './index/index-state.js';
26
+ import SearchEngine from './search/search-engine.js';
27
+ import QueryRewriter from './search/query-rewriter.js';
28
+
29
+ export default class DocSearchEngine {
30
+ constructor() {
31
+ /** @type {import('./types.js').EngineOptions|null} */
32
+ this.options = null;
33
+ this.paths = null;
34
+ /** @type {import('./store/doc-store.js').default|null} */
35
+ this.store = null;
36
+ this.backend = null;
37
+ this.indexManager = null;
38
+ this.searchEngine = null;
39
+ this.tokenizer = null;
40
+ this.lexicon = null;
41
+ this.indexState = null;
42
+ this.pathSafety = null;
43
+ this.rewriter = new QueryRewriter();
44
+ /** @type {Map<string, import('./types.js').CatalogConfig>} */
45
+ this.catalogMap = new Map();
46
+ /** @type {Map<number, string>} */
47
+ this.idToCatalogMap = new Map();
48
+ this.initialized = false;
49
+ }
50
+
51
+ /**
52
+ * 初始化引擎。
53
+ * @param {import('./types.js').EngineOptions} options
54
+ * @returns {Promise<void>}
55
+ */
56
+ async init(options = {}) {
57
+ this.options = options;
58
+
59
+ // 1. 数据目录(未指定时使用内置默认路径 Archive/data)
60
+ const dataDir = options.dataDir || DEFAULT_DATA_DIR;
61
+ this.pathSafety = new PathSafety(dataDir);
62
+ await fs.promises.mkdir(dataDir, { recursive: true });
63
+ await this.pathSafety.assertDataDirSafe();
64
+
65
+ this.paths = resolveRuntimePaths(dataDir);
66
+ await fs.promises.mkdir(this.paths.docsDir, { recursive: true });
67
+ await fs.promises.mkdir(this.paths.indexDir, { recursive: true });
68
+
69
+ // 2. 目录映射
70
+ this.catalogMap = new Map();
71
+ for (const cat of (options.catalogs || DEFAULT_CATALOGS)) {
72
+ this.catalogMap.set(cat.id, cat);
73
+ }
74
+ this.idToCatalogMap = new Map();
75
+ let i = 0;
76
+ for (const [id] of this.catalogMap) {
77
+ this.idToCatalogMap.set(i, id);
78
+ i++;
79
+ }
80
+
81
+ // 3. 词典(未指定 lexiconDir 时使用内置默认词典)
82
+ const builtinLexiconDir = path.resolve(
83
+ path.dirname(new URL(import.meta.url).pathname),
84
+ 'data'
85
+ );
86
+ const lexiconDir = options.lexiconDir || builtinLexiconDir;
87
+ this.lexicon = new Lexicon(lexiconDir);
88
+ this.tokenizer = new Tokenizer(lexiconDir, this.lexicon);
89
+ await this.tokenizer.init();
90
+
91
+ // 4. DB
92
+ const dbPath = options.dbPath || this.paths.dbPath;
93
+ this.backend = new SqliteBackend();
94
+ await this.backend.init();
95
+ await this.backend.openWriteDb(dbPath);
96
+
97
+ // 5. 存储
98
+ this.store = new DirectoryDocStore(this.paths.docsDir);
99
+
100
+ // 6. 索引 + 搜索
101
+ this.indexManager = new IndexManager(this.backend, this.tokenizer, dbPath);
102
+ this.indexState = new IndexState(dbPath, this.paths.metaPath, this.lexicon);
103
+ this.searchEngine = new SearchEngine(
104
+ this.backend,
105
+ this.tokenizer,
106
+ this.lexicon,
107
+ dbPath,
108
+ this.catalogMap,
109
+ (cat) => this.catalogToId(cat),
110
+ (id) => this.idToCatalog(id),
111
+ this.rewriter
112
+ );
113
+
114
+ this._dbPath = dbPath;
115
+ this.initialized = true;
116
+ }
117
+
118
+ // ─── 查询 ──────────────────────────────────────────────────────
119
+
120
+ /**
121
+ * 搜索文档。
122
+ * @param {import('./types.js').SearchQuery} query
123
+ * @param {import('./types.js').SearchOptions} [options]
124
+ * @returns {Promise<import('./types.js').SearchResult[]>}
125
+ */
126
+ async search(query, options) {
127
+ this.assertReady();
128
+ return this.searchEngine.search(query, options);
129
+ }
130
+
131
+ /**
132
+ * 获取文档全文。
133
+ * @param {string} documentId
134
+ * @returns {Promise<string|null>}
135
+ */
136
+ async getDocument(documentId) {
137
+ this.assertReady();
138
+ this.pathSafety.validateDocumentId(documentId);
139
+ return this.store.read(documentId);
140
+ }
141
+
142
+ /**
143
+ * 获取文档完整记录(含元数据)。
144
+ * @param {string} documentId
145
+ * @returns {Promise<import('./types.js').DocumentRecord|null>}
146
+ */
147
+ async getDocumentRecord(documentId) {
148
+ this.assertReady();
149
+ this.pathSafety.validateDocumentId(documentId);
150
+
151
+ const content = await this.store.read(documentId);
152
+ if (content === null) return null;
153
+
154
+ const db = await this.backend.openReadDb(this._dbPath);
155
+ const stmt = db.prepare(
156
+ 'SELECT document_id, catalog_id, doc_title, metadata, created_at, updated_at FROM documents WHERE document_id = ?'
157
+ );
158
+ stmt.bind([documentId]);
159
+ let row = null;
160
+ if (stmt.step()) row = stmt.get();
161
+ stmt.finalize();
162
+
163
+ if (!row) {
164
+ // 文件存在但无索引记录
165
+ return {
166
+ documentId,
167
+ title: documentId,
168
+ catalog: this.inferCatalogFromId(documentId),
169
+ content,
170
+ segmentCount: 0,
171
+ metadata: {},
172
+ createdAt: 0,
173
+ updatedAt: 0,
174
+ };
175
+ }
176
+
177
+ const segStmt = db.prepare('SELECT COUNT(*) AS cnt FROM segments WHERE doc_id = (SELECT id FROM documents WHERE document_id = ?)');
178
+ segStmt.bind([documentId]);
179
+ segStmt.step();
180
+ const segCount = segStmt.get().cnt || 0;
181
+ segStmt.finalize();
182
+
183
+ return {
184
+ documentId: row.document_id,
185
+ title: row.doc_title,
186
+ catalog: this.idToCatalog(row.catalog_id),
187
+ content,
188
+ segmentCount: segCount,
189
+ metadata: safeParseJson(row.metadata),
190
+ createdAt: row.created_at,
191
+ updatedAt: row.updated_at,
192
+ };
193
+ }
194
+
195
+ // ─── 写入 ──────────────────────────────────────────────────────
196
+
197
+ /**
198
+ * 检查文档是否已存在于索引中。
199
+ * @param {string} documentId
200
+ * @returns {Promise<boolean>}
201
+ */
202
+ async hasDocument(documentId) {
203
+ this.assertReady();
204
+ this.pathSafety.validateDocumentId(documentId);
205
+ const db = await this.backend.openReadDb(this._dbPath);
206
+ const count = db.selectValue(
207
+ 'SELECT COUNT(*) AS cnt FROM documents WHERE document_id = ?',
208
+ documentId
209
+ );
210
+ return count > 0;
211
+ }
212
+
213
+ /**
214
+ * 添加或更新文档。
215
+ * @param {import('./types.js').DocumentInput} doc
216
+ * @returns {Promise<void>}
217
+ */
218
+ async addDocument(doc) {
219
+ this.assertReady();
220
+ this.pathSafety.validateDocumentId(doc.documentId);
221
+ const catalogId = this.catalogToId(doc.catalog);
222
+
223
+ const release = await acquireLock(this.paths.lockPath);
224
+ try {
225
+ await this.store.write(doc.documentId, doc.content);
226
+ await this.indexManager.addDocument(doc, catalogId);
227
+ await this.updateMetaAfterWrite();
228
+ } finally {
229
+ await release();
230
+ }
231
+ }
232
+
233
+ /**
234
+ * 批量添加文档。
235
+ * @param {import('./types.js').DocumentInput[]} docs
236
+ * @returns {Promise<void>}
237
+ */
238
+ async addDocuments(docs) {
239
+ this.assertReady();
240
+ for (const doc of docs) {
241
+ this.pathSafety.validateDocumentId(doc.documentId);
242
+ }
243
+
244
+ const release = await acquireLock(this.paths.lockPath);
245
+ try {
246
+ for (const doc of docs) {
247
+ await this.store.write(doc.documentId, doc.content);
248
+ }
249
+ const docsWithCatalogId = docs.map((doc) => ({
250
+ ...doc,
251
+ catalogId: this.catalogToId(doc.catalog),
252
+ }));
253
+ await this.indexManager.addDocuments(docsWithCatalogId);
254
+ await this.updateMetaAfterWrite();
255
+ } finally {
256
+ await release();
257
+ }
258
+ }
259
+
260
+ /**
261
+ * 删除文档。
262
+ * @param {string} documentId
263
+ * @returns {Promise<void>}
264
+ */
265
+ async removeDocument(documentId) {
266
+ this.assertReady();
267
+ this.pathSafety.validateDocumentId(documentId);
268
+
269
+ const release = await acquireLock(this.paths.lockPath);
270
+ try {
271
+ await this.store.delete(documentId);
272
+ await this.indexManager.deleteDocument(documentId);
273
+ await this.updateMetaAfterWrite();
274
+ } finally {
275
+ await release();
276
+ }
277
+ }
278
+
279
+ // ─── 索引管理 ──────────────────────────────────────────────────
280
+
281
+ /**
282
+ * 全量重建索引。
283
+ * @returns {Promise<void>}
284
+ */
285
+ async rebuildIndex() {
286
+ this.assertReady();
287
+ const release = await acquireLock(this.paths.lockPath);
288
+ try {
289
+ const documentIds = await this.store.list();
290
+ await this.indexManager.clearAll();
291
+
292
+ /** @type {Array<import('./types.js').DocumentInput & {catalogId:number}>} */
293
+ const docs = [];
294
+ for (const id of documentIds) {
295
+ const content = await this.store.read(id);
296
+ if (content) {
297
+ const catalog = this.inferCatalogFromId(id);
298
+ docs.push({
299
+ documentId: id,
300
+ catalog,
301
+ catalogId: this.catalogToId(catalog),
302
+ content,
303
+ });
304
+ }
305
+ }
306
+ await this.indexManager.addDocuments(docs);
307
+
308
+ const stats = await this.indexManager.getStats();
309
+ await this.indexState.markBuilt(stats.segmentCount);
310
+
311
+ // 重建后自动 VACUUM 回收碎片空间
312
+ await this.backend.vacuum();
313
+ } finally {
314
+ await release();
315
+ }
316
+ }
317
+
318
+ /**
319
+ * 压缩数据库(VACUUM),回收删除/更新产生的碎片空间。
320
+ * 建议在批量删除文档后手动调用。
321
+ * @returns {Promise<void>}
322
+ */
323
+ async vacuum() {
324
+ this.assertReady();
325
+ const release = await acquireLock(this.paths.lockPath);
326
+ try {
327
+ await this.backend.vacuum();
328
+ } finally {
329
+ await release();
330
+ }
331
+ }
332
+
333
+ /**
334
+ * 将 WAL 日志合并回主库文件(checkpoint)。
335
+ * 确保 git 提交的 .db 文件包含所有最新写入。
336
+ * @returns {Promise<void>}
337
+ */
338
+ async checkpoint() {
339
+ this.assertReady();
340
+ await this.backend.checkpoint();
341
+ }
342
+
343
+ /**
344
+ * 获取引擎状态。
345
+ * @returns {Promise<import('./types.js').EngineStatus>}
346
+ */
347
+ async getStatus() {
348
+ if (!this.initialized) {
349
+ return {
350
+ ready: false,
351
+ documentCount: 0,
352
+ segmentCount: 0,
353
+ dbSizeBytes: 0,
354
+ indexVersion: INDEX_VERSION,
355
+ lastBuiltAt: null,
356
+ isWriting: false,
357
+ dbSizeWarning: false,
358
+ };
359
+ }
360
+
361
+ const stats = await this.indexManager.getStats();
362
+ let dbSizeBytes = 0;
363
+ try {
364
+ const stat = await fs.promises.stat(this._dbPath);
365
+ dbSizeBytes = stat.size;
366
+ } catch {
367
+ // DB 文件可能尚未创建
368
+ }
369
+
370
+ const meta = await this.indexState.readMeta();
371
+
372
+ return {
373
+ ready: true,
374
+ documentCount: stats.documentCount,
375
+ segmentCount: stats.segmentCount,
376
+ dbSizeBytes,
377
+ indexVersion: INDEX_VERSION,
378
+ lastBuiltAt: meta?.builtAt ?? null,
379
+ isWriting: false,
380
+ dbSizeWarning: dbSizeBytes >= GIT_WARN_BYTES,
381
+ };
382
+ }
383
+
384
+ // ─── 高级接口 ──────────────────────────────────────────────────
385
+
386
+ /**
387
+ * 原始 SQL 查询(高级用户直接查 FTS5)。
388
+ * @param {string} sql
389
+ * @param {...unknown} params
390
+ * @returns {Promise<any[]>}
391
+ */
392
+ async rawQuery(sql, ...params) {
393
+ this.assertReady();
394
+ const db = await this.backend.openReadDb(this._dbPath);
395
+ const stmt = db.prepare(sql);
396
+ if (params.length > 0) stmt.bind(params);
397
+ /** @type {any[]} */
398
+ const rows = [];
399
+ while (stmt.step()) rows.push(stmt.get());
400
+ stmt.finalize();
401
+ return rows;
402
+ }
403
+
404
+ /**
405
+ * 注册自定义同义词组。
406
+ * @param {string[]} words
407
+ */
408
+ addSynonyms(words) {
409
+ this.rewriter.addSynonyms(words);
410
+ }
411
+
412
+ /**
413
+ * 添加自定义词典词条(影响 jieba 分词)。
414
+ * @param {string[]} words
415
+ */
416
+ addTerms(words) {
417
+ this.tokenizer.addTerms(words);
418
+ }
419
+
420
+ /**
421
+ * 关闭引擎,释放资源。
422
+ * @returns {Promise<void>}
423
+ */
424
+ async close() {
425
+ if (this.backend) await this.backend.close();
426
+ this.initialized = false;
427
+ }
428
+
429
+ // ─── 内部辅助 ──────────────────────────────────────────────────
430
+
431
+ /**
432
+ * catalog id → 数字 ID。
433
+ * @param {string} catalog
434
+ * @returns {number}
435
+ * @private
436
+ */
437
+ catalogToId(catalog) {
438
+ let idx = 0;
439
+ for (const [id] of this.catalogMap) {
440
+ if (id === catalog) return idx;
441
+ idx++;
442
+ }
443
+ throw new Error(`Unknown catalog: ${catalog}`);
444
+ }
445
+
446
+ /**
447
+ * 数字 ID → catalog id。
448
+ * @param {number} id
449
+ * @returns {string}
450
+ * @private
451
+ */
452
+ idToCatalog(id) {
453
+ return this.idToCatalogMap.get(id) || 'unknown';
454
+ }
455
+
456
+ /**
457
+ * 从 documentId 推断 catalog。
458
+ * @param {string} documentId
459
+ * @returns {string}
460
+ * @private
461
+ */
462
+ inferCatalogFromId(documentId) {
463
+ const firstSeg = documentId.split('/')[0];
464
+ if (this.catalogMap.has(firstSeg)) return firstSeg;
465
+ return this.catalogMap.keys().next().value;
466
+ }
467
+
468
+ /**
469
+ * 写入后更新元数据。
470
+ * @returns {Promise<void>}
471
+ * @private
472
+ */
473
+ async updateMetaAfterWrite() {
474
+ const stats = await this.indexManager.getStats();
475
+ await this.indexState.markBuilt(stats.segmentCount);
476
+ }
477
+
478
+ /**
479
+ * 断言引擎已初始化。
480
+ * @private
481
+ */
482
+ assertReady() {
483
+ if (!this.initialized) {
484
+ throw new Error('Engine not initialized. Call init() first.');
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * 安全解析 JSON。
491
+ * @param {string} raw
492
+ * @returns {Record<string, string>}
493
+ */
494
+ function safeParseJson(raw) {
495
+ try {
496
+ const obj = JSON.parse(raw);
497
+ return obj && typeof obj === 'object' ? obj : {};
498
+ } catch {
499
+ return {};
500
+ }
501
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * index-manager.js — 索引管理器
3
+ *
4
+ * 职责:
5
+ * 1. 增量添加/删除单个文档(解析 → 拆段 → 写 DB → 失效缓存)。
6
+ * 2. 批量添加(事务写入)。
7
+ * 3. 全量清空(用于 rebuildIndex)。
8
+ *
9
+ * 写入流程使用持久化 DB(openWriteDb),写入后失效只读缓存。
10
+ */
11
+
12
+ import fs from 'node:fs';
13
+ import { splitMarkdown, resolveDocTitle } from '../parser/markdown-splitter.js';
14
+ import { buildSearchText } from './search-text-builder.js';
15
+
16
+ export default class IndexManager {
17
+ /**
18
+ * @param {import('../db/sqlite-backend.js').default} backend
19
+ * @param {import('../tokenizer/tokenizer.js').default} tokenizer
20
+ * @param {string} dbPath
21
+ */
22
+ constructor(backend, tokenizer, dbPath) {
23
+ this.backend = backend;
24
+ this.tokenizer = tokenizer;
25
+ this.dbPath = dbPath;
26
+ }
27
+
28
+ /**
29
+ * 增量添加单个文档。
30
+ * @param {import('../types.js').DocumentInput} doc
31
+ * @param {number} catalogId
32
+ * @returns {Promise<void>}
33
+ */
34
+ async addDocument(doc, catalogId) {
35
+ const db = await this.backend.openWriteDb(this.dbPath);
36
+
37
+ // 先删除旧索引
38
+ await this.deleteDocumentInternal(db, doc.documentId);
39
+
40
+ // 解析 Markdown → segments
41
+ const docTitle = doc.title || resolveDocTitle(doc.content, undefined, doc.documentId);
42
+ const sources = splitMarkdown(doc.content, { documentId: doc.documentId, docTitle });
43
+
44
+ const now = Date.now();
45
+ db.exec('BEGIN');
46
+ try {
47
+ // 写入 documents 表
48
+ const insertDoc = db.prepare(
49
+ 'INSERT INTO documents(document_id, catalog_id, doc_title, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)'
50
+ );
51
+ insertDoc.bind([
52
+ doc.documentId,
53
+ catalogId,
54
+ docTitle,
55
+ JSON.stringify(doc.metadata || {}),
56
+ now,
57
+ now,
58
+ ]);
59
+ insertDoc.step();
60
+ insertDoc.finalize();
61
+
62
+ const docRowId = db.selectValue('SELECT last_insert_rowid()');
63
+
64
+ // 写入 segments 表(触发器自动同步 FTS)
65
+ const insertSeg = db.prepare(
66
+ 'INSERT INTO segments(doc_id, section_title, lead_text, search_text, excerpt_truncated) VALUES (?, ?, ?, ?, ?)'
67
+ );
68
+
69
+ for (const source of sources) {
70
+ const searchText = await buildSearchText(source, this.tokenizer);
71
+ insertSeg.bind([
72
+ docRowId,
73
+ source.sectionTitle,
74
+ source.leadText,
75
+ searchText,
76
+ source.excerptTruncated ? 1 : 0,
77
+ ]);
78
+ insertSeg.step();
79
+ insertSeg.reset();
80
+ }
81
+ insertSeg.finalize();
82
+
83
+ db.exec('COMMIT');
84
+ } catch (error) {
85
+ db.exec('ROLLBACK');
86
+ throw error;
87
+ }
88
+
89
+ this.backend.invalidateReadCache();
90
+ }
91
+
92
+ /**
93
+ * 批量添加文档(外层事务)。
94
+ * @param {Array<import('../types.js').DocumentInput & {catalogId:number}>} docs
95
+ * @returns {Promise<void>}
96
+ */
97
+ async addDocuments(docs) {
98
+ const db = await this.backend.openWriteDb(this.dbPath);
99
+ db.exec('BEGIN');
100
+ try {
101
+ for (const doc of docs) {
102
+ await this.addDocumentInternal(db, doc);
103
+ }
104
+ db.exec('COMMIT');
105
+ } catch (error) {
106
+ db.exec('ROLLBACK');
107
+ throw error;
108
+ }
109
+ this.backend.invalidateReadCache();
110
+ }
111
+
112
+ /**
113
+ * 内部添加(复用已开启的事务)。
114
+ * @param {any} db
115
+ * @param {import('../types.js').DocumentInput & {catalogId:number}} doc
116
+ */
117
+ async addDocumentInternal(db, doc) {
118
+ await this.deleteDocumentInternal(db, doc.documentId);
119
+
120
+ const docTitle = doc.title || resolveDocTitle(doc.content, undefined, doc.documentId);
121
+ const sources = splitMarkdown(doc.content, { documentId: doc.documentId, docTitle });
122
+ const now = Date.now();
123
+
124
+ const insertDoc = db.prepare(
125
+ 'INSERT INTO documents(document_id, catalog_id, doc_title, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)'
126
+ );
127
+ insertDoc.bind([
128
+ doc.documentId,
129
+ doc.catalogId,
130
+ docTitle,
131
+ JSON.stringify(doc.metadata || {}),
132
+ now,
133
+ now,
134
+ ]);
135
+ insertDoc.step();
136
+ insertDoc.finalize();
137
+
138
+ const docRowId = db.selectValue('SELECT last_insert_rowid()');
139
+
140
+ const insertSeg = db.prepare(
141
+ 'INSERT INTO segments(doc_id, section_title, lead_text, search_text, excerpt_truncated) VALUES (?, ?, ?, ?, ?)'
142
+ );
143
+
144
+ for (const source of sources) {
145
+ const searchText = await buildSearchText(source, this.tokenizer);
146
+ insertSeg.bind([
147
+ docRowId,
148
+ source.sectionTitle,
149
+ source.leadText,
150
+ searchText,
151
+ source.excerptTruncated ? 1 : 0,
152
+ ]);
153
+ insertSeg.step();
154
+ insertSeg.reset();
155
+ }
156
+ insertSeg.finalize();
157
+ }
158
+
159
+ /**
160
+ * 删除文档(级联删除 segments + FTS)。
161
+ * @param {string} documentId
162
+ * @returns {Promise<void>}
163
+ */
164
+ async deleteDocument(documentId) {
165
+ const db = await this.backend.openWriteDb(this.dbPath);
166
+ await this.deleteDocumentInternal(db, documentId);
167
+ this.backend.invalidateReadCache();
168
+ }
169
+
170
+ /**
171
+ * 内部删除(复用事务)。
172
+ * @param {any} db
173
+ * @param {string} documentId
174
+ */
175
+ async deleteDocumentInternal(db, documentId) {
176
+ const stmt = db.prepare('DELETE FROM documents WHERE document_id = ?');
177
+ stmt.bind([documentId]);
178
+ stmt.step();
179
+ stmt.finalize();
180
+ }
181
+
182
+ /**
183
+ * 清空所有索引(用于全量重建)。
184
+ * @returns {Promise<void>}
185
+ */
186
+ async clearAll() {
187
+ const db = await this.backend.openWriteDb(this.dbPath);
188
+ db.exec('DELETE FROM segments');
189
+ db.exec('DELETE FROM documents');
190
+ this.backend.invalidateReadCache();
191
+ }
192
+
193
+ /**
194
+ * 获取统计信息(文档数、段数)。
195
+ * @returns {Promise<{documentCount:number, segmentCount:number}>}
196
+ */
197
+ async getStats() {
198
+ const db = await this.backend.openReadDb(this.dbPath);
199
+ const documentCount = db.selectValue('SELECT COUNT(*) FROM documents') || 0;
200
+ const segmentCount = db.selectValue('SELECT COUNT(*) FROM segments') || 0;
201
+ return { documentCount, segmentCount };
202
+ }
203
+ }