chatlab-cli 0.36.2 → 0.37.1

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 (203) hide show
  1. package/dist/{chat-command-SEV6N3AR.mjs → chat-command-YAFRLHMQ.mjs} +15 -16
  2. package/dist/{chat-command-SEV6N3AR.mjs.map → chat-command-YAFRLHMQ.mjs.map} +1 -1
  3. package/dist/{chunk-6EZPD3XJ.mjs → chunk-2PY4XCH3.mjs} +6 -3
  4. package/dist/chunk-2PY4XCH3.mjs.map +1 -0
  5. package/dist/{chunk-HWIXK7SU.mjs → chunk-73TJ2S7W.mjs} +1643 -3
  6. package/dist/chunk-73TJ2S7W.mjs.map +1 -0
  7. package/dist/{chunk-YUH3H3QW.mjs → chunk-ARIBCH47.mjs} +96 -5
  8. package/dist/chunk-ARIBCH47.mjs.map +1 -0
  9. package/dist/{chunk-WRBFV2GX.mjs → chunk-FPVRSXQ4.mjs} +49 -55
  10. package/dist/chunk-FPVRSXQ4.mjs.map +1 -0
  11. package/dist/{chunk-VREVDZ4Q.mjs → chunk-GKCIRKOH.mjs} +1934 -35
  12. package/dist/chunk-GKCIRKOH.mjs.map +1 -0
  13. package/dist/{chunk-TJ3NOBVI.mjs → chunk-JWJREWVN.mjs} +119 -288
  14. package/dist/chunk-JWJREWVN.mjs.map +1 -0
  15. package/dist/{chunk-TWH422HC.mjs → chunk-MMTEGUQA.mjs} +3 -3
  16. package/dist/{chunk-VUYWUHWW.mjs → chunk-N4SB6QZV.mjs} +3 -3
  17. package/dist/chunk-N4SB6QZV.mjs.map +1 -0
  18. package/dist/{chunk-TRGR3KKJ.mjs → chunk-OTYNMDDL.mjs} +2643 -682
  19. package/dist/chunk-OTYNMDDL.mjs.map +1 -0
  20. package/dist/{chunk-CH5YAZ4Q.mjs → chunk-PDQV4RUA.mjs} +2 -2
  21. package/dist/{chunk-WJQOXOMR.mjs → chunk-UP6JIV3S.mjs} +285 -41
  22. package/dist/chunk-UP6JIV3S.mjs.map +1 -0
  23. package/dist/chunk-WDLO7OLX.mjs +101 -0
  24. package/dist/chunk-WDLO7OLX.mjs.map +1 -0
  25. package/dist/cli.mjs +9 -9
  26. package/dist/contacts-worker.mjs +2 -2
  27. package/dist/global-insight-worker.mjs +1 -1
  28. package/dist/{http-237XYNRT.mjs → http-LPFPVTBC.mjs} +12 -12
  29. package/dist/{import-R2E2TFAK.mjs → import-T4WX2ZJL.mjs} +7 -7
  30. package/dist/index.mjs +12 -12
  31. package/dist/{mcp-BED4AARV.mjs → mcp-SMF3NWIN.mjs} +8 -8
  32. package/dist/people-relationships-worker.mjs +2 -2
  33. package/dist/semantic-index-worker.mjs +1 -1
  34. package/dist/{src-AYMALWU5.mjs → src-EGLLMTAU.mjs} +44 -14
  35. package/dist/{stream-import-RXNSOOYH.mjs → stream-import-2BXJTTCE.mjs} +7 -7
  36. package/dist/{worker-runtime-JGAZU7MC.mjs → worker-runtime-RDLSPXFT.mjs} +5 -5
  37. package/dist-cli-web/assets/AISettingsTab-CEhrXlYR.js +1 -0
  38. package/dist-cli-web/assets/{AboutTab-DCM1-glA.js → AboutTab-Bbu0SsMM.js} +1 -1
  39. package/dist-cli-web/assets/ActionToolsPanel-DDNXXH8v.css +1 -0
  40. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-IB1yZxCE.js +75 -0
  41. package/dist-cli-web/assets/{Alert-D-egBWcb.js → Alert-510jTN3l.js} +1 -1
  42. package/dist-cli-web/assets/ApiSettingsTab-Bi1GRMN4.js +2 -0
  43. package/dist-cli-web/assets/BatchManageTab-DhnqMCSM.js +1 -0
  44. package/dist-cli-web/assets/{BufferResource-BE2_2bZK.js → BufferResource-CKZxnACp.js} +1 -1
  45. package/dist-cli-web/assets/{CanvasRenderer-DDKdvEYQ.js → CanvasRenderer-BeK5POBy.js} +1 -1
  46. package/dist-cli-web/assets/CardDecoration-RuBA3N2j.js +1 -0
  47. package/dist-cli-web/assets/ChatRecordDrawer-BY_-lIEj.js +1 -0
  48. package/dist-cli-web/assets/{ChatRecordWorkspace-DqUI4QiU.js → ChatRecordWorkspace-7prKd-Tq.js} +1 -1
  49. package/dist-cli-web/assets/Checkbox-_eH64bb0.js +1 -0
  50. package/dist-cli-web/assets/DataDirCleanupNotice-BHaXwlfr.js +1 -0
  51. package/dist-cli-web/assets/{DatePicker.vue_vue_type_script_setup_true_lang-sPDoPzZ9.js → DatePicker.vue_vue_type_script_setup_true_lang-D9Ul_GYz.js} +1 -1
  52. package/dist-cli-web/assets/{DebugToolsPanel-BIHA6jw4.js → DebugToolsPanel-ByKItGrS.js} +1 -1
  53. package/dist-cli-web/assets/EChart.vue_vue_type_script_setup_true_lang-DubnuUXI.js +1 -0
  54. package/dist-cli-web/assets/EChartBar.vue_vue_type_script_setup_true_lang-DKNCZuFm.js +1 -0
  55. package/dist-cli-web/assets/EChartLine.vue_vue_type_script_setup_true_lang-Fl7xAOSR.js +1 -0
  56. package/dist-cli-web/assets/EChartWordcloud-C6XM4KOX.js +1 -0
  57. package/dist-cli-web/assets/FileDropZone.vue_vue_type_script_setup_true_lang-DYDdRSNE.js +1 -0
  58. package/dist-cli-web/assets/{Filter-kO4dIDJ2.js → Filter-CByj_rCR.js} +1 -1
  59. package/dist-cli-web/assets/GlobalTaskBar-BdELjzrs.js +1 -0
  60. package/dist-cli-web/assets/GlobalTaskBar-haLzezqY.css +1 -0
  61. package/dist-cli-web/assets/GroupRelationshipGalaxyView-CnzPEgUS.js +1 -0
  62. package/dist-cli-web/assets/Label-DCIcxkhU.js +1 -0
  63. package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-BBcCt_Qm.js +1 -0
  64. package/dist-cli-web/assets/MessageExportModal-DNU16Bs8.js +1 -0
  65. package/dist-cli-web/assets/PageHeader-CKeZjNNR.js +1 -0
  66. package/dist-cli-web/assets/PageHeader-CPLgiKT8.css +1 -0
  67. package/dist-cli-web/assets/{PageTabs.vue_vue_type_script_setup_true_lang-Cgungbjl.js → PageTabs.vue_vue_type_script_setup_true_lang-C8M3wNNH.js} +1 -1
  68. package/dist-cli-web/assets/{RelationshipGalaxyCanvas-Cod1JX9m.js → RelationshipGalaxyCanvas-CDhljGyJ.js} +3 -3
  69. package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-CkefzeFJ.js → RelationshipGalaxyThreeCanvas-BP8fsvvf.js} +1 -1
  70. package/dist-cli-web/assets/{RenderTargetSystem-SuQ7cbze.js → RenderTargetSystem-D76QrDT_.js} +1 -1
  71. package/dist-cli-web/assets/{RovingFocusItem-psoFYxrC.js → RovingFocusItem-BJMUQ5DJ.js} +1 -1
  72. package/dist-cli-web/assets/{ScreenCaptureModal-D4r9ixKh.js → ScreenCaptureModal-DkX4z-M1.js} +1 -1
  73. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-qFhJO5B0.js +1 -0
  74. package/dist-cli-web/assets/Select-B5oJc5y1.js +1 -0
  75. package/dist-cli-web/assets/SettingsModal-BO-QKQ2h.js +2 -0
  76. package/dist-cli-web/assets/{StorageTab-DhSJ-0Ir.js → StorageTab-CPPXXaRm.js} +1 -1
  77. package/dist-cli-web/assets/Switch-CDk3LZ4_.js +1 -0
  78. package/dist-cli-web/assets/Tabs-BMq9wODW.js +1 -0
  79. package/dist-cli-web/assets/{Tabs-Bnm_Q7yW.js → Tabs-DqWTBV8x.js} +1 -1
  80. package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-D2outEOV.js +1 -0
  81. package/dist-cli-web/assets/{TimeSelect.vue_vue_type_script_setup_true_lang-Cjz8Wpg-.js → TimeSelect.vue_vue_type_script_setup_true_lang-BcflxP_U.js} +1 -1
  82. package/dist-cli-web/assets/{VisuallyHiddenInput-Bo_XwaLu.js → VisuallyHiddenInput-BAFpkxMI.js} +1 -1
  83. package/dist-cli-web/assets/{WebGLRenderer-D1x9WZRj.js → WebGLRenderer-DjCPvR_O.js} +1 -1
  84. package/dist-cli-web/assets/{WebGPURenderer-37Nw-tLm.js → WebGPURenderer-C2B_u03z.js} +1 -1
  85. package/dist-cli-web/assets/aiChat--CR4g2M7.js +8 -0
  86. package/dist-cli-web/assets/base-F6s0vTrr.js +1 -0
  87. package/dist-cli-web/assets/{browserAll-CWrz_-4U.js → browserAll-DiUAZg4L.js} +1 -1
  88. package/dist-cli-web/assets/chatMessages-BGRbSlvD.css +1 -0
  89. package/dist-cli-web/assets/chatMessages-Bd33SSXI.js +147 -0
  90. package/dist-cli-web/assets/cli-web-DoibEdj2.js +1 -0
  91. package/dist-cli-web/assets/{fetch-CkXgOxcu.js → fetch-4nh1LaVF.js} +3 -3
  92. package/dist-cli-web/assets/fetch-7lUvXbhU.js +1 -0
  93. package/dist-cli-web/assets/{fetch-CFpLXWTe.js → fetch-BEaW1j5H.js} +1 -1
  94. package/dist-cli-web/assets/{fetch-CgA1peJD.js → fetch-B_YFABxI.js} +1 -1
  95. package/dist-cli-web/assets/{fetch-BGmCCyzz.js → fetch-BeX69kpS.js} +1 -1
  96. package/dist-cli-web/assets/{fetch-Du-wU0OO.js → fetch-CRocA-4C.js} +1 -1
  97. package/dist-cli-web/assets/{fetch-DPSWt6yX.js → fetch-CXM-cjDS.js} +1 -1
  98. package/dist-cli-web/assets/{fetch-qd2FgBWQ.js → fetch-CXqeAbnY.js} +1 -1
  99. package/dist-cli-web/assets/{fetch-6pcVxB9h.js → fetch-CxVcHNid.js} +1 -1
  100. package/dist-cli-web/assets/fetch-DBpDMy29.js +1 -0
  101. package/dist-cli-web/assets/{fetch-Gu4qQjUB.js → fetch-DKBBS9Op.js} +1 -1
  102. package/dist-cli-web/assets/{fetch-DZ6OF-xW.js → fetch-epFXmPIQ.js} +1 -1
  103. package/dist-cli-web/assets/{host-D8zlA62K.js → host-C4rU8pKA.js} +1 -1
  104. package/dist-cli-web/assets/{host-CWsAWKgy.js → host-CtSqR_Zm.js} +1 -1
  105. package/dist-cli-web/assets/{index-Dy0fRMDf.js → index-4DVZ2g0M.js} +10 -10
  106. package/dist-cli-web/assets/index-B1oj9KDv.js +135 -0
  107. package/dist-cli-web/assets/index-BFH1j_R9.js +1 -0
  108. package/dist-cli-web/assets/index-BZpR015e.css +1 -0
  109. package/dist-cli-web/assets/index-BgelKQTW.css +1 -0
  110. package/dist-cli-web/assets/{index-v6DlL4ue.js → index-BvMAw3Zd.js} +1 -1
  111. package/dist-cli-web/assets/index-C1A1FwpO.css +1 -0
  112. package/dist-cli-web/assets/index-CIIyg_o9.css +1 -0
  113. package/dist-cli-web/assets/index-CaYtkONV.js +64 -0
  114. package/dist-cli-web/assets/index-CuCQ1d9f.js +1 -0
  115. package/dist-cli-web/assets/index-D3Hyjv4e.js +137 -0
  116. package/dist-cli-web/assets/index-D8cHMqV-.js +2 -0
  117. package/dist-cli-web/assets/index-DlHuILT_.js +1 -0
  118. package/dist-cli-web/assets/index-R4LqAnx9.js +2 -0
  119. package/dist-cli-web/assets/index-_m1mf1jm.js +1 -0
  120. package/dist-cli-web/assets/{index-a7CT8DQm.js → index-ekvsNmfe.js} +1 -1
  121. package/dist-cli-web/assets/index-mXe1LssU.js +1 -0
  122. package/dist-cli-web/assets/index-mbLXPIyC.js +1 -0
  123. package/dist-cli-web/assets/{people-page-header-Czt9e1jv.js → people-page-header-BbYSSpsO.js} +1 -1
  124. package/dist-cli-web/assets/{prompt-DGazz143.js → prompt-C-ofJ7t_.js} +1 -1
  125. package/dist-cli-web/assets/{semantic-index-config-builder-BkFHzIUy.js → semantic-index-config-builder-D3TYVYC-.js} +1 -1
  126. package/dist-cli-web/assets/service-BYOUkk7V.js +1 -0
  127. package/dist-cli-web/assets/{service-CAp0YJoS.js → service-CppETV4a.js} +1 -1
  128. package/dist-cli-web/assets/service-tEx-5LUL.js +1 -0
  129. package/dist-cli-web/assets/useFormControl-CrWi_6Hn.js +1 -0
  130. package/dist-cli-web/assets/useTable-Clk7ecq-.js +1 -0
  131. package/dist-cli-web/assets/{useToast-YAHDkKDN.js → useToast-B5LaA6c-.js} +1 -1
  132. package/dist-cli-web/assets/{vendor-echarts-hTLhTs52.js → vendor-echarts-C9ugTmum.js} +1 -1
  133. package/dist-cli-web/assets/{vendor-echarts-wordcloud-B8CF_NgP.js → vendor-echarts-wordcloud-DNiU8Psq.js} +1 -1
  134. package/dist-cli-web/assets/{webworkerAll-CvSp2Y8j.js → webworkerAll-CyA4b4LT.js} +1 -1
  135. package/dist-cli-web/index.html +2 -2
  136. package/package.json +3 -3
  137. package/dist/chunk-6E46U4H2.mjs +0 -41
  138. package/dist/chunk-6E46U4H2.mjs.map +0 -1
  139. package/dist/chunk-6EZPD3XJ.mjs.map +0 -1
  140. package/dist/chunk-HWIXK7SU.mjs.map +0 -1
  141. package/dist/chunk-TJ3NOBVI.mjs.map +0 -1
  142. package/dist/chunk-TRGR3KKJ.mjs.map +0 -1
  143. package/dist/chunk-VREVDZ4Q.mjs.map +0 -1
  144. package/dist/chunk-VUYWUHWW.mjs.map +0 -1
  145. package/dist/chunk-WJQOXOMR.mjs.map +0 -1
  146. package/dist/chunk-WRBFV2GX.mjs.map +0 -1
  147. package/dist/chunk-YUH3H3QW.mjs.map +0 -1
  148. package/dist-cli-web/assets/AISettingsTab-VgI7PiJd.js +0 -1
  149. package/dist-cli-web/assets/ActionToolsPanel-BTGgwXVD.css +0 -1
  150. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-a7RMjjve.js +0 -112
  151. package/dist-cli-web/assets/ApiSettingsTab-Co3LWf1f.js +0 -2
  152. package/dist-cli-web/assets/BatchManageTab-2LrckmQm.js +0 -1
  153. package/dist-cli-web/assets/ChatRecordDrawer-DRhztqLG.js +0 -1
  154. package/dist-cli-web/assets/Checkbox-EfpQmv4A.js +0 -1
  155. package/dist-cli-web/assets/DataDirCleanupNotice-BYIiR58J.js +0 -1
  156. package/dist-cli-web/assets/EChart.vue_vue_type_script_setup_true_lang-DryMSoCz.js +0 -1
  157. package/dist-cli-web/assets/EChartBar.vue_vue_type_script_setup_true_lang-BdFW-hvt.js +0 -1
  158. package/dist-cli-web/assets/EChartLine.vue_vue_type_script_setup_true_lang-C69Kdw9X.js +0 -1
  159. package/dist-cli-web/assets/EChartWordcloud-DM7UEHdK.js +0 -1
  160. package/dist-cli-web/assets/GlobalTaskBar-BZS_h0zC.js +0 -1
  161. package/dist-cli-web/assets/GlobalTaskBar-COW_uBX6.css +0 -1
  162. package/dist-cli-web/assets/GroupRelationshipGalaxyView--WIaHx_F.js +0 -1
  163. package/dist-cli-web/assets/Label-CIuDXyeM.js +0 -1
  164. package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-B_7VHxTx.js +0 -1
  165. package/dist-cli-web/assets/MessageExportModal-BQsIpyxv.js +0 -1
  166. package/dist-cli-web/assets/PageHeader-DxhQHF_0.js +0 -1
  167. package/dist-cli-web/assets/PageHeader-XIXMTomn.css +0 -1
  168. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-v_7aRBgM.js +0 -1
  169. package/dist-cli-web/assets/Select-CxurgslB.js +0 -1
  170. package/dist-cli-web/assets/SettingsModal-DBZucuf6.js +0 -2
  171. package/dist-cli-web/assets/Switch-3D81G6uV.js +0 -1
  172. package/dist-cli-web/assets/Tabs-DB603EEu.js +0 -1
  173. package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-BDTdkH5i.js +0 -1
  174. package/dist-cli-web/assets/aiChat-BCzYQeZq.js +0 -8
  175. package/dist-cli-web/assets/cli-web-C-8N0CJh.js +0 -1
  176. package/dist-cli-web/assets/fetch-B69OkB6B.js +0 -1
  177. package/dist-cli-web/assets/fetch-z9M9FcIC.js +0 -1
  178. package/dist-cli-web/assets/index-BGd4ZfhO.js +0 -2
  179. package/dist-cli-web/assets/index-BYZF1ngN.js +0 -1
  180. package/dist-cli-web/assets/index-BcuwlCin.css +0 -1
  181. package/dist-cli-web/assets/index-BnaLj2dx.css +0 -1
  182. package/dist-cli-web/assets/index-Bq5q3kSU.js +0 -36
  183. package/dist-cli-web/assets/index-CPR3jdC2.js +0 -137
  184. package/dist-cli-web/assets/index-D3AjvaOX.js +0 -1
  185. package/dist-cli-web/assets/index-DAitScUA.js +0 -1
  186. package/dist-cli-web/assets/index-DBSMWAJ0.js +0 -1
  187. package/dist-cli-web/assets/index-DTn9htw-.js +0 -64
  188. package/dist-cli-web/assets/index-DV_rWxcH.js +0 -1
  189. package/dist-cli-web/assets/index-DxhAGqYM.css +0 -1
  190. package/dist-cli-web/assets/index-I0mIcDpE.css +0 -1
  191. package/dist-cli-web/assets/index-onwNShRw.js +0 -2
  192. package/dist-cli-web/assets/service-BMqwB74f.js +0 -1
  193. package/dist-cli-web/assets/service-CUQ8iHdS.js +0 -1
  194. package/dist-cli-web/assets/useFormControl-DcSMUc2d.js +0 -1
  195. package/dist-cli-web/assets/useTable-Clmq3sge.js +0 -1
  196. /package/dist/{chunk-TWH422HC.mjs.map → chunk-MMTEGUQA.mjs.map} +0 -0
  197. /package/dist/{chunk-CH5YAZ4Q.mjs.map → chunk-PDQV4RUA.mjs.map} +0 -0
  198. /package/dist/{http-237XYNRT.mjs.map → http-LPFPVTBC.mjs.map} +0 -0
  199. /package/dist/{import-R2E2TFAK.mjs.map → import-T4WX2ZJL.mjs.map} +0 -0
  200. /package/dist/{mcp-BED4AARV.mjs.map → mcp-SMF3NWIN.mjs.map} +0 -0
  201. /package/dist/{src-AYMALWU5.mjs.map → src-EGLLMTAU.mjs.map} +0 -0
  202. /package/dist/{stream-import-RXNSOOYH.mjs.map → stream-import-2BXJTTCE.mjs.map} +0 -0
  203. /package/dist/{worker-runtime-JGAZU7MC.mjs.map → worker-runtime-RDLSPXFT.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/utils/Utf8Stream.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/Parser.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/utils/emit.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/index.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/filters/FilterBase.js","../../../node_modules/.pnpm/stream-chain@2.2.5/node_modules/stream-chain/index.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/utils/withParser.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/filters/Pick.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/Assembler.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/streamers/StreamBase.js","../../../node_modules/.pnpm/stream-json@1.9.1/node_modules/stream-json/streamers/StreamValues.js","../../../node_modules/stream-chain/index.js","../../../node_modules/.pnpm/pend@1.2.0/node_modules/pend/index.js","../../../node_modules/.pnpm/yauzl@3.4.0/node_modules/yauzl/fd-slicer.js","../../../node_modules/.pnpm/yauzl@3.4.0/node_modules/yauzl/crc32.js","../../../node_modules/.pnpm/yauzl@3.4.0/node_modules/yauzl/index.js","../../../packages/node-runtime/src/temp-workspace.ts","../../../packages/parser/src/index.ts","../../../packages/parser/src/sniffer.ts","../../../packages/parser/src/format-ids.ts","../../../packages/parser/src/formats/chatlab.ts","../../../packages/parser/src/utils.ts","../../../packages/parser/src/native/loader.ts","../../../packages/parser/src/native/create-native-parser.ts","../../../packages/parser/src/native/chatlab-native.ts","../../../packages/parser/src/formats/chatlab-jsonl.ts","../../../packages/parser/src/formats/shuakami-qq-exporter.ts","../../../packages/parser/src/formats/shuakami-qq-preprocessor.ts","../../../packages/parser/src/native/shuakami-qq-native.ts","../../../packages/parser/src/formats/shuakami-qq-exporter-chunked.ts","../../../packages/parser/src/formats/weflow.ts","../../../packages/parser/src/native/weflow-native.ts","../../../packages/parser/src/formats/weflow-preprocessor.ts","../../../packages/parser/src/formats/ycccccccy-echotrace.ts","../../../packages/parser/src/formats/tyrrrz-discord-exporter.ts","../../../packages/parser/src/formats/telegram-native.ts","../../../packages/parser/src/formats/utils/telegram-utils.ts","../../../packages/parser/src/formats/telegram-native-single.ts","../../../packages/parser/src/browser/telegram.ts","../../../packages/parser/src/formats/whatsapp-native-txt.ts","../../../packages/parser/src/browser/whatsapp.ts","../../../packages/parser/src/formats/qq-native-txt.ts","../../../packages/parser/src/browser/qq.ts","../../../packages/parser/src/formats/instagram-native.ts","../../../packages/parser/src/formats/google-chat-takeout.ts","../../../packages/parser/src/formats/line-native-txt.ts","../../../packages/parser/src/browser/line.ts","../../../packages/parser/src/formats/index.ts","../../../packages/parser/src/chatlab-validator.ts","../../../packages/parser/src/chatlab-demo-timeline.ts","../../../packages/node-runtime/src/import/native-parser-status.ts","../../../packages/node-runtime/src/import/perf-logger.ts","../../../packages/node-runtime/src/import/message-batch-inserter.ts","../../../packages/node-runtime/src/import/streaming-importer.ts","../../../packages/node-runtime/src/import/message-deduplicator.ts","../../../packages/node-runtime/src/import/incremental-importer.ts","../../../packages/node-runtime/src/import/session-id.ts","../../../packages/node-runtime/src/import/auto-import-matcher.ts","../../../packages/node-runtime/src/import/auto-importer.ts","../../../packages/node-runtime/src/import/batch-coordinator.ts","../../../packages/node-runtime/src/import/auto-import-batch.ts","../../../packages/node-runtime/src/import/import-lock.ts","../../../packages/node-runtime/src/import/archive/errors.ts","../../../packages/node-runtime/src/import/archive/archive-reader.ts","../../../packages/node-runtime/src/import/archive/google-chat-resolver.ts","../../../packages/node-runtime/src/import/archive/source-manager.ts","../../../packages/node-runtime/src/export/markdown-exporter.ts","../../../packages/node-runtime/src/export/format-exporter.ts","../../../packages/node-runtime/src/merger/temp-db.ts","../../../packages/node-runtime/src/services/session-service.ts","../../../packages/node-runtime/src/services/member-service.ts","../../../packages/node-runtime/src/services/session-index-service.ts","../../../packages/node-runtime/src/services/summary-service.ts","../../../packages/node-runtime/src/services/export-service.ts","../../../packages/node-runtime/src/services/owner-profile-service.ts","../../../packages/node-runtime/src/services/contacts/overrides.ts","../../../packages/node-runtime/src/services/contacts/signature.ts","../../../packages/node-runtime/src/services/contacts/snapshot.ts","../../../packages/node-runtime/src/services/contacts/worker-runner.ts","../../../packages/node-runtime/src/services/contacts/service.ts","../../../packages/node-runtime/src/services/contacts/index.ts","../../../packages/node-runtime/src/services/people/relationships/signature.ts","../../../packages/node-runtime/src/services/people/relationships/snapshot.ts","../../../packages/node-runtime/src/services/people/relationships/worker-runner.ts","../../../packages/node-runtime/src/services/people/relationships/service.ts","../../../packages/node-runtime/src/services/people/relationships/index.ts","../../../packages/node-runtime/src/services/global-insight/service.ts","../../../packages/node-runtime/src/services/global-insight/signature.ts","../../../packages/node-runtime/src/services/global-insight/worker-runner.ts","../../../packages/node-runtime/src/services/global-insight/time-investment-service.ts","../../../packages/node-runtime/src/services/global-insight/time-investment-signature.ts","../../../packages/node-runtime/src/services/global-insight/index.ts","../../../packages/node-runtime/src/semantic-index/config.ts","../../../packages/node-runtime/src/semantic-index/embedding/text.ts","../../../packages/node-runtime/src/semantic-index/embedding/local.ts","../../../packages/node-runtime/src/semantic-index/service.ts","../../../packages/node-runtime/src/semantic-index/chunker-config.ts","../../../packages/node-runtime/src/semantic-index/tokens.ts","../../../packages/node-runtime/src/semantic-index/embedding/api.ts","../../../packages/node-runtime/src/semantic-index/embedding/profiles.ts","../../../packages/node-runtime/src/semantic-index/embedder-factory.ts","../../../packages/node-runtime/src/semantic-index/store.ts","../../../packages/node-runtime/src/semantic-index/schema.ts","../../../packages/node-runtime/src/semantic-index/session-state-store.ts","../../../packages/node-runtime/src/semantic-index/warmup/job-queue.ts","../../../packages/node-runtime/src/semantic-index/chunker.ts","../../../packages/node-runtime/src/semantic-index/warmup/runner.ts","../../../packages/node-runtime/src/semantic-index/retrieval/semantic-search.ts","../../../packages/node-runtime/src/semantic-index/retrieval/evidence.ts","../../../packages/node-runtime/src/semantic-index/chat-db/message-source.ts","../../../packages/node-runtime/src/semantic-index/chat-db/message-range-reader.ts","../../../packages/node-runtime/src/semantic-index/embedding/local-runtime.ts","../../../packages/node-runtime/src/services/navigation-layout-service.ts","../../../packages/node-runtime/src/services/merge-cache.ts","../../../packages/node-runtime/src/services/push-importer.ts","../../../packages/node-runtime/src/services/import-idempotency.ts"],"sourcesContent":["'use strict';\n\nconst {Transform} = require('stream');\nconst {StringDecoder} = require('string_decoder');\n\nclass Utf8Stream extends Transform {\n constructor(options) {\n super(Object.assign({}, options, {writableObjectMode: false}));\n this._buffer = '';\n }\n\n _transform(chunk, encoding, callback) {\n if (typeof chunk == 'string') {\n this._transform = this._transformString;\n } else {\n this._stringDecoder = new StringDecoder();\n this._transform = this._transformBuffer;\n }\n this._transform(chunk, encoding, callback);\n }\n\n _transformBuffer(chunk, _, callback) {\n this._buffer += this._stringDecoder.write(chunk);\n this._processBuffer(callback);\n }\n\n _transformString(chunk, _, callback) {\n this._buffer += chunk.toString();\n this._processBuffer(callback);\n }\n\n _processBuffer(callback) {\n if (this._buffer) {\n this.push(this._buffer, 'utf8');\n this._buffer = '';\n }\n callback(null);\n }\n\n _flushInput() {\n // meant to be called from _flush()\n if (this._stringDecoder) {\n this._buffer += this._stringDecoder.end();\n }\n }\n\n _flush(callback) {\n this._flushInput();\n this._processBuffer(callback);\n }\n}\n\nmodule.exports = Utf8Stream;\n","'use strict';\n\nconst Utf8Stream = require('./utils/Utf8Stream');\n\nconst patterns = {\n value1: /^(?:[\\\"\\{\\[\\]\\-\\d]|true\\b|false\\b|null\\b|\\s{1,256})/,\n string: /^(?:[^\\x00-\\x1f\\\"\\\\]{1,256}|\\\\[bfnrt\\\"\\\\\\/]|\\\\u[\\da-fA-F]{4}|\\\")/,\n key1: /^(?:[\\\"\\}]|\\s{1,256})/,\n colon: /^(?:\\:|\\s{1,256})/,\n comma: /^(?:[\\,\\]\\}]|\\s{1,256})/,\n ws: /^\\s{1,256}/,\n numberStart: /^\\d/,\n numberDigit: /^\\d{0,256}/,\n numberFraction: /^[\\.eE]/,\n numberExponent: /^[eE]/,\n numberExpSign: /^[-+]/\n};\nconst MAX_PATTERN_SIZE = 16;\n\nlet noSticky = true;\ntry {\n new RegExp('.', 'y');\n noSticky = false;\n} catch (e) {\n // suppress\n}\n\n!noSticky &&\n Object.keys(patterns).forEach(key => {\n let src = patterns[key].source.slice(1); // lop off ^\n if (src.slice(0, 3) === '(?:' && src.slice(-1) === ')') {\n src = src.slice(3, -1);\n }\n patterns[key] = new RegExp(src, 'y');\n });\n\npatterns.numberFracStart = patterns.numberExpStart = patterns.numberStart;\npatterns.numberFracDigit = patterns.numberExpDigit = patterns.numberDigit;\n\nconst values = {true: true, false: false, null: null},\n expected = {object: 'objectStop', array: 'arrayStop', '': 'done'};\n\n// long hexadecimal codes: \\uXXXX\nconst fromHex = s => String.fromCharCode(parseInt(s.slice(2), 16));\n\n// short codes: \\b \\f \\n \\r \\t \\\" \\\\ \\/\nconst codes = {b: '\\b', f: '\\f', n: '\\n', r: '\\r', t: '\\t', '\"': '\"', '\\\\': '\\\\', '/': '/'};\n\nclass Parser extends Utf8Stream {\n static make(options) {\n return new Parser(options);\n }\n\n constructor(options) {\n super(Object.assign({}, options, {readableObjectMode: true}));\n\n this._packKeys = this._packStrings = this._packNumbers = this._streamKeys = this._streamStrings = this._streamNumbers = true;\n if (options) {\n 'packValues' in options && (this._packKeys = this._packStrings = this._packNumbers = options.packValues);\n 'packKeys' in options && (this._packKeys = options.packKeys);\n 'packStrings' in options && (this._packStrings = options.packStrings);\n 'packNumbers' in options && (this._packNumbers = options.packNumbers);\n 'streamValues' in options && (this._streamKeys = this._streamStrings = this._streamNumbers = options.streamValues);\n 'streamKeys' in options && (this._streamKeys = options.streamKeys);\n 'streamStrings' in options && (this._streamStrings = options.streamStrings);\n 'streamNumbers' in options && (this._streamNumbers = options.streamNumbers);\n this._jsonStreaming = options.jsonStreaming;\n }\n !this._packKeys && (this._streamKeys = true);\n !this._packStrings && (this._streamStrings = true);\n !this._packNumbers && (this._streamNumbers = true);\n\n this._done = false;\n this._expect = this._jsonStreaming ? 'done' : 'value';\n this._stack = [];\n this._parent = '';\n this._open_number = false;\n this._accumulator = '';\n }\n\n _flush(callback) {\n this._done = true;\n super._flush(error => {\n if (error) return callback(error);\n if (this._open_number) {\n if (this._streamNumbers) {\n this.push({name: 'endNumber'});\n }\n this._open_number = false;\n if (this._packNumbers) {\n this.push({name: 'numberValue', value: this._accumulator});\n this._accumulator = '';\n }\n }\n callback(null);\n });\n }\n\n _processBuffer(callback) {\n let match,\n value,\n index = 0;\n main: for (;;) {\n switch (this._expect) {\n case 'value1':\n case 'value':\n patterns.value1.lastIndex = index;\n match = patterns.value1.exec(this._buffer);\n if (!match) {\n if (this._done || index + MAX_PATTERN_SIZE < this._buffer.length) {\n if (index < this._buffer.length) return callback(new Error('Parser cannot parse input: expected a value'));\n return callback(new Error('Parser has expected a value'));\n }\n break main; // wait for more input\n }\n value = match[0];\n switch (value) {\n case '\"':\n this._streamStrings && this.push({name: 'startString'});\n this._expect = 'string';\n break;\n case '{':\n this.push({name: 'startObject'});\n this._stack.push(this._parent);\n this._parent = 'object';\n this._expect = 'key1';\n break;\n case '[':\n this.push({name: 'startArray'});\n this._stack.push(this._parent);\n this._parent = 'array';\n this._expect = 'value1';\n break;\n case ']':\n if (this._expect !== 'value1') return callback(new Error(\"Parser cannot parse input: unexpected token ']'\"));\n if (this._open_number) {\n this._streamNumbers && this.push({name: 'endNumber'});\n this._open_number = false;\n if (this._packNumbers) {\n this.push({name: 'numberValue', value: this._accumulator});\n this._accumulator = '';\n }\n }\n this.push({name: 'endArray'});\n this._parent = this._stack.pop();\n this._expect = expected[this._parent];\n break;\n case '-':\n this._open_number = true;\n if (this._streamNumbers) {\n this.push({name: 'startNumber'});\n this.push({name: 'numberChunk', value: '-'});\n }\n this._packNumbers && (this._accumulator = '-');\n this._expect = 'numberStart';\n break;\n case '0':\n this._open_number = true;\n if (this._streamNumbers) {\n this.push({name: 'startNumber'});\n this.push({name: 'numberChunk', value: '0'});\n }\n this._packNumbers && (this._accumulator = '0');\n this._expect = 'numberFraction';\n break;\n case '1':\n case '2':\n case '3':\n case '4':\n case '5':\n case '6':\n case '7':\n case '8':\n case '9':\n this._open_number = true;\n if (this._streamNumbers) {\n this.push({name: 'startNumber'});\n this.push({name: 'numberChunk', value: value});\n }\n this._packNumbers && (this._accumulator = value);\n this._expect = 'numberDigit';\n break;\n case 'true':\n case 'false':\n case 'null':\n if (this._buffer.length - index === value.length && !this._done) break main; // wait for more input\n this.push({name: value + 'Value', value: values[value]});\n this._expect = expected[this._parent];\n break;\n // default: // ws\n }\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'keyVal':\n case 'string':\n patterns.string.lastIndex = index;\n match = patterns.string.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length && (this._done || this._buffer.length - index >= 6))\n return callback(new Error('Parser cannot parse input: escaped characters'));\n if (this._done) return callback(new Error('Parser has expected a string value'));\n break main; // wait for more input\n }\n value = match[0];\n if (value === '\"') {\n if (this._expect === 'keyVal') {\n this._streamKeys && this.push({name: 'endKey'});\n if (this._packKeys) {\n this.push({name: 'keyValue', value: this._accumulator});\n this._accumulator = '';\n }\n this._expect = 'colon';\n } else {\n this._streamStrings && this.push({name: 'endString'});\n if (this._packStrings) {\n this.push({name: 'stringValue', value: this._accumulator});\n this._accumulator = '';\n }\n this._expect = expected[this._parent];\n }\n } else if (value.length > 1 && value.charAt(0) === '\\\\') {\n const t = value.length == 2 ? codes[value.charAt(1)] : fromHex(value);\n if (this._expect === 'keyVal' ? this._streamKeys : this._streamStrings) {\n this.push({name: 'stringChunk', value: t});\n }\n if (this._expect === 'keyVal' ? this._packKeys : this._packStrings) {\n this._accumulator += t;\n }\n } else {\n if (this._expect === 'keyVal' ? this._streamKeys : this._streamStrings) {\n this.push({name: 'stringChunk', value: value});\n }\n if (this._expect === 'keyVal' ? this._packKeys : this._packStrings) {\n this._accumulator += value;\n }\n }\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'key1':\n case 'key':\n patterns.key1.lastIndex = index;\n match = patterns.key1.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected an object key'));\n break main; // wait for more input\n }\n value = match[0];\n if (value === '\"') {\n this._streamKeys && this.push({name: 'startKey'});\n this._expect = 'keyVal';\n } else if (value === '}') {\n if (this._expect !== 'key1') return callback(new Error(\"Parser cannot parse input: unexpected token '}'\"));\n this.push({name: 'endObject'});\n this._parent = this._stack.pop();\n this._expect = expected[this._parent];\n }\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'colon':\n patterns.colon.lastIndex = index;\n match = patterns.colon.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) return callback(new Error(\"Parser cannot parse input: expected ':'\"));\n break main; // wait for more input\n }\n value = match[0];\n value === ':' && (this._expect = 'value');\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'arrayStop':\n case 'objectStop':\n patterns.comma.lastIndex = index;\n match = patterns.comma.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) return callback(new Error(\"Parser cannot parse input: expected ','\"));\n break main; // wait for more input\n }\n if (this._open_number) {\n this._streamNumbers && this.push({name: 'endNumber'});\n this._open_number = false;\n if (this._packNumbers) {\n this.push({name: 'numberValue', value: this._accumulator});\n this._accumulator = '';\n }\n }\n value = match[0];\n if (value === ',') {\n this._expect = this._expect === 'arrayStop' ? 'value' : 'key';\n } else if (value === '}' || value === ']') {\n if (value === '}' ? this._expect === 'arrayStop' : this._expect !== 'arrayStop') {\n return callback(new Error(\"Parser cannot parse input: expected '\" + (this._expect === 'arrayStop' ? ']' : '}') + \"'\"));\n }\n this.push({name: value === '}' ? 'endObject' : 'endArray'});\n this._parent = this._stack.pop();\n this._expect = expected[this._parent];\n }\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n // number chunks\n case 'numberStart': // [0-9]\n patterns.numberStart.lastIndex = index;\n match = patterns.numberStart.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected a starting digit'));\n break main; // wait for more input\n }\n value = match[0];\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n this._expect = value === '0' ? 'numberFraction' : 'numberDigit';\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'numberDigit': // [0-9]*\n patterns.numberDigit.lastIndex = index;\n match = patterns.numberDigit.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected a digit'));\n break main; // wait for more input\n }\n value = match[0];\n if (value) {\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n } else {\n if (index < this._buffer.length) {\n this._expect = 'numberFraction';\n break;\n }\n if (this._done) {\n this._expect = expected[this._parent];\n break;\n }\n break main; // wait for more input\n }\n break;\n case 'numberFraction': // [\\.eE]?\n patterns.numberFraction.lastIndex = index;\n match = patterns.numberFraction.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) {\n this._expect = expected[this._parent];\n break;\n }\n break main; // wait for more input\n }\n value = match[0];\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n this._expect = value === '.' ? 'numberFracStart' : 'numberExpSign';\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'numberFracStart': // [0-9]\n patterns.numberFracStart.lastIndex = index;\n match = patterns.numberFracStart.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected a fractional part of a number'));\n break main; // wait for more input\n }\n value = match[0];\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n this._expect = 'numberFracDigit';\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'numberFracDigit': // [0-9]*\n patterns.numberFracDigit.lastIndex = index;\n match = patterns.numberFracDigit.exec(this._buffer);\n value = match[0];\n if (value) {\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n } else {\n if (index < this._buffer.length) {\n this._expect = 'numberExponent';\n break;\n }\n if (this._done) {\n this._expect = expected[this._parent];\n break;\n }\n break main; // wait for more input\n }\n break;\n case 'numberExponent': // [eE]?\n patterns.numberExponent.lastIndex = index;\n match = patterns.numberExponent.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length) {\n this._expect = expected[this._parent];\n break;\n }\n if (this._done) {\n this._expect = 'done';\n break;\n }\n break main; // wait for more input\n }\n value = match[0];\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n this._expect = 'numberExpSign';\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'numberExpSign': // [-+]?\n patterns.numberExpSign.lastIndex = index;\n match = patterns.numberExpSign.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length) {\n this._expect = 'numberExpStart';\n break;\n }\n if (this._done) return callback(new Error('Parser has expected an exponent value of a number'));\n break main; // wait for more input\n }\n value = match[0];\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n this._expect = 'numberExpStart';\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'numberExpStart': // [0-9]\n patterns.numberExpStart.lastIndex = index;\n match = patterns.numberExpStart.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected an exponent part of a number'));\n break main; // wait for more input\n }\n value = match[0];\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n this._expect = 'numberExpDigit';\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n case 'numberExpDigit': // [0-9]*\n patterns.numberExpDigit.lastIndex = index;\n match = patterns.numberExpDigit.exec(this._buffer);\n value = match[0];\n if (value) {\n this._streamNumbers && this.push({name: 'numberChunk', value: value});\n this._packNumbers && (this._accumulator += value);\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n } else {\n if (index < this._buffer.length || this._done) {\n this._expect = expected[this._parent];\n break;\n }\n break main; // wait for more input\n }\n break;\n case 'done':\n patterns.ws.lastIndex = index;\n match = patterns.ws.exec(this._buffer);\n if (!match) {\n if (index < this._buffer.length) {\n if (this._jsonStreaming) {\n this._expect = 'value';\n break;\n }\n return callback(new Error('Parser cannot parse input: unexpected characters'));\n }\n break main; // wait for more input\n }\n value = match[0];\n if (this._open_number) {\n this._streamNumbers && this.push({name: 'endNumber'});\n this._open_number = false;\n if (this._packNumbers) {\n this.push({name: 'numberValue', value: this._accumulator});\n this._accumulator = '';\n }\n }\n if (noSticky) {\n this._buffer = this._buffer.slice(value.length);\n } else {\n index += value.length;\n }\n break;\n }\n }\n !noSticky && (this._buffer = this._buffer.slice(index));\n callback(null);\n }\n}\nParser.parser = Parser.make;\nParser.make.Constructor = Parser;\n\nmodule.exports = Parser;\n","'use strict';\n\nconst emit = stream => stream.on('data', item => stream.emit(item.name, item.value));\n\nmodule.exports = emit;\n","'use strict';\n\nconst Parser = require('./Parser');\nconst emit = require('./utils/emit');\n\nconst make = options => emit(new Parser(options));\n\nmake.Parser = Parser;\nmake.parser = Parser.parser;\n\nmodule.exports = make;\n","'use strict';\n\nconst {Transform} = require('stream');\n\nclass FilterBase extends Transform {\n static stringFilter(string, separator) {\n return stack => {\n const path = stack.join(separator);\n return (\n (path.length === string.length && path === string) ||\n (path.length > string.length && path.substr(0, string.length) === string && path.substr(string.length, separator.length) === separator)\n );\n };\n }\n\n static regExpFilter(regExp, separator) {\n return stack => regExp.test(stack.join(separator));\n }\n\n static arrayReplacement(array) {\n return () => array;\n }\n\n constructor(options) {\n super(Object.assign({}, options, {writableObjectMode: true, readableObjectMode: true}));\n this._transform = this._check;\n this._stack = [];\n\n const filter = options && options.filter,\n separator = (options && options.pathSeparator) || '.';\n if (typeof filter == 'string') {\n this._filter = FilterBase.stringFilter(filter, separator);\n } else if (typeof filter == 'function') {\n this._filter = filter;\n } else if (filter instanceof RegExp) {\n this._filter = FilterBase.regExpFilter(filter, separator);\n }\n\n const replacement = options && options.replacement;\n if (typeof replacement == 'function') {\n this._replacement = replacement;\n } else {\n this._replacement = FilterBase.arrayReplacement(replacement || FilterBase.defaultReplacement);\n }\n this._allowEmptyReplacement = options && options.allowEmptyReplacement;\n\n this._streamKeys = true;\n if (options) {\n 'streamValues' in options && (this._streamKeys = options.streamValues);\n 'streamKeys' in options && (this._streamKeys = options.streamKeys);\n }\n\n this._once = options && options.once;\n this._previousToken = '';\n }\n\n _check(chunk, _, callback) {\n // update the last stack key\n switch (chunk.name) {\n case 'startObject':\n case 'startArray':\n case 'startString':\n case 'startNumber':\n case 'nullValue':\n case 'trueValue':\n case 'falseValue':\n if (typeof this._stack[this._stack.length - 1] == 'number') {\n // array\n ++this._stack[this._stack.length - 1];\n }\n break;\n case 'keyValue':\n this._stack[this._stack.length - 1] = chunk.value;\n break;\n case 'numberValue':\n if (this._previousToken !== 'endNumber' && typeof this._stack[this._stack.length - 1] == 'number') {\n // array\n ++this._stack[this._stack.length - 1];\n }\n break;\n case 'stringValue':\n if (this._previousToken !== 'endString' && typeof this._stack[this._stack.length - 1] == 'number') {\n // array\n ++this._stack[this._stack.length - 1];\n }\n break;\n }\n this._previousToken = chunk.name;\n // check, if we allow a chunk\n if (this._checkChunk(chunk)) {\n return callback(null);\n }\n // update the stack\n switch (chunk.name) {\n case 'startObject':\n this._stack.push(null);\n break;\n case 'startArray':\n this._stack.push(-1);\n break;\n case 'endObject':\n case 'endArray':\n this._stack.pop();\n break;\n }\n callback(null);\n }\n\n _passObject(chunk, _, callback) {\n this.push(chunk);\n switch (chunk.name) {\n case 'startObject':\n case 'startArray':\n ++this._depth;\n break;\n case 'endObject':\n case 'endArray':\n --this._depth;\n break;\n }\n if (!this._depth) {\n this._transform = this._once ? this._skip : this._check;\n }\n callback(null);\n }\n\n _pass(chunk, _, callback) {\n this.push(chunk);\n callback(null);\n }\n\n _skipObject(chunk, _, callback) {\n switch (chunk.name) {\n case 'startObject':\n case 'startArray':\n ++this._depth;\n break;\n case 'endObject':\n case 'endArray':\n --this._depth;\n break;\n }\n if (!this._depth) {\n this._transform = this._once ? this._pass : this._check;\n }\n callback(null);\n }\n\n _skip(chunk, _, callback) {\n callback(null);\n }\n}\n\nFilterBase.defaultReplacement = [{name: 'nullValue', value: null}];\n\nconst passValue = (last, post) =>\n function(chunk, _, callback) {\n if (this._expected) {\n const expected = this._expected;\n this._expected = '';\n this._transform = this._once ? this._skip : this._check;\n if (expected === chunk.name) {\n this.push(chunk);\n } else {\n return this._transform(chunk, _, callback);\n }\n } else {\n this.push(chunk);\n if (chunk.name === last) {\n this._expected = post;\n }\n }\n callback(null);\n };\n\nFilterBase.prototype._passNumber = passValue('endNumber', 'numberValue');\nFilterBase.prototype._passString = passValue('endString', 'stringValue');\nFilterBase.prototype._passKey = passValue('endKey', 'keyValue');\n\nconst skipValue = (last, post) =>\n function(chunk, _, callback) {\n if (this._expected) {\n const expected = this._expected;\n this._expected = '';\n this._transform = this._once ? this._pass : this._check;\n if (expected !== chunk.name) {\n return this._transform(chunk, _, callback);\n }\n } else {\n if (chunk.name === last) {\n this._expected = post;\n }\n }\n callback(null);\n };\n\nFilterBase.prototype._skipNumber = skipValue('endNumber', 'numberValue');\nFilterBase.prototype._skipString = skipValue('endString', 'stringValue');\nFilterBase.prototype._skipKey = skipValue('endKey', 'keyValue');\n\nmodule.exports = FilterBase;\n","'use strict';\n\nconst {Readable, Writable, Duplex, Transform} = require('stream');\n\nconst none = Symbol.for('object-stream.none');\nconst finalSymbol = Symbol.for('object-stream.final');\nconst manySymbol = Symbol.for('object-stream.many');\n\nconst final = value => ({[finalSymbol]: value});\nconst many = values => ({[manySymbol]: values});\n\nconst isFinal = o => o && typeof o == 'object' && finalSymbol in o;\nconst isMany = o => o && typeof o == 'object' && manySymbol in o;\n\nconst getFinalValue = o => o[finalSymbol];\nconst getManyValues = o => o[manySymbol];\n\nconst runAsyncGenerator = async (gen, stream) => {\n for (;;) {\n let data = gen.next();\n if (data && typeof data.then == 'function') {\n data = await data;\n }\n if (data.done) break;\n let value = data.value;\n if (value && typeof value.then == 'function') {\n value = await value;\n }\n Chain.sanitize(value, stream);\n }\n};\n\nconst wrapFunction = fn =>\n new Transform({\n writableObjectMode: true,\n readableObjectMode: true,\n transform(chunk, encoding, callback) {\n try {\n const result = fn.call(this, chunk, encoding);\n if (result && typeof result.then == 'function') {\n // thenable\n result.then(\n result => (Chain.sanitize(result, this), callback(null)),\n error => callback(error)\n );\n return;\n }\n if (result && typeof result.next == 'function') {\n // generator\n runAsyncGenerator(result, this).then(\n () => callback(null),\n error => callback(error)\n );\n return;\n }\n Chain.sanitize(result, this);\n callback(null);\n } catch (error) {\n callback(error);\n }\n }\n });\n\nconst wrapArray = fns =>\n new Transform({\n writableObjectMode: true,\n readableObjectMode: true,\n transform(chunk, encoding, callback) {\n try {\n let value = chunk;\n for (let i = 0; i < fns.length; ++i) {\n const result = fns[i].call(this, value, encoding);\n if (result === Chain.none) {\n callback(null);\n return;\n }\n if (Chain.isFinal(result)) {\n value = Chain.getFinalValue(result);\n break;\n }\n value = result;\n }\n Chain.sanitize(value, this);\n callback(null);\n } catch (error) {\n callback(error);\n }\n }\n });\n\n// is*NodeStream functions taken from https://github.com/nodejs/node/blob/master/lib/internal/streams/utils.js\nconst isReadableNodeStream = obj =>\n obj &&\n typeof obj.pipe === 'function' &&\n typeof obj.on === 'function' &&\n (!obj._writableState || (typeof obj._readableState === 'object' ? obj._readableState.readable : null) !== false) && // Duplex\n (!obj._writableState || obj._readableState); // Writable has .pipe.\n\nconst isWritableNodeStream = obj =>\n obj &&\n typeof obj.write === 'function' &&\n typeof obj.on === 'function' &&\n (!obj._readableState || (typeof obj._writableState === 'object' ? obj._writableState.writable : null) !== false); // Duplex\n\nconst isDuplexNodeStream = obj =>\n obj && typeof obj.pipe === 'function' && obj._readableState && typeof obj.on === 'function' && typeof obj.write === 'function';\n\nclass Chain extends Duplex {\n constructor(fns, options) {\n super(options || {writableObjectMode: true, readableObjectMode: true});\n\n if (!(fns instanceof Array) || !fns.length) {\n throw Error(\"Chain's argument should be a non-empty array.\");\n }\n\n this.streams = fns\n .filter(fn => fn)\n .map((fn, index, fns) => {\n if (typeof fn === 'function' || fn instanceof Array) return Chain.convertToTransform(fn);\n if (isDuplexNodeStream(fn) || (!index && isReadableNodeStream(fn)) || (index === fns.length - 1 && isWritableNodeStream(fn))) {\n return fn;\n }\n throw Error('Arguments should be functions, arrays or streams.');\n })\n .filter(s => s);\n this.input = this.streams[0];\n this.output = this.streams.reduce((output, stream) => (output && output.pipe(stream)) || stream);\n\n if (!isWritableNodeStream(this.input)) {\n this._write = (_1, _2, callback) => callback(null);\n this._final = callback => callback(null); // unavailable in Node 6\n this.input.on('end', () => this.end());\n }\n\n if (isReadableNodeStream(this.output)) {\n this.output.on('data', chunk => !this.push(chunk) && this.output.pause());\n this.output.on('end', () => this.push(null));\n } else {\n this._read = () => {}; // nop\n this.resume();\n this.output.on('finish', () => this.push(null));\n }\n\n // connect events\n if (!options || !options.skipEvents) {\n this.streams.forEach(stream => stream.on('error', error => this.emit('error', error)));\n }\n }\n _write(chunk, encoding, callback) {\n let error = null;\n try {\n this.input.write(chunk, encoding, e => callback(e || error));\n } catch (e) {\n error = e;\n }\n }\n _final(callback) {\n let error = null;\n try {\n this.input.end(null, null, e => callback(e || error));\n } catch (e) {\n error = e;\n }\n }\n _read() {\n this.output.resume();\n }\n static make(fns, options) {\n return new Chain(fns, options);\n }\n static sanitize(result, stream) {\n if (Chain.isFinal(result)) {\n result = Chain.getFinalValue(result);\n } else if (Chain.isMany(result)) {\n result = Chain.getManyValues(result);\n }\n if (result !== undefined && result !== null && result !== Chain.none) {\n if (result instanceof Array) {\n result.forEach(value => value !== undefined && value !== null && stream.push(value));\n } else {\n stream.push(result);\n }\n }\n }\n static convertToTransform(fn) {\n if (typeof fn === 'function') return wrapFunction(fn);\n if (fn instanceof Array) return fn.length ? wrapArray(fn) : null;\n return null;\n }\n}\n\nChain.none = none;\nChain.final = final;\nChain.isFinal = isFinal;\nChain.getFinalValue = getFinalValue;\nChain.many = many;\nChain.isMany = isMany;\nChain.getManyValues = getManyValues;\n\nChain.chain = Chain.make;\nChain.make.Constructor = Chain;\n\nmodule.exports = Chain;\n","'use strict';\n\nconst {chain} = require('stream-chain');\n\nconst Parser = require('../Parser');\n\nconst withParser = (fn, options) =>\n chain([new Parser(options), fn(options)], Object.assign({}, options, {writableObjectMode: false, readableObjectMode: true}));\n\nmodule.exports = withParser;\n","'use strict';\n\nconst FilterBase = require('./FilterBase');\nconst withParser = require('../utils/withParser');\n\nclass Pick extends FilterBase {\n static make(options) {\n return new Pick(options);\n }\n\n static withParser(options) {\n return withParser(Pick.make, options);\n }\n\n _checkChunk(chunk) {\n switch (chunk.name) {\n case 'startObject':\n case 'startArray':\n if (this._filter(this._stack, chunk)) {\n this.push(chunk);\n this._transform = this._passObject;\n this._depth = 1;\n return true;\n }\n break;\n case 'startString':\n if (this._filter(this._stack, chunk)) {\n this.push(chunk);\n this._transform = this._passString;\n return true;\n }\n break;\n case 'startNumber':\n if (this._filter(this._stack, chunk)) {\n this.push(chunk);\n this._transform = this._passNumber;\n return true;\n }\n break;\n case 'nullValue':\n case 'trueValue':\n case 'falseValue':\n case 'stringValue':\n case 'numberValue':\n if (this._filter(this._stack, chunk)) {\n this.push(chunk);\n this._transform = this._once ? this._skip : this._check;\n return true;\n }\n break;\n }\n return false;\n }\n}\nPick.pick = Pick.make;\nPick.make.Constructor = Pick;\n\nmodule.exports = Pick;\n","'use strict';\n\nconst EventEmitter = require('events');\n\nconst startObject = Ctr =>\n function () {\n if (this.done) {\n this.done = false;\n } else {\n this.stack.push(this.current, this.key);\n }\n this.current = new Ctr();\n this.key = null;\n };\n\nclass Assembler extends EventEmitter {\n static connectTo(stream, options) {\n return new Assembler(options).connectTo(stream);\n }\n\n constructor(options) {\n super();\n this.stack = [];\n this.current = this.key = null;\n this.done = true;\n if (options) {\n this.reviver = typeof options.reviver == 'function' && options.reviver;\n if (this.reviver) {\n this.stringValue = this._saveValue = this._saveValueWithReviver;\n }\n if (options.numberAsString) {\n this.numberValue = this.stringValue;\n }\n }\n }\n\n connectTo(stream) {\n stream.on('data', chunk => {\n if (this[chunk.name]) {\n this[chunk.name](chunk.value);\n if (this.done) this.emit('done', this);\n }\n });\n return this;\n }\n\n get depth() {\n return (this.stack.length >> 1) + (this.done ? 0 : 1);\n }\n\n get path() {\n const path = [];\n for (let i = 0; i < this.stack.length; i += 2) {\n const key = this.stack[i + 1];\n path.push(key === null ? this.stack[i].length : key);\n }\n return path;\n }\n\n dropToLevel(level) {\n if (level < this.depth) {\n if (level) {\n const index = (level - 1) << 1;\n this.current = this.stack[index];\n this.key = this.stack[index + 1];\n this.stack.splice(index);\n } else {\n this.stack = [];\n this.current = this.key = null;\n this.done = true;\n }\n }\n return this;\n }\n\n consume(chunk) {\n this[chunk.name] && this[chunk.name](chunk.value);\n return this;\n }\n\n keyValue(value) {\n this.key = value;\n }\n\n //stringValue() - aliased below to _saveValue()\n\n numberValue(value) {\n this._saveValue(parseFloat(value));\n }\n nullValue() {\n this._saveValue(null);\n }\n trueValue() {\n this._saveValue(true);\n }\n falseValue() {\n this._saveValue(false);\n }\n\n //startObject() - assigned below\n\n endObject() {\n if (this.stack.length) {\n const value = this.current;\n this.key = this.stack.pop();\n this.current = this.stack.pop();\n this._saveValue(value);\n } else {\n this.done = true;\n }\n }\n\n //startArray() - assigned below\n //endArray() - aliased below to endObject()\n\n _saveValue(value) {\n if (this.done) {\n this.current = value;\n } else {\n if (this.current instanceof Array) {\n this.current.push(value);\n } else {\n this.current[this.key] = value;\n this.key = null;\n }\n }\n }\n _saveValueWithReviver(value) {\n if (this.done) {\n this.current = this.reviver('', value);\n } else {\n if (this.current instanceof Array) {\n value = this.reviver('' + this.current.length, value);\n this.current.push(value);\n if (value === undefined) {\n delete this.current[this.current.length - 1];\n }\n } else {\n value = this.reviver(this.key, value);\n if (value !== undefined) {\n this.current[this.key] = value;\n }\n this.key = null;\n }\n }\n }\n}\n\nAssembler.prototype.stringValue = Assembler.prototype._saveValue;\nAssembler.prototype.startObject = startObject(Object);\nAssembler.prototype.startArray = startObject(Array);\nAssembler.prototype.endArray = Assembler.prototype.endObject;\n\nmodule.exports = Assembler;\n","'use strict';\n\nconst {Transform} = require('stream');\nconst Assembler = require('../Assembler');\n\nclass Counter {\n constructor(initialDepth) {\n this.depth = initialDepth;\n }\n startObject() {\n ++this.depth;\n }\n endObject() {\n --this.depth;\n }\n startArray() {\n ++this.depth;\n }\n endArray() {\n --this.depth;\n }\n}\n\nclass StreamBase extends Transform {\n constructor(options) {\n super(Object.assign({}, options, {writableObjectMode: true, readableObjectMode: true}));\n if (options) {\n this.objectFilter = options.objectFilter;\n this.includeUndecided = options.includeUndecided;\n }\n if (typeof this.objectFilter != 'function') {\n this._filter = this._transform;\n }\n this._transform = this._wait || this._filter;\n this._assembler = new Assembler(options);\n }\n\n _transform(chunk, encoding, callback) {\n if (this._assembler[chunk.name]) {\n this._assembler[chunk.name](chunk.value);\n if (this._assembler.depth === this._level) {\n this._push();\n }\n }\n callback(null);\n }\n\n _filter(chunk, encoding, callback) {\n if (this._assembler[chunk.name]) {\n this._assembler[chunk.name](chunk.value);\n const result = this.objectFilter(this._assembler);\n if (result) {\n if (this._assembler.depth === this._level) {\n this._push();\n this._transform = this._filter;\n }\n this._transform = this._accept;\n return callback(null);\n }\n if (result === false) {\n this._saved_assembler = this._assembler;\n this._assembler = new Counter(this._saved_assembler.depth);\n this._saved_assembler.dropToLevel(this._level);\n if (this._assembler.depth === this._level) {\n this._assembler = this._saved_assembler;\n this._transform = this._filter;\n }\n this._transform = this._reject;\n return callback(null);\n }\n if (this._assembler.depth === this._level) {\n this._push(!this.includeUndecided);\n }\n }\n callback(null);\n }\n\n _accept(chunk, encoding, callback) {\n if (this._assembler[chunk.name]) {\n this._assembler[chunk.name](chunk.value);\n if (this._assembler.depth === this._level) {\n this._push();\n this._transform = this._filter;\n }\n }\n callback(null);\n }\n\n _reject(chunk, encoding, callback) {\n if (this._assembler[chunk.name]) {\n this._assembler[chunk.name](chunk.value);\n if (this._assembler.depth === this._level) {\n this._assembler = this._saved_assembler;\n this._transform = this._filter;\n }\n }\n callback(null);\n }\n}\n\nmodule.exports = StreamBase;\n","'use strict';\n\nconst StreamBase = require('./StreamBase');\nconst withParser = require('../utils/withParser');\n\nclass StreamValues extends StreamBase {\n static make(options) {\n return new StreamValues(options);\n }\n\n static withParser(options) {\n return withParser(StreamValues.make, Object.assign({}, options, {jsonStreaming: true}));\n }\n\n constructor(options) {\n super(options);\n this._counter = 0;\n this._level = 0;\n }\n\n _push(discard) {\n if (discard) {\n ++this._counter;\n } else {\n this.push({key: this._counter++, value: this._assembler.current});\n }\n this._assembler.current = this._assembler.key = null;\n }\n}\nStreamValues.streamValues = StreamValues.make;\nStreamValues.make.Constructor = StreamValues;\n\nmodule.exports = StreamValues;\n","'use strict';\n\nconst {Readable, Writable, Duplex, Transform} = require('stream');\n\nconst none = Symbol.for('object-stream.none');\nconst finalSymbol = Symbol.for('object-stream.final');\nconst manySymbol = Symbol.for('object-stream.many');\n\nconst final = value => ({[finalSymbol]: value});\nconst many = values => ({[manySymbol]: values});\n\nconst isFinal = o => o && typeof o == 'object' && finalSymbol in o;\nconst isMany = o => o && typeof o == 'object' && manySymbol in o;\n\nconst getFinalValue = o => o[finalSymbol];\nconst getManyValues = o => o[manySymbol];\n\nconst runAsyncGenerator = async (gen, stream) => {\n for (;;) {\n let data = gen.next();\n if (data && typeof data.then == 'function') {\n data = await data;\n }\n if (data.done) break;\n let value = data.value;\n if (value && typeof value.then == 'function') {\n value = await value;\n }\n Chain.sanitize(value, stream);\n }\n};\n\nconst wrapFunction = fn =>\n new Transform({\n writableObjectMode: true,\n readableObjectMode: true,\n transform(chunk, encoding, callback) {\n try {\n const result = fn.call(this, chunk, encoding);\n if (result && typeof result.then == 'function') {\n // thenable\n result.then(\n result => (Chain.sanitize(result, this), callback(null)),\n error => callback(error)\n );\n return;\n }\n if (result && typeof result.next == 'function') {\n // generator\n runAsyncGenerator(result, this).then(\n () => callback(null),\n error => callback(error)\n );\n return;\n }\n Chain.sanitize(result, this);\n callback(null);\n } catch (error) {\n callback(error);\n }\n }\n });\n\nconst wrapArray = fns =>\n new Transform({\n writableObjectMode: true,\n readableObjectMode: true,\n transform(chunk, encoding, callback) {\n try {\n let value = chunk;\n for (let i = 0; i < fns.length; ++i) {\n const result = fns[i].call(this, value, encoding);\n if (result === Chain.none) {\n callback(null);\n return;\n }\n if (Chain.isFinal(result)) {\n value = Chain.getFinalValue(result);\n break;\n }\n value = result;\n }\n Chain.sanitize(value, this);\n callback(null);\n } catch (error) {\n callback(error);\n }\n }\n });\n\n// is*NodeStream functions taken from https://github.com/nodejs/node/blob/master/lib/internal/streams/utils.js\nconst isReadableNodeStream = obj =>\n obj &&\n typeof obj.pipe === 'function' &&\n typeof obj.on === 'function' &&\n (!obj._writableState || (typeof obj._readableState === 'object' ? obj._readableState.readable : null) !== false) && // Duplex\n (!obj._writableState || obj._readableState); // Writable has .pipe.\n\nconst isWritableNodeStream = obj =>\n obj &&\n typeof obj.write === 'function' &&\n typeof obj.on === 'function' &&\n (!obj._readableState || (typeof obj._writableState === 'object' ? obj._writableState.writable : null) !== false); // Duplex\n\nconst isDuplexNodeStream = obj =>\n obj && typeof obj.pipe === 'function' && obj._readableState && typeof obj.on === 'function' && typeof obj.write === 'function';\n\nclass Chain extends Duplex {\n constructor(fns, options) {\n super(options || {writableObjectMode: true, readableObjectMode: true});\n\n if (!(fns instanceof Array) || !fns.length) {\n throw Error(\"Chain's argument should be a non-empty array.\");\n }\n\n this.streams = fns\n .filter(fn => fn)\n .map((fn, index, fns) => {\n if (typeof fn === 'function' || fn instanceof Array) return Chain.convertToTransform(fn);\n if (isDuplexNodeStream(fn) || (!index && isReadableNodeStream(fn)) || (index === fns.length - 1 && isWritableNodeStream(fn))) {\n return fn;\n }\n throw Error('Arguments should be functions, arrays or streams.');\n })\n .filter(s => s);\n this.input = this.streams[0];\n this.output = this.streams.reduce((output, stream) => (output && output.pipe(stream)) || stream);\n\n if (!isWritableNodeStream(this.input)) {\n this._write = (_1, _2, callback) => callback(null);\n this._final = callback => callback(null); // unavailable in Node 6\n this.input.on('end', () => this.end());\n }\n\n if (isReadableNodeStream(this.output)) {\n this.output.on('data', chunk => !this.push(chunk) && this.output.pause());\n this.output.on('end', () => this.push(null));\n } else {\n this._read = () => {}; // nop\n this.resume();\n this.output.on('finish', () => this.push(null));\n }\n\n // connect events\n if (!options || !options.skipEvents) {\n this.streams.forEach(stream => stream.on('error', error => this.emit('error', error)));\n }\n }\n _write(chunk, encoding, callback) {\n let error = null;\n try {\n this.input.write(chunk, encoding, e => callback(e || error));\n } catch (e) {\n error = e;\n }\n }\n _final(callback) {\n let error = null;\n try {\n this.input.end(null, null, e => callback(e || error));\n } catch (e) {\n error = e;\n }\n }\n _read() {\n this.output.resume();\n }\n static make(fns, options) {\n return new Chain(fns, options);\n }\n static sanitize(result, stream) {\n if (Chain.isFinal(result)) {\n result = Chain.getFinalValue(result);\n } else if (Chain.isMany(result)) {\n result = Chain.getManyValues(result);\n }\n if (result !== undefined && result !== null && result !== Chain.none) {\n if (result instanceof Array) {\n result.forEach(value => value !== undefined && value !== null && stream.push(value));\n } else {\n stream.push(result);\n }\n }\n }\n static convertToTransform(fn) {\n if (typeof fn === 'function') return wrapFunction(fn);\n if (fn instanceof Array) return fn.length ? wrapArray(fn) : null;\n return null;\n }\n}\n\nChain.none = none;\nChain.final = final;\nChain.isFinal = isFinal;\nChain.getFinalValue = getFinalValue;\nChain.many = many;\nChain.isMany = isMany;\nChain.getManyValues = getManyValues;\n\nChain.chain = Chain.make;\nChain.make.Constructor = Chain;\n\nmodule.exports = Chain;\n","module.exports = Pend;\n\nfunction Pend() {\n this.pending = 0;\n this.max = Infinity;\n this.listeners = [];\n this.waiting = [];\n this.error = null;\n}\n\nPend.prototype.go = function(fn) {\n if (this.pending < this.max) {\n pendGo(this, fn);\n } else {\n this.waiting.push(fn);\n }\n};\n\nPend.prototype.wait = function(cb) {\n if (this.pending === 0) {\n cb(this.error);\n } else {\n this.listeners.push(cb);\n }\n};\n\nPend.prototype.hold = function() {\n return pendHold(this);\n};\n\nfunction pendHold(self) {\n self.pending += 1;\n var called = false;\n return onCb;\n function onCb(err) {\n if (called) throw new Error(\"callback called twice\");\n called = true;\n self.error = self.error || err;\n self.pending -= 1;\n if (self.waiting.length > 0 && self.pending < self.max) {\n pendGo(self, self.waiting.shift());\n } else if (self.pending === 0) {\n var listeners = self.listeners;\n self.listeners = [];\n listeners.forEach(cbListener);\n }\n }\n function cbListener(listener) {\n listener(self.error);\n }\n}\n\nfunction pendGo(self, fn) {\n fn(pendHold(self));\n}\n","// This was adapted from https://github.com/andrewrk/node-fd-slicer by Andrew Kelley under the MIT License.\nvar fs = require('fs');\nvar util = require('util');\nvar stream = require('stream');\nvar Readable = stream.Readable;\nvar PassThrough = stream.PassThrough;\nvar Pend = require('pend');\nvar EventEmitter = require('events').EventEmitter;\n\nexports.BufferSlicer = BufferSlicer;\nexports.FdSlicer = FdSlicer;\n\nutil.inherits(FdSlicer, EventEmitter);\nfunction FdSlicer(fd) {\n EventEmitter.call(this);\n\n this.fd = fd;\n this.pend = new Pend();\n this.pend.max = 1;\n this.refCount = 0;\n}\n\nFdSlicer.prototype.read = function(buffer, offset, length, position, callback) {\n var self = this;\n self.pend.go(function(cb) {\n fs.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer) {\n cb();\n callback(err, bytesRead, buffer);\n });\n });\n};\n\nFdSlicer.prototype.createReadStream = function(options) {\n return new ReadStream(this, options);\n};\n\nFdSlicer.prototype.ref = function() {\n this.refCount += 1;\n};\n\nFdSlicer.prototype.unref = function() {\n var self = this;\n self.refCount -= 1;\n if (self.refCount < 0) throw new Error(\"invalid unref\");\n if (self.refCount > 0) return;\n\n fs.close(self.fd, onCloseDone);\n\n function onCloseDone(err) {\n if (err) {\n self.emit('error', err);\n } else {\n self.emit('close');\n }\n }\n};\n\nutil.inherits(ReadStream, Readable);\nfunction ReadStream(context, options) {\n options = options || {};\n Readable.call(this, options);\n\n this.context = context;\n this.context.ref();\n\n this.start = options.start || 0;\n this.endOffset = options.end;\n this.pos = this.start;\n}\n\nReadStream.prototype._read = function(n) {\n var self = this;\n\n var toRead = Math.min(self._readableState.highWaterMark, n);\n if (self.endOffset != null) {\n toRead = Math.min(toRead, self.endOffset - self.pos);\n }\n if (toRead <= 0) {\n self.push(null);\n this._cleanup();\n return;\n }\n self.context.pend.go(function(cb) {\n var buffer = Buffer.allocUnsafe(toRead);\n fs.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {\n if (err) {\n self.destroy(err);\n } else if (bytesRead === 0) {\n self.push(null);\n self._cleanup();\n } else {\n self.pos += bytesRead;\n self.push(buffer.slice(0, bytesRead));\n }\n cb();\n });\n });\n};\n\nReadStream.prototype._destroy = function(err, cb) {\n // Node 14+ calls this automatically at EOF.\n this._cleanup();\n cb(err);\n};\n\nReadStream.prototype._cleanup = function() {\n if (this.context != null) {\n this.context.unref();\n this.context = null;\n }\n};\n\nutil.inherits(BufferSlicer, EventEmitter);\nfunction BufferSlicer(buffer) {\n EventEmitter.call(this);\n\n this.refCount = 0;\n this.buffer = buffer;\n}\n\nBufferSlicer.prototype.read = function(buffer, offset, length, position, callback) {\n if (!(0 <= offset && offset <= buffer.length)) throw new RangeError(\"offset outside buffer: 0 <= \" + offset + \" <= \" + buffer.length);\n if (position < 0) throw new RangeError(\"position is negative: \" + position);\n if (offset + length > buffer.length) {\n // The caller's buffer can't hold all the bytes they're trying to read.\n // Clamp the length instead of giving an error.\n // The callback will be informed of fewer than expected bytes written.\n length = buffer.length - offset;\n }\n if (position + length > this.buffer.length) {\n // Clamp any attempt to read past the end of the source buffer.\n length = this.buffer.length - position;\n }\n if (length <= 0) {\n // After any clamping, we're fully out of bounds or otherwise have nothing to do.\n // This isn't an error; it's just zero bytes written.\n setImmediate(function() {\n callback(null, 0);\n });\n return;\n }\n this.buffer.copy(buffer, offset, position, position + length);\n setImmediate(function() {\n callback(null, length);\n });\n};\n\nBufferSlicer.prototype.createReadStream = function(options) {\n options = options || {};\n var readStream = new PassThrough(options);\n readStream.start = options.start || 0;\n readStream.endOffset = options.end;\n // by the time this function returns, we'll be done.\n readStream.pos = readStream.endOffset || this.buffer.length;\n\n var entireSlice = this.buffer.slice(readStream.start, readStream.pos);\n // Cut the buffer into smaller slices for better memory usage when streaming into a zlib inflate stream.\n // See https://github.com/thejoshwolfe/yauzl/issues/87\n var maxChunkSize = 0x10000;\n var offset = 0;\n while (true) {\n var nextOffset = offset + maxChunkSize;\n if (nextOffset >= entireSlice.length) {\n // last chunk\n if (offset < entireSlice.length) {\n readStream.write(entireSlice.slice(offset, entireSlice.length));\n }\n break;\n }\n readStream.write(entireSlice.slice(offset, nextOffset));\n offset = nextOffset;\n }\n\n readStream.end();\n return readStream;\n};\n\nBufferSlicer.prototype.ref = function() {\n this.refCount += 1;\n};\n\nBufferSlicer.prototype.unref = function() {\n this.refCount -= 1;\n\n if (this.refCount < 0) {\n throw new Error(\"invalid unref\");\n }\n};\n","// Adapted from https://github.com/brianloveswords/buffer-crc32/blob/v0.2.13/index.js\nconst CRC_TABLE = new Int32Array([\n 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,\n 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,\n 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,\n 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\n 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,\n 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\n 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,\n 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,\n 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,\n 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,\n 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,\n 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,\n 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,\n 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,\n 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,\n 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,\n 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,\n 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,\n 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,\n 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,\n 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,\n 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,\n]);\n\nfunction crc32(buf) {\n let crc = -1;\n for (let x of buf) {\n crc = CRC_TABLE[(crc ^ x) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ -1) >>> 0;\n}\n\nmodule.exports = crc32;\n","var fs = require(\"fs\");\nvar zlib = require(\"zlib\");\nvar fd_slicer = require(\"./fd-slicer\");\nvar util = require(\"util\");\nvar EventEmitter = require(\"events\").EventEmitter;\nvar Transform = require(\"stream\").Transform;\nvar PassThrough = require(\"stream\").PassThrough;\nvar Writable = require(\"stream\").Writable;\n\n// Node 20 added zlib.crc32.\nconst crc32 = typeof zlib.crc32 === \"function\" ? zlib.crc32 : require(\"./crc32\");\n\nexports.open = open;\nexports.fromFd = fromFd;\nexports.fromBuffer = fromBuffer;\nexports.fromRandomAccessReader = fromRandomAccessReader;\nexports.openPromise = openPromise;\nexports.fromFdPromise = fromFdPromise;\nexports.fromBufferPromise = fromBufferPromise;\nexports.fromRandomAccessReaderPromise = fromRandomAccessReaderPromise;\n\nexports.dosDateTimeToDate = dosDateTimeToDate;\nexports.getFileNameLowLevel = getFileNameLowLevel;\nexports.validateFileName = validateFileName;\nexports.parseExtraFields = parseExtraFields;\nexports.ZipFile = ZipFile;\nexports.Entry = Entry;\nexports.LocalFileHeader = LocalFileHeader;\nexports.RandomAccessReader = RandomAccessReader;\n\nfunction openPromise(path, options) {\n return new Promise((resolve, reject) => {\n open(path, {...options, lazyEntries: true}, function(err, zipfile) {\n if (err) return reject(err);\n resolve(zipfile);\n });\n });\n}\nfunction fromFdPromise(fd, options) {\n return new Promise((resolve, reject) => {\n fromFd(fd, {...options, lazyEntries: true}, function(err, zipfile) {\n if (err) return reject(err);\n resolve(zipfile);\n });\n });\n}\nfunction fromBufferPromise(buffer, options) {\n return new Promise((resolve, reject) => {\n fromBuffer(buffer, {...options, lazyEntries: true}, function(err, zipfile) {\n if (err) return reject(err);\n resolve(zipfile);\n });\n });\n}\nfunction fromRandomAccessReaderPromise(reader, totalSize, options) {\n return new Promise((resolve, reject) => {\n fromRandomAccessReader(reader, totalSize, {...options, lazyEntries: true}, function(err, zipfile) {\n if (err) return reject(err);\n resolve(zipfile);\n });\n });\n}\n\nfunction open(path, options, callback) {\n if (typeof options === \"function\") {\n callback = options;\n options = null;\n }\n if (options == null) options = {};\n if (options.autoClose == null) options.autoClose = true;\n if (options.lazyEntries == null) options.lazyEntries = false;\n if (options.decodeStrings == null) options.decodeStrings = true;\n if (options.validateEntrySizes == null) options.validateEntrySizes = true;\n if (options.strictFileNames == null) options.strictFileNames = false;\n if (callback == null) callback = defaultCallback;\n fs.open(path, \"r\", function(err, fd) {\n if (err) return callback(err);\n fromFd(fd, options, function(err, zipfile) {\n if (err) fs.close(fd, defaultCallback);\n callback(err, zipfile);\n });\n });\n}\n\nfunction fromFd(fd, options, callback) {\n if (typeof options === \"function\") {\n callback = options;\n options = null;\n }\n if (options == null) options = {};\n if (options.autoClose == null) options.autoClose = false;\n if (options.lazyEntries == null) options.lazyEntries = false;\n if (options.decodeStrings == null) options.decodeStrings = true;\n if (options.validateEntrySizes == null) options.validateEntrySizes = true;\n if (options.strictFileNames == null) options.strictFileNames = false;\n if (callback == null) callback = defaultCallback;\n fs.fstat(fd, function(err, stats) {\n if (err) return callback(err);\n var reader = new fd_slicer.FdSlicer(fd);\n fromRandomAccessReader(reader, stats.size, options, callback);\n });\n}\n\nfunction fromBuffer(buffer, options, callback) {\n if (typeof options === \"function\") {\n callback = options;\n options = null;\n }\n if (options == null) options = {};\n options.autoClose = false;\n if (options.lazyEntries == null) options.lazyEntries = false;\n if (options.decodeStrings == null) options.decodeStrings = true;\n if (options.validateEntrySizes == null) options.validateEntrySizes = true;\n if (options.strictFileNames == null) options.strictFileNames = false;\n var reader = new fd_slicer.BufferSlicer(buffer);\n fromRandomAccessReader(reader, buffer.length, options, callback);\n}\n\nfunction fromRandomAccessReader(reader, totalSize, options, callback) {\n if (typeof options === \"function\") {\n callback = options;\n options = null;\n }\n if (options == null) options = {};\n if (options.autoClose == null) options.autoClose = true;\n if (options.lazyEntries == null) options.lazyEntries = false;\n if (options.decodeStrings == null) options.decodeStrings = true;\n var decodeStrings = !!options.decodeStrings;\n if (options.validateEntrySizes == null) options.validateEntrySizes = true;\n if (options.strictFileNames == null) options.strictFileNames = false;\n if (callback == null) callback = defaultCallback;\n if (typeof totalSize !== \"number\") throw new Error(\"expected totalSize parameter to be a number\");\n if (totalSize > Number.MAX_SAFE_INTEGER) {\n throw new Error(\"zip file too large. only file sizes up to 2^52 are supported due to JavaScript's Number type being an IEEE 754 double.\");\n }\n\n // the matching unref() call is in zipfile.close()\n reader.ref();\n\n // eocdr means End of Central Directory Record.\n // search backwards for the eocdr signature.\n // the last field of the eocdr is a variable-length comment.\n // the comment size is encoded in a 2-byte field in the eocdr, which we can't find without trudging backwards through the comment to find it.\n // as a consequence of this design decision, it's possible to have ambiguous zip file metadata if a coherent eocdr was in the comment.\n // we search backwards for a eocdr signature, and hope that whoever made the zip file was smart enough to forbid the eocdr signature in the comment.\n var eocdrWithoutCommentSize = 22;\n var zip64EocdlSize = 20; // Zip64 end of central directory locator\n var maxCommentSize = 0xffff; // 2-byte size\n var bufferSize = Math.min(zip64EocdlSize + eocdrWithoutCommentSize + maxCommentSize, totalSize);\n var buffer = newBuffer(bufferSize);\n var bufferReadStart = totalSize - buffer.length;\n readAndAssertNoEof(reader, buffer, 0, bufferSize, bufferReadStart, function(err) {\n if (err) return callback(err);\n for (var i = bufferSize - eocdrWithoutCommentSize; i >= 0; i -= 1) {\n if (buffer.readUInt32LE(i) !== 0x06054b50) continue;\n // found eocdr\n var eocdrBuffer = buffer.subarray(i);\n\n // 0 - End of central directory signature = 0x06054b50\n // 4 - Number of this disk\n var diskNumber = eocdrBuffer.readUInt16LE(4);\n // 6 - Disk where central directory starts\n // 8 - Number of central directory records on this disk\n // 10 - Total number of central directory records\n var entryCount = eocdrBuffer.readUInt16LE(10);\n // 12 - Size of central directory (bytes)\n // 16 - Offset of start of central directory, relative to start of archive\n var centralDirectoryOffset = eocdrBuffer.readUInt32LE(16);\n // 20 - Comment length\n var commentLength = eocdrBuffer.readUInt16LE(20);\n var expectedCommentLength = eocdrBuffer.length - eocdrWithoutCommentSize;\n if (commentLength !== expectedCommentLength) {\n return callback(new Error(\"Invalid comment length. Expected: \" + expectedCommentLength + \". Found: \" + commentLength + \". Are there extra bytes at the end of the file? Or is the end of central dir signature `PK☺☻` in the comment?\"));\n }\n // 22 - Comment\n // the encoding is always cp437.\n var comment = decodeStrings ? decodeBuffer(eocdrBuffer.subarray(22), false)\n : eocdrBuffer.subarray(22);\n\n // Look for a Zip64 end of central directory locator\n if (i - zip64EocdlSize >= 0 && buffer.readUInt32LE(i - zip64EocdlSize) === 0x07064b50) {\n // ZIP64 format\n var zip64EocdlBuffer = buffer.subarray(i - zip64EocdlSize, i - zip64EocdlSize + zip64EocdlSize);\n // 0 - zip64 end of central dir locator signature = 0x07064b50\n // 4 - number of the disk with the start of the zip64 end of central directory\n // 8 - relative offset of the zip64 end of central directory record\n var zip64EocdrOffset = readUInt64LE(zip64EocdlBuffer, 8);\n // 16 - total number of disks\n\n // ZIP64 end of central directory record\n var zip64EocdrBuffer = newBuffer(56);\n return readAndAssertNoEof(reader, zip64EocdrBuffer, 0, zip64EocdrBuffer.length, zip64EocdrOffset, function(err) {\n if (err) return callback(err);\n\n // 0 - zip64 end of central dir signature 4 bytes (0x06064b50)\n if (zip64EocdrBuffer.readUInt32LE(0) !== 0x06064b50) {\n return callback(new Error(\"invalid zip64 end of central directory record signature\"));\n }\n // 4 - size of zip64 end of central directory record 8 bytes\n // 12 - version made by 2 bytes\n // 14 - version needed to extract 2 bytes\n // 16 - number of this disk 4 bytes\n diskNumber = zip64EocdrBuffer.readUInt32LE(16);\n if (diskNumber !== 0) {\n // Check this only after zip64 overrides. See #118.\n return callback(new Error(\"multi-disk zip files are not supported: found disk number: \" + diskNumber));\n }\n // 20 - number of the disk with the start of the central directory 4 bytes\n // 24 - total number of entries in the central directory on this disk 8 bytes\n // 32 - total number of entries in the central directory 8 bytes\n entryCount = readUInt64LE(zip64EocdrBuffer, 32);\n // 40 - size of the central directory 8 bytes\n // 48 - offset of start of central directory with respect to the starting disk number 8 bytes\n centralDirectoryOffset = readUInt64LE(zip64EocdrBuffer, 48);\n // 56 - zip64 extensible data sector (variable size)\n return callback(null, new ZipFile(reader, centralDirectoryOffset, totalSize, entryCount, comment, options.autoClose, options.lazyEntries, decodeStrings, options.validateEntrySizes, options.strictFileNames));\n });\n }\n\n // Not ZIP64 format\n if (diskNumber !== 0) {\n return callback(new Error(\"multi-disk zip files are not supported: found disk number: \" + diskNumber));\n }\n return callback(null, new ZipFile(reader, centralDirectoryOffset, totalSize, entryCount, comment, options.autoClose, options.lazyEntries, decodeStrings, options.validateEntrySizes, options.strictFileNames));\n\n }\n\n // Not a zip file.\n callback(new Error(\"End of central directory record signature not found. Either not a zip file, or file is truncated.\"));\n });\n}\n\nutil.inherits(ZipFile, EventEmitter);\nfunction ZipFile(reader, centralDirectoryOffset, fileSize, entryCount, comment, autoClose, lazyEntries, decodeStrings, validateEntrySizes, strictFileNames) {\n var self = this;\n EventEmitter.call(self);\n self.reader = reader;\n // forward close events\n self.reader.on(\"error\", function(err) {\n // error closing the fd\n emitError(self, err);\n });\n self.reader.once(\"close\", function() {\n self.emit(\"close\");\n });\n self.readEntryCursor = centralDirectoryOffset;\n self.fileSize = fileSize;\n self.entryCount = entryCount;\n self.comment = comment;\n self.entriesRead = 0;\n self.autoClose = !!autoClose;\n self.lazyEntries = !!lazyEntries;\n self.decodeStrings = !!decodeStrings;\n self.validateEntrySizes = !!validateEntrySizes;\n self.strictFileNames = !!strictFileNames;\n self.isOpen = true;\n self.emittedError = false;\n self.hasEachEntryBeenCalled = false;\n\n if (!self.lazyEntries) self._readEntry();\n}\nZipFile.prototype.close = function() {\n if (!this.isOpen) return;\n this.isOpen = false;\n this.reader.unref();\n};\n\nfunction emitErrorAndAutoClose(self, err) {\n if (self.autoClose) self.close();\n emitError(self, err);\n}\nfunction emitError(self, err) {\n if (self.emittedError) return;\n self.emittedError = true;\n self.emit(\"error\", err);\n}\n\nZipFile.prototype.readEntry = function() {\n if (!this.lazyEntries) throw new Error(\"readEntry() called without lazyEntries:true\");\n this._readEntry();\n};\nZipFile.prototype._readEntry = function() {\n var self = this;\n if (self.entryCount === self.entriesRead) {\n // done with metadata\n setImmediate(function() {\n if (self.autoClose) self.close();\n if (self.emittedError) return;\n self.emit(\"end\");\n });\n return;\n }\n if (self.emittedError) return;\n var buffer = newBuffer(46);\n readAndAssertNoEof(self.reader, buffer, 0, buffer.length, self.readEntryCursor, function(err) {\n if (err) return emitErrorAndAutoClose(self, err);\n if (self.emittedError) return;\n var entry = new Entry();\n // 0 - Central directory file header signature\n var signature = buffer.readUInt32LE(0);\n if (signature !== 0x02014b50) return emitErrorAndAutoClose(self, new Error(\"invalid central directory file header signature: 0x\" + signature.toString(16)));\n // 4 - Version made by\n entry.versionMadeBy = buffer.readUInt16LE(4);\n // 6 - Version needed to extract (minimum)\n entry.versionNeededToExtract = buffer.readUInt16LE(6);\n // 8 - General purpose bit flag\n entry.generalPurposeBitFlag = buffer.readUInt16LE(8);\n // 10 - Compression method\n entry.compressionMethod = buffer.readUInt16LE(10);\n // 12 - File last modification time\n entry.lastModFileTime = buffer.readUInt16LE(12);\n // 14 - File last modification date\n entry.lastModFileDate = buffer.readUInt16LE(14);\n // 16 - CRC-32\n entry.crc32 = buffer.readUInt32LE(16);\n // 20 - Compressed size\n entry.compressedSize = buffer.readUInt32LE(20);\n // 24 - Uncompressed size\n entry.uncompressedSize = buffer.readUInt32LE(24);\n // 28 - File name length (n)\n entry.fileNameLength = buffer.readUInt16LE(28);\n // 30 - Extra field length (m)\n entry.extraFieldLength = buffer.readUInt16LE(30);\n // 32 - File comment length (k)\n entry.fileCommentLength = buffer.readUInt16LE(32);\n // 34 - Disk number where file starts\n // 36 - Internal file attributes\n entry.internalFileAttributes = buffer.readUInt16LE(36);\n // 38 - External file attributes\n entry.externalFileAttributes = buffer.readUInt32LE(38);\n // 42 - Relative offset of local file header\n entry.relativeOffsetOfLocalHeader = buffer.readUInt32LE(42);\n\n if (entry.generalPurposeBitFlag & 0x40) return emitErrorAndAutoClose(self, new Error(\"strong encryption is not supported\"));\n\n self.readEntryCursor += 46;\n\n buffer = newBuffer(entry.fileNameLength + entry.extraFieldLength + entry.fileCommentLength);\n readAndAssertNoEof(self.reader, buffer, 0, buffer.length, self.readEntryCursor, function(err) {\n if (err) return emitErrorAndAutoClose(self, err);\n if (self.emittedError) return;\n // 46 - File name\n entry.fileNameRaw = buffer.subarray(0, entry.fileNameLength);\n // 46+n - Extra field\n var fileCommentStart = entry.fileNameLength + entry.extraFieldLength;\n entry.extraFieldRaw = buffer.subarray(entry.fileNameLength, fileCommentStart);\n // 46+n+m - File comment\n entry.fileCommentRaw = buffer.subarray(fileCommentStart, fileCommentStart + entry.fileCommentLength);\n\n // Parse the extra fields, which we need for processing other fields.\n try {\n entry.extraFields = parseExtraFields(entry.extraFieldRaw);\n } catch (err) {\n return emitErrorAndAutoClose(self, err);\n }\n\n // Interpret strings according to bit flags, extra fields, and options.\n if (self.decodeStrings) {\n var isUtf8 = (entry.generalPurposeBitFlag & 0x800) !== 0;\n entry.fileComment = decodeBuffer(entry.fileCommentRaw, isUtf8);\n entry.fileName = getFileNameLowLevel(entry.generalPurposeBitFlag, entry.fileNameRaw, entry.extraFields, self.strictFileNames);\n var errorMessage = validateFileName(entry.fileName);\n if (errorMessage != null) return emitErrorAndAutoClose(self, new Error(errorMessage));\n } else {\n entry.fileComment = entry.fileCommentRaw;\n entry.fileName = entry.fileNameRaw;\n }\n // Maintain API compatibility. See https://github.com/thejoshwolfe/yauzl/issues/47\n entry.comment = entry.fileComment;\n\n self.readEntryCursor += buffer.length;\n self.entriesRead += 1;\n\n // Check for the Zip64 Extended Information Extra Field.\n for (var i = 0; i < entry.extraFields.length; i++) {\n var extraField = entry.extraFields[i];\n if (extraField.id !== 0x0001) continue;\n // Found it.\n\n var zip64EiefBuffer = extraField.data;\n var index = 0;\n // 0 - Original Size 8 bytes\n if (entry.uncompressedSize === 0xffffffff) {\n if (index + 8 > zip64EiefBuffer.length) {\n return emitErrorAndAutoClose(self, new Error(\"zip64 extended information extra field does not include uncompressed size\"));\n }\n entry.uncompressedSize = readUInt64LE(zip64EiefBuffer, index);\n index += 8;\n }\n // 8 - Compressed Size 8 bytes\n if (entry.compressedSize === 0xffffffff) {\n if (index + 8 > zip64EiefBuffer.length) {\n return emitErrorAndAutoClose(self, new Error(\"zip64 extended information extra field does not include compressed size\"));\n }\n entry.compressedSize = readUInt64LE(zip64EiefBuffer, index);\n index += 8;\n }\n // 16 - Relative Header Offset 8 bytes\n if (entry.relativeOffsetOfLocalHeader === 0xffffffff) {\n if (index + 8 > zip64EiefBuffer.length) {\n return emitErrorAndAutoClose(self, new Error(\"zip64 extended information extra field does not include relative header offset\"));\n }\n entry.relativeOffsetOfLocalHeader = readUInt64LE(zip64EiefBuffer, index);\n index += 8;\n }\n // 24 - Disk Start Number 4 bytes\n\n break;\n }\n\n // validate file size\n if (self.validateEntrySizes && entry.compressionMethod === 0) {\n var expectedCompressedSize = entry.uncompressedSize;\n if (entry.isEncrypted()) {\n // traditional encryption prefixes the file data with a header\n expectedCompressedSize += 12;\n }\n if (entry.compressedSize !== expectedCompressedSize) {\n var msg = \"compressed/uncompressed size mismatch for stored file: \" + entry.compressedSize + \" != \" + entry.uncompressedSize;\n return emitErrorAndAutoClose(self, new Error(msg));\n }\n }\n\n self.emit(\"entry\", entry);\n\n if (!self.lazyEntries) self._readEntry();\n });\n });\n};\n\nZipFile.prototype.eachEntry = function() {\n const self = this;\n if (!self.lazyEntries) throw new Error(\"eachEntry() requires lazyEntries: true\");\n if (self.hasEachEntryBeenCalled) throw new Error(\"eachEntry() must only be called once per ZipFile\");\n self.hasEachEntryBeenCalled = true;\n\n let pendingResolveReject = null;\n self.on(\"entry\", onEntry);\n self.on(\"end\", onEnd);\n self.on(\"error\", onError);\n function cleanup() {\n self.removeListener(\"entry\", onEntry);\n self.removeListener(\"end\", onEnd);\n self.removeListener(\"error\", onError);\n if (self.autoClose) self.close();\n }\n\n function onEntry(entry) {\n let {resolve} = pendingResolveReject;\n pendingResolveReject = null;\n resolve({value: entry});\n }\n function onEnd() {\n let {resolve} = pendingResolveReject;\n pendingResolveReject = null;\n cleanup();\n resolve({done: true});\n }\n function onError(err) {\n let {reject} = pendingResolveReject;\n pendingResolveReject = null;\n cleanup();\n reject(err);\n }\n\n return {\n [Symbol.asyncIterator]() {\n // Called once by `for await...of`.\n return this;\n },\n next() {\n // Called on each iteration in a `for await...of`.\n const promise = new Promise((resolve, reject) => {\n if (pendingResolveReject != null) throw new Error(\"next() called before previous Promise was resolved.\");\n pendingResolveReject = {resolve, reject};\n });\n self.readEntry();\n return promise;\n },\n return(value) {\n // Called when breaking, returning, throwing out of a `for await...of`.\n cleanup();\n return Promise.resolve({done: true, value});\n },\n throw(value) {\n // Almost never called. Something about `yield*` maybe?\n cleanup();\n return Promise.reject(value);\n },\n };\n};\n\nZipFile.prototype.openReadStream = function(entry, options, callback) {\n var self = this;\n // parameter validation\n var relativeStart = 0;\n var relativeEnd = entry.compressedSize;\n if (callback == null) {\n callback = options;\n options = null;\n }\n if (options == null) {\n options = {};\n } else {\n if (options.decodeFileData === false) {\n // new, simple option\n if (options.decrypt != null) {\n throw new Error(\"cannot use options.decrypt when options.decodeFileData === false\");\n }\n if (options.decompress != null) {\n throw new Error(\"cannot use options.decompress when options.decodeFileData === false\");\n }\n // start and end are allowed\n } else {\n // old, complicated options\n if (options.decrypt != null) {\n if (!entry.isEncrypted()) {\n throw new Error(\"options.decrypt can only be specified for encrypted entries. See also option decodeFileData.\");\n }\n if (options.decrypt !== false) throw new Error(\"invalid options.decrypt value: \" + options.decrypt);\n if (entry.isCompressed()) {\n if (options.decompress !== false) throw new Error(\"entry is encrypted and compressed, and options.decompress !== false. See also option decodeFileData.\");\n }\n }\n if (options.decompress != null) {\n if (!entry.isCompressed()) {\n throw new Error(\"options.decompress can only be specified for compressed entries. See also option decodeFileData.\");\n }\n if (!(options.decompress === false || options.decompress === true)) {\n throw new Error(\"invalid options.decompress value: \" + options.decompress);\n }\n decompress = options.decompress;\n }\n }\n if (options.start != null) {\n relativeStart = options.start;\n if (relativeStart < 0) throw new Error(\"options.start < 0\");\n if (relativeStart > entry.compressedSize) throw new Error(\"options.start > entry.compressedSize\");\n }\n if (options.end != null) {\n relativeEnd = options.end;\n if (relativeEnd < 0) throw new Error(\"options.end < 0\");\n if (relativeEnd > entry.compressedSize) throw new Error(\"options.end > entry.compressedSize\");\n if (relativeEnd < relativeStart) throw new Error(\"options.end < options.start\");\n }\n }\n var rawMode = (\n options.decodeFileData === false || // Explicitly requested raw.\n (\n (entry.compressionMethod === 0 || // Naturally without compression.\n (entry.compressionMethod === 8 && options.decompress === false) // Deprecated compression bypass option.\n ) &&\n (!entry.isEncrypted() || // Naturally without encryption.\n options.decrypt === false // Deprecated encryption bypass option.\n )\n )\n );\n if (options.start != null || options.end != null) {\n // Ensure slicing deals with raw data.\n if (!rawMode) throw new Error(\"start/end range require options.decodeFileData === false for non-trivial encoded entries.\");\n }\n\n // any further errors can either be caused by the zipfile,\n // or were introduced in a minor version of yauzl,\n // so should be passed to the client rather than thrown.\n if (!self.isOpen) return callback(new Error(\"closed\"));\n if (entry.isEncrypted() && !rawMode) {\n if (options.decrypt !== false) return callback(new Error(\"entry is encrypted, and options.decodeFileData !== false\"));\n }\n var decompress;\n if (rawMode) {\n decompress = false;\n } else if (entry.compressionMethod === 8) {\n // 8 - The file is Deflated\n decompress = options.decodeFileData !== true;\n } else {\n return callback(new Error(\"unsupported compression method: \" + entry.compressionMethod));\n }\n\n self.readLocalFileHeader(entry, {minimal: true}, function(err, localFileHeader) {\n if (err) return callback(err);\n self.openReadStreamLowLevel(\n localFileHeader.fileDataStart, entry.compressedSize,\n relativeStart, relativeEnd,\n decompress, entry.uncompressedSize,\n callback);\n });\n};\n\nZipFile.prototype.openReadStreamLowLevel = function(fileDataStart, compressedSize, relativeStart, relativeEnd, decompress, uncompressedSize, callback) {\n var self = this;\n\n var fileDataEnd = fileDataStart + compressedSize;\n var readStream = self.reader.createReadStream({\n start: fileDataStart + relativeStart,\n end: fileDataStart + relativeEnd,\n });\n var endpointStream = readStream;\n if (decompress) {\n var destroyed = false;\n var inflateFilter = zlib.createInflateRaw();\n readStream.on(\"error\", function(err) {\n // setImmediate here because errors can be emitted during the first call to pipe()\n setImmediate(function() {\n if (!destroyed) inflateFilter.emit(\"error\", err);\n });\n });\n readStream.pipe(inflateFilter);\n\n if (self.validateEntrySizes) {\n endpointStream = new AssertByteCountStream(uncompressedSize);\n inflateFilter.on(\"error\", function(err) {\n // forward zlib errors to the client-visible stream\n setImmediate(function() {\n if (!destroyed) endpointStream.emit(\"error\", err);\n });\n });\n inflateFilter.pipe(endpointStream);\n } else {\n // the zlib filter is the client-visible stream\n endpointStream = inflateFilter;\n }\n // this is part of yauzl's API, so implement this function on the client-visible stream\n installDestroyFn(endpointStream, function() {\n destroyed = true;\n if (inflateFilter !== endpointStream) inflateFilter.unpipe(endpointStream);\n readStream.unpipe(inflateFilter);\n // TODO: the inflateFilter may cause a memory leak. see Issue #27.\n readStream.destroy();\n });\n }\n callback(null, endpointStream);\n};\n\nZipFile.prototype.readLocalFileHeader = function(entry, options, callback) {\n var self = this;\n if (callback == null) {\n callback = options;\n options = null;\n }\n if (options == null) options = {};\n\n self.reader.ref();\n var buffer = newBuffer(30);\n readAndAssertNoEof(self.reader, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader, function(err) {\n try {\n if (err) return callback(err);\n // 0 - Local file header signature = 0x04034b50\n var signature = buffer.readUInt32LE(0);\n if (signature !== 0x04034b50) {\n return callback(new Error(\"invalid local file header signature: 0x\" + signature.toString(16)));\n }\n\n var fileNameLength = buffer.readUInt16LE(26);\n var extraFieldLength = buffer.readUInt16LE(28);\n var fileDataStart = entry.relativeOffsetOfLocalHeader + 30 + fileNameLength + extraFieldLength;\n // We now have enough information to do this bounds check.\n if (fileDataStart + entry.compressedSize > self.fileSize) {\n return callback(new Error(\"file data overflows file bounds: \" +\n fileDataStart + \" + \" + entry.compressedSize + \" > \" + self.fileSize));\n }\n\n if (options.minimal) {\n return callback(null, {fileDataStart: fileDataStart});\n }\n\n var localFileHeader = new LocalFileHeader();\n localFileHeader.fileDataStart = fileDataStart;\n\n // 4 - Version needed to extract (minimum)\n localFileHeader.versionNeededToExtract = buffer.readUInt16LE(4);\n // 6 - General purpose bit flag\n localFileHeader.generalPurposeBitFlag = buffer.readUInt16LE(6);\n // 8 - Compression method\n localFileHeader.compressionMethod = buffer.readUInt16LE(8);\n // 10 - File last modification time\n localFileHeader.lastModFileTime = buffer.readUInt16LE(10);\n // 12 - File last modification date\n localFileHeader.lastModFileDate = buffer.readUInt16LE(12);\n // 14 - CRC-32\n localFileHeader.crc32 = buffer.readUInt32LE(14);\n // 18 - Compressed size\n localFileHeader.compressedSize = buffer.readUInt32LE(18);\n // 22 - Uncompressed size\n localFileHeader.uncompressedSize = buffer.readUInt32LE(22);\n // 26 - File name length (n)\n localFileHeader.fileNameLength = fileNameLength;\n // 28 - Extra field length (m)\n localFileHeader.extraFieldLength = extraFieldLength;\n // 30 - File name\n // 30+n - Extra field\n\n buffer = newBuffer(fileNameLength + extraFieldLength);\n self.reader.ref();\n readAndAssertNoEof(self.reader, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader + 30, function(err) {\n try {\n if (err) return callback(err);\n localFileHeader.fileName = buffer.subarray(0, fileNameLength);\n localFileHeader.extraField = buffer.subarray(fileNameLength);\n return callback(null, localFileHeader);\n } finally {\n self.reader.unref();\n }\n });\n } finally {\n self.reader.unref();\n }\n });\n};\n\nZipFile.prototype.openReadStreamPromise = function(entry, options) {\n return new Promise((resolve, reject) => {\n this.openReadStream(entry, options, function(err, readStream) {\n if (err) return reject(err);\n resolve(readStream);\n });\n });\n};\nZipFile.prototype.openReadStreamLowLevelPromise = function(fileDataStart, compressedSize, relativeStart, relativeEnd, decompress, uncompressedSize) {\n return new Promise((resolve, reject) => {\n this.openReadStream(fileDataStart, compressedSize, relativeStart, relativeEnd, decompress, uncompressedSize, function(err, readStream) {\n if (err) return reject(err);\n resolve(readStream);\n });\n });\n};\nZipFile.prototype.readLocalFileHeaderPromise = function(entry, options) {\n return new Promise((resolve, reject) => {\n this.readLocalFileHeader(entry, options, function(err, localFileHeader) {\n if (err) return reject(err);\n resolve(localFileHeader);\n });\n });\n};\n\nfunction Entry() {\n}\nEntry.prototype.getLastModDate = function(options) {\n if (options == null) options = {};\n\n if (!options.forceDosFormat) {\n // Check extended fields.\n for (var i = 0; i < this.extraFields.length; i++) {\n var extraField = this.extraFields[i];\n if (extraField.id === 0x5455) {\n // Info-ZIP \"universal timestamp\" extended field (`0x5455` aka `\"UT\"`).\n // See the Info-ZIP source code unix/unix.c:set_extra_field() and zipfile.c:ef_scan_ut_time().\n var data = extraField.data;\n if (data.length < 5) continue; // Too short.\n // The flags define which of the three fields are present: mtime, atime, ctime.\n // We only care about mtime.\n // Also, ctime is never included in practice.\n // And also, atime is only included in the local file header for some reason\n // despite the flags lying about its inclusion in the central header.\n var flags = data[0];\n var HAS_MTIME = 1;\n if (!(flags & HAS_MTIME)) continue; // This will realistically never happen.\n // Although the positions of all of the fields shift around depending on the presence of other fields,\n // mtime is always first if present, and that's the only one we care about.\n var posixTimestamp = data.readInt32LE(1);\n return new Date(posixTimestamp * 1000);\n } else if (extraField.id === 0x000a) {\n var data = extraField.data;\n if (data.length !== 32) continue; // The length is always the same.\n // 4 bytes reserved\n // 2 bytes Tag\n if (data.readUInt16LE(4) !== 1) continue; // Tag1 is actually the only defined Tag.\n // 2 bytes Size\n if (data.readUInt16LE(6) !== 24) continue; // Size is always 24.\n // 8 bytes Mtime\n var hundredNanoSecondsSince1601 = data.readUInt32LE(8) + 4294967296 * data.readInt32LE(12);\n // Convert from NTFS to POSIX milliseconds.\n // The big number below is the milliseconds between year 1601 and year 1970\n // (i.e. the negative POSIX timestamp of 1601-01-01 00:00:00Z)\n var millisecondsSince1970 = hundredNanoSecondsSince1601 / 10000 - 11644473600000;\n // Note on numeric precision: JavaScript Number objects lose precision above Number.MAX_SAFE_INTEGER,\n // and NTFS timestamps are typically much bigger than that limit.\n // (MAX_SAFE_INTEGER would represent 1629-07-17T23:58:45.475Z.)\n // However, we're losing precision in the conversion from 100nanosecond units to millisecond units anyway,\n // and the time at which we also lose 1-millisecond precision is year 275760, the JavaScript Date limit (by design).\n // Up through the year 2057, this conversion only drops 4 bits of precision,\n // which is well under the 13-14 bits ratio between the milliseconds and 100nanoseconds.\n return new Date(millisecondsSince1970);\n }\n }\n }\n\n // Fallback to non-extended encoding.\n return dosDateTimeToDate(this.lastModFileDate, this.lastModFileTime, options.timezone);\n};\n\nEntry.prototype.canDecodeFileData = function() {\n return (\n !this.isEncrypted() &&\n (this.compressionMethod === 0 || this.compressionMethod === 8)\n );\n};\nEntry.prototype.isEncrypted = function() {\n return (this.generalPurposeBitFlag & 0x1) !== 0;\n};\nEntry.prototype.isCompressed = function() {\n return this.compressionMethod === 8;\n};\n\nfunction LocalFileHeader() {\n}\n\nfunction dosDateTimeToDate(date, time, timezone) {\n var day = date & 0x1f; // 1-31\n var month = (date >> 5 & 0xf) - 1; // 1-12, 0-11\n var year = (date >> 9 & 0x7f) + 1980; // 0-128, 1980-2108\n\n var millisecond = 0;\n var second = (time & 0x1f) * 2; // 0-29, 0-58 (even numbers)\n var minute = time >> 5 & 0x3f; // 0-59\n var hour = time >> 11 & 0x1f; // 0-23\n\n if (timezone == null || timezone === \"local\") {\n return new Date(year, month, day, hour, minute, second, millisecond);\n } else if (timezone === \"UTC\") {\n return new Date(Date.UTC(year, month, day, hour, minute, second, millisecond));\n } else {\n throw new Error(\"unrecognized options.timezone: \" + options.timezone);\n }\n}\n\nfunction getFileNameLowLevel(generalPurposeBitFlag, fileNameBuffer, extraFields, strictFileNames) {\n var fileName = null;\n\n // check for Info-ZIP Unicode Path Extra Field (0x7075)\n // see https://github.com/thejoshwolfe/yauzl/issues/33\n for (var i = 0; i < extraFields.length; i++) {\n var extraField = extraFields[i];\n if (extraField.id === 0x7075) {\n if (extraField.data.length < 6) {\n // too short to be meaningful\n continue;\n }\n // Version 1 byte version of this extra field, currently 1\n if (extraField.data.readUInt8(0) !== 1) {\n // > Changes may not be backward compatible so this extra\n // > field should not be used if the version is not recognized.\n continue;\n }\n // NameCRC32 4 bytes File Name Field CRC32 Checksum\n var oldNameCrc32 = extraField.data.readUInt32LE(1);\n if (crc32(fileNameBuffer) !== oldNameCrc32) {\n // > If the CRC check fails, this UTF-8 Path Extra Field should be\n // > ignored and the File Name field in the header should be used instead.\n continue;\n }\n // UnicodeName Variable UTF-8 version of the entry File Name\n fileName = decodeBuffer(extraField.data.subarray(5), true);\n break;\n }\n }\n\n if (fileName == null) {\n // The typical case.\n var isUtf8 = (generalPurposeBitFlag & 0x800) !== 0;\n fileName = decodeBuffer(fileNameBuffer, isUtf8);\n }\n\n if (!strictFileNames) {\n // Allow backslash.\n fileName = fileName.replace(/\\\\/g, \"/\");\n }\n return fileName;\n}\n\nfunction validateFileName(fileName) {\n if (fileName.indexOf(\"\\\\\") !== -1) {\n return \"invalid characters in fileName: \" + fileName;\n }\n if (/^[a-zA-Z]:/.test(fileName) || /^\\//.test(fileName)) {\n return \"absolute path: \" + fileName;\n }\n if (fileName.split(\"/\").indexOf(\"..\") !== -1) {\n return \"invalid relative path: \" + fileName;\n }\n // all good\n return null;\n}\n\nfunction parseExtraFields(extraFieldBuffer) {\n var extraFields = [];\n var i = 0;\n while (i < extraFieldBuffer.length - 3) {\n var headerId = extraFieldBuffer.readUInt16LE(i + 0);\n var dataSize = extraFieldBuffer.readUInt16LE(i + 2);\n var dataStart = i + 4;\n var dataEnd = dataStart + dataSize;\n if (dataEnd > extraFieldBuffer.length) throw new Error(\"extra field length exceeds extra field buffer size\");\n var dataBuffer = extraFieldBuffer.subarray(dataStart, dataEnd);\n extraFields.push({\n id: headerId,\n data: dataBuffer,\n });\n i = dataEnd;\n }\n return extraFields;\n}\n\nfunction readAndAssertNoEof(reader, buffer, offset, length, position, callback) {\n if (length === 0) {\n // fs.read will throw an out-of-bounds error if you try to read 0 bytes from a 0 byte file\n return setImmediate(function() { callback(null, newBuffer(0)); });\n }\n reader.read(buffer, offset, length, position, function(err, bytesRead) {\n if (err) return callback(err);\n if (bytesRead < length) {\n return callback(new Error(\"unexpected EOF\"));\n }\n callback();\n });\n}\n\nutil.inherits(AssertByteCountStream, Transform);\nfunction AssertByteCountStream(byteCount) {\n Transform.call(this);\n this.actualByteCount = 0;\n this.expectedByteCount = byteCount;\n}\nAssertByteCountStream.prototype._transform = function(chunk, encoding, cb) {\n this.actualByteCount += chunk.length;\n if (this.actualByteCount > this.expectedByteCount) {\n var msg = \"too many bytes in the stream. expected \" + this.expectedByteCount + \". got at least \" + this.actualByteCount;\n return cb(new Error(msg));\n }\n cb(null, chunk);\n};\nAssertByteCountStream.prototype._flush = function(cb) {\n if (this.actualByteCount < this.expectedByteCount) {\n var msg = \"not enough bytes in the stream. expected \" + this.expectedByteCount + \". got only \" + this.actualByteCount;\n return cb(new Error(msg));\n }\n cb();\n};\n\nutil.inherits(RandomAccessReader, EventEmitter);\nfunction RandomAccessReader() {\n EventEmitter.call(this);\n this.refCount = 0;\n}\nRandomAccessReader.prototype.ref = function() {\n this.refCount += 1;\n};\nRandomAccessReader.prototype.unref = function() {\n var self = this;\n self.refCount -= 1;\n\n if (self.refCount > 0) return;\n if (self.refCount < 0) throw new Error(\"invalid unref\");\n\n self.close(onCloseDone);\n\n function onCloseDone(err) {\n if (err) return self.emit('error', err);\n self.emit('close');\n }\n};\nRandomAccessReader.prototype.createReadStream = function(options) {\n if (options == null) options = {};\n var start = options.start;\n var end = options.end;\n if (start === end) {\n var emptyStream = new PassThrough();\n setImmediate(function() {\n emptyStream.end();\n });\n return emptyStream;\n }\n var stream = this._readStreamForRange(start, end);\n\n var destroyed = false;\n var refUnrefFilter = new RefUnrefFilter(this);\n stream.on(\"error\", function(err) {\n setImmediate(function() {\n if (!destroyed) refUnrefFilter.emit(\"error\", err);\n });\n });\n installDestroyFn(refUnrefFilter, function() {\n stream.unpipe(refUnrefFilter);\n refUnrefFilter.unref();\n stream.destroy();\n });\n\n var byteCounter = new AssertByteCountStream(end - start);\n refUnrefFilter.on(\"error\", function(err) {\n setImmediate(function() {\n if (!destroyed) byteCounter.emit(\"error\", err);\n });\n });\n installDestroyFn(byteCounter, function() {\n destroyed = true;\n refUnrefFilter.unpipe(byteCounter);\n refUnrefFilter.destroy();\n });\n\n return stream.pipe(refUnrefFilter).pipe(byteCounter);\n};\nRandomAccessReader.prototype._readStreamForRange = function(start, end) {\n throw new Error(\"not implemented\");\n};\nRandomAccessReader.prototype.read = function(buffer, offset, length, position, callback) {\n var readStream = this.createReadStream({start: position, end: position + length});\n var writeStream = new Writable();\n var written = 0;\n writeStream._write = function(chunk, encoding, cb) {\n chunk.copy(buffer, offset + written, 0, chunk.length);\n written += chunk.length;\n cb();\n };\n writeStream.on(\"finish\", callback);\n readStream.on(\"error\", function(error) {\n callback(error);\n });\n readStream.pipe(writeStream);\n};\nRandomAccessReader.prototype.close = function(callback) {\n setImmediate(callback);\n};\n\nutil.inherits(RefUnrefFilter, PassThrough);\nfunction RefUnrefFilter(context) {\n PassThrough.call(this);\n this.context = context;\n this.context.ref();\n this.unreffedYet = false;\n}\nRefUnrefFilter.prototype._flush = function(cb) {\n this.unref();\n cb();\n};\nRefUnrefFilter.prototype.unref = function(cb) {\n if (this.unreffedYet) return;\n this.unreffedYet = true;\n this.context.unref();\n};\n\nvar cp437 = '\\u0000☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ';\nfunction decodeBuffer(buffer, isUtf8) {\n if (isUtf8) {\n return buffer.toString(\"utf8\");\n } else {\n var result = \"\";\n for (var i = 0; i < buffer.length; i++) {\n result += cp437[buffer[i]];\n }\n return result;\n }\n}\n\nfunction readUInt64LE(buffer, offset) {\n // There is no native function for this, because we can't actually store 64-bit integers precisely.\n // after 53 bits, JavaScript's Number type (IEEE 754 double) can't store individual integers anymore.\n // but since 53 bits is a whole lot more than 32 bits, we do our best anyway.\n // As of 2020, Node has added support for BigInt, which obviates this whole function,\n // but yauzl hasn't been updated to depend on BigInt (yet?).\n var lower32 = buffer.readUInt32LE(offset);\n var upper32 = buffer.readUInt32LE(offset + 4);\n // we can't use bitshifting here, because JavaScript bitshifting only works on 32-bit integers.\n return upper32 * 0x100000000 + lower32;\n // as long as we're bounds checking the result of this function against the total file size,\n // we'll catch any overflow errors, because we already made sure the total file size was within reason.\n}\n\n// Node 10 deprecated new Buffer().\nvar newBuffer;\nif (typeof Buffer.allocUnsafe === \"function\") {\n newBuffer = function(len) {\n return Buffer.allocUnsafe(len);\n };\n} else {\n newBuffer = function(len) {\n return new Buffer(len);\n };\n}\n\n// Node 8 introduced a proper destroy() implementation on writable streams.\nfunction installDestroyFn(stream, fn) {\n if (typeof stream.destroy === \"function\") {\n // New API.\n stream._destroy = function(err, cb) {\n fn();\n if (cb != null) cb(err);\n };\n } else {\n // Old API.\n stream.destroy = fn;\n }\n}\n\nfunction defaultCallback(err) {\n if (err) throw err;\n}\n","import * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\n\nexport const CHATLAB_TEMP_ROOT_ENV = 'CHATLAB_TEMP_ROOT'\n\nconst CHATLAB_TEMP_SCOPES = new Set(['tests', 'runtime', 'imports', 'merge', 'sync', 'parser', 'build', 'bench'])\n\nexport type ChatLabTempScope = 'tests' | 'runtime' | 'imports' | 'merge' | 'sync' | 'parser' | 'build' | 'bench'\n\nexport interface ChatLabTempRootOptions {\n env?: NodeJS.ProcessEnv\n platform?: NodeJS.Platform\n systemTempDir?: string\n}\n\nfunction optionsEnv(options: ChatLabTempRootOptions): NodeJS.ProcessEnv {\n return options.env ?? process.env\n}\n\nexport function resolveChatLabTempRoot(options: ChatLabTempRootOptions = {}): string {\n const envRoot = optionsEnv(options)[CHATLAB_TEMP_ROOT_ENV]?.trim()\n if (envRoot) return path.resolve(envRoot)\n\n // Product boundary: ChatLab runs as a single OS user. The single private 0700 root is intentional;\n // multi-UID hosts are unsupported, while isolated tests and CI must override CHATLAB_TEMP_ROOT.\n const platform = options.platform ?? process.platform\n const systemTempDir = options.systemTempDir ?? os.tmpdir()\n return path.resolve(platform === 'darwin' ? '/private/tmp/chatlab' : path.join(systemTempDir, 'chatlab'))\n}\n\nfunction ensurePrivateOwnedDirectory(dirPath: string, label: string): void {\n fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 })\n const stats = fs.lstatSync(dirPath)\n if (stats.isSymbolicLink()) {\n throw new Error(`${label} must not be a symbolic link: ${dirPath}`)\n }\n if (!stats.isDirectory()) {\n throw new Error(`${label} must be a directory: ${dirPath}`)\n }\n if (typeof process.getuid === 'function' && stats.uid !== process.getuid()) {\n throw new Error(`${label} is owned by another user: ${dirPath}`)\n }\n if (process.platform !== 'win32') {\n fs.chmodSync(dirPath, 0o700)\n }\n}\n\nexport function ensureChatLabTempRoot(options: ChatLabTempRootOptions = {}): string {\n const root = resolveChatLabTempRoot(options)\n ensurePrivateOwnedDirectory(root, 'ChatLab temp root')\n optionsEnv(options)[CHATLAB_TEMP_ROOT_ENV] = root\n return root\n}\n\nfunction assertValidScope(scope: string): asserts scope is ChatLabTempScope {\n if (!CHATLAB_TEMP_SCOPES.has(scope)) {\n throw new Error(`Invalid ChatLab temp scope: ${scope}`)\n }\n}\n\nfunction assertValidPrefix(prefix: string): void {\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(prefix)) {\n throw new Error(`Invalid ChatLab temp prefix: ${prefix}`)\n }\n}\n\nexport function getChatLabTempScopeDir(scope: ChatLabTempScope, options: ChatLabTempRootOptions = {}): string {\n assertValidScope(scope)\n const scopeDir = path.join(ensureChatLabTempRoot(options), scope)\n ensurePrivateOwnedDirectory(scopeDir, 'ChatLab temp scope')\n return scopeDir\n}\n\nexport function createChatLabTempDir(\n scope: ChatLabTempScope,\n prefix: string,\n options: ChatLabTempRootOptions = {}\n): string {\n assertValidPrefix(prefix)\n return fs.mkdtempSync(path.join(getChatLabTempScopeDir(scope, options), prefix))\n}\n\nexport function removeChatLabTempDir(\n dirPath: string,\n scope: ChatLabTempScope,\n options: ChatLabTempRootOptions = {}\n): void {\n const scopeDir = path.resolve(getChatLabTempScopeDir(scope, options))\n const resolvedDir = path.resolve(dirPath)\n if (path.dirname(resolvedDir) !== scopeDir) {\n throw new Error(`Refusing to remove non-owned ChatLab temp directory: ${resolvedDir}`)\n }\n fs.rmSync(resolvedDir, { recursive: true, force: true })\n}\n","/**\n * Parser V2 - 模块入口\n * 三层架构:标准层、嗅探层、解析层\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { FormatSniffer, createSniffer } from './sniffer'\nimport { formats } from './formats'\nimport { getFileSize } from './utils'\nimport type {\n ParseOptions,\n ParseEvent,\n ParseResult,\n ParseProgress,\n LogLevel,\n FormatFeature,\n Parser,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n MultiChatInfo,\n} from './types'\n\nexport {\n validateChatLabFile,\n type ChatLabValidationFormat,\n type ChatLabValidationIssue,\n type ChatLabValidationReport,\n type ChatLabValidationSeverity,\n type ChatLabValidationStats,\n} from './chatlab-validator'\nexport { rebaseChatLabDemoDocuments, type RebasedChatLabDemoDocuments } from './chatlab-demo-timeline'\nexport {\n PARSER_FORMAT_IDS,\n LEGACY_PARSER_FORMAT_ID_ALIASES,\n isParserFormatId,\n normalizeParserFormatId,\n type ParserFormatId,\n} from './format-ids'\n\n// ==================== 全局嗅探器实例 ====================\n\nconst sniffer = createSniffer()\nsniffer.registerAll(formats)\n\n// ==================== 公共 API ====================\n\n/**\n * 检测文件格式\n * @param filePath 文件路径\n * @returns 格式特征,如果无法识别则返回 null\n */\nexport function detectFormat(filePath: string): FormatFeature | null {\n return sniffer.sniff(filePath)\n}\n\n/**\n * 检测所有匹配的文件格式(按优先级排序)\n * 用于 fallback 机制:当第一个格式解析失败时尝试下一个\n * @param filePath 文件路径\n * @returns 所有匹配的格式特征列表\n */\nexport function detectAllFormats(filePath: string): FormatFeature[] {\n return sniffer.sniffAll(filePath)\n}\n\n/**\n * 获取文件对应的解析器\n * @param filePath 文件路径\n * @returns 解析器实例,如果无法识别则返回 null\n */\nexport function getParser(filePath: string): Parser | null {\n return sniffer.getParser(filePath)\n}\n\n/**\n * 获取所有支持的格式\n */\nexport function getSupportedFormats(): FormatFeature[] {\n return sniffer.getSupportedFormats()\n}\n\n/**\n * 根据格式 ID 获取格式特征\n * 用于手动指定格式时跳过嗅探\n */\nexport function getFormatFeatureById(formatId: string): FormatFeature | null {\n return sniffer.getFeatureById(formatId)\n}\n\n/**\n * 获取格式的预处理器(如果有)\n */\nexport function getPreprocessor(filePath: string) {\n const feature = sniffer.sniff(filePath)\n if (!feature) return null\n\n const module = formats.find((m) => m.feature.id === feature.id)\n return module?.preprocessor || null\n}\n\n/**\n * 扫描多聊天文件中的聊天列表\n * 自动检测格式并调用对应格式模块的 scanChats\n * @param filePath 文件路径\n * @returns 聊天列表,如果格式不支持多聊天则抛出错误\n */\nexport async function scanMultiChatFile(filePath: string): Promise<MultiChatInfo[]> {\n const feature = sniffer.sniff(filePath)\n if (!feature) {\n throw new Error(`无法识别文件格式: ${filePath}`)\n }\n\n if (!feature.multiChat) {\n throw new Error(`格式 \"${feature.name}\" 不是多聊天格式`)\n }\n\n const module = formats.find((m) => m.feature.id === feature.id)\n if (!module?.scanChats) {\n throw new Error(`格式 \"${feature.name}\" 声明了 multiChat 但未实现 scanChats`)\n }\n\n return module.scanChats(filePath)\n}\n\n/**\n * 检查文件是否需要预处理\n */\nexport function needsPreprocess(filePath: string): boolean {\n const preprocessor = getPreprocessor(filePath)\n if (!preprocessor) return false\n\n const fileSize = getFileSize(filePath)\n return preprocessor.needsPreprocess(filePath, fileSize)\n}\n\n/**\n * 流式解析文件\n * @param options 解析选项\n * @yields 解析事件流\n */\nexport async function* parseFile(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const parser = sniffer.getParser(options.filePath)\n if (!parser) {\n yield { type: 'error', data: new Error(`无法识别文件格式: ${options.filePath}`) }\n return\n }\n\n console.log(`[Parser V2] Using parser: ${parser.feature.name}`)\n yield* parser.parse(options)\n}\n\n/**\n * 使用指定格式解析文件(用于 fallback 机制)\n * @param formatId 格式 ID\n * @param options 解析选项\n * @yields 解析事件流\n */\nexport async function* parseFileWithFormat(\n formatId: string,\n options: ParseOptions\n): AsyncGenerator<ParseEvent, void, unknown> {\n const parser = sniffer.getParserById(formatId)\n if (!parser) {\n yield { type: 'error', data: new Error(`未知的格式 ID: ${formatId}`) }\n return\n }\n\n console.log(`[Parser V2] Using parser (explicit): ${parser.feature.name}`)\n yield* parser.parse(options)\n}\n\n/**\n * 同步解析文件(收集所有事件为完整结果)\n * 适用于不需要流式处理的场景(如合并工具)\n * @param filePath 文件路径\n * @param onProgress 进度回调(可选)\n */\nexport async function parseFileSync(\n filePath: string,\n onProgress?: (progress: ParseProgress) => void\n): Promise<ParseResult> {\n let meta: ParsedMeta | null = null\n const members: ParsedMember[] = []\n const messages: ParsedMessage[] = []\n\n for await (const event of parseFile({ filePath, onProgress })) {\n switch (event.type) {\n case 'meta':\n meta = event.data\n break\n case 'members':\n members.push(...event.data)\n break\n case 'messages':\n messages.push(...event.data)\n break\n case 'progress':\n onProgress?.(event.data)\n break\n case 'error':\n throw event.data\n }\n }\n\n if (!meta) {\n throw new Error('解析失败:未获取到元信息')\n }\n\n return { meta, members, messages }\n}\n\n/**\n * 解析文件获取基本信息(只统计,不返回完整消息)\n * 用于预览和合并工具\n */\nexport async function parseFileInfo(\n filePath: string,\n onProgress?: (progress: ParseProgress) => void\n): Promise<{\n name: string\n format: string\n platform: string\n messageCount: number\n memberCount: number\n fileSize: number\n}> {\n const feature = sniffer.sniff(filePath)\n if (!feature) {\n throw new Error(`无法识别文件格式: ${filePath}`)\n }\n\n let name = '未知群聊'\n let platform = feature.platform\n let messageCount = 0\n let memberCount = 0\n\n for await (const event of parseFile({ filePath, onProgress })) {\n switch (event.type) {\n case 'meta':\n name = event.data.name\n platform = event.data.platform\n break\n case 'members':\n memberCount += event.data.length\n break\n case 'messages':\n messageCount += event.data.length\n break\n case 'progress':\n onProgress?.(event.data)\n break\n case 'error':\n throw event.data\n }\n }\n\n const fileSize = fs.statSync(filePath).size\n\n return {\n name,\n format: feature.name,\n platform,\n messageCount,\n memberCount,\n fileSize,\n }\n}\n\n/**\n * Scan a directory for a recognizable entry file (e.g. manifest.json for chunked formats).\n * Uses existing format detection; no format-specific filenames are hardcoded here.\n * @param dirPath directory path to scan\n * @param maxDepth max depth to scan (default 1, only top-level files)\n * @returns path to the entry file, or null if none found\n */\nexport function findEntryFileInDirectory(dirPath: string, maxDepth = 1): string | null {\n const supportedExtensions = new Set(\n sniffer\n .getSupportedFormats()\n .flatMap((feature) => feature.extensions)\n .map((extension) => extension.toLowerCase())\n )\n\n function walk(current: string, depth: number): string | null {\n if (depth > maxDepth) return null\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(current, { withFileTypes: true })\n } catch {\n return null\n }\n for (const entry of entries) {\n const full = path.join(current, entry.name)\n if (entry.isFile() && supportedExtensions.has(path.extname(entry.name).toLowerCase())) {\n const format = detectFormat(full)\n if (format) return full\n }\n }\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const found = walk(path.join(current, entry.name), depth + 1)\n if (found) return found\n }\n }\n return null\n }\n\n return walk(dirPath, 0)\n}\n\n// ==================== 导出类型 ====================\n\nexport type {\n ParseOptions,\n ParseEvent,\n ParseResult,\n ParseProgress,\n FormatFeature,\n Parser,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n MultiChatInfo,\n}\n\n// ==================== 导出嗅探器(高级用法) ====================\n\nexport { FormatSniffer, createSniffer }\n\n// ==================== 导出工具函数 ====================\n\nexport { getFileSize, formatFileSize, parseTimestamp, isValidYear, createProgress, readFileHeadBytes } from './utils'\n\n// ==================== 导出 native 加载状态(供运行时启动日志使用) ====================\n\nexport { getNativeParserStatus, isNativeFormatAvailable } from './native/loader'\nexport type { NativeParserStatus } from './native/loader'\n\n// ==================== 回调模式 API ====================\n\n/**\n * 回调模式的解析选项\n */\nexport interface StreamParseCallbacks {\n onProgress: (progress: ParseProgress) => void\n onMeta: (meta: ParsedMeta) => void\n onMembers: (members: ParsedMember[]) => void\n onMessageBatch: (messages: ParsedMessage[]) => void\n /** 日志回调(可选) */\n onLog?: (level: LogLevel, message: string) => void\n}\n\nexport interface StreamParseOptions extends StreamParseCallbacks {\n filePath: string\n batchSize?: number\n formatOptions?: Record<string, unknown>\n}\n\n/**\n * 回调模式的流式解析\n * 内部使用 AsyncGenerator,对外提供回调接口\n * @param filePath 文件路径\n * @param callbacks 回调选项\n * @param formatId 可选,指定格式 ID(用于 fallback 机制,跳过自动检测)\n */\nexport async function streamParseFile(\n filePath: string,\n callbacks: Omit<StreamParseOptions, 'filePath'>,\n formatId?: string\n): Promise<void> {\n const { onProgress, onMeta, onMembers, onMessageBatch, onLog, batchSize = 5000, formatOptions } = callbacks\n\n // 根据是否指定 formatId 选择解析方式\n const generator = formatId\n ? parseFileWithFormat(formatId, { filePath, batchSize, formatOptions, onProgress, onLog })\n : parseFile({ filePath, batchSize, formatOptions, onProgress, onLog })\n\n for await (const event of generator) {\n switch (event.type) {\n case 'meta':\n onMeta(event.data)\n break\n case 'members':\n onMembers(event.data)\n break\n case 'messages':\n onMessageBatch(event.data)\n break\n case 'progress':\n onProgress(event.data)\n break\n case 'error':\n throw event.data\n }\n }\n}\n","/**\n * Parser V2 - 嗅探层\n * 负责检测文件格式,匹配对应的解析器\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { normalizeParserFormatId } from './format-ids'\nimport type { FormatFeature, FormatModule, Parser } from './types'\n\n/** 文件头检测大小 (64KB) - 考虑到现代聊天记录文件可能包含 base64 头像等大数据 */\nconst HEAD_SIZE = 64 * 1024\n\n/**\n * 读取文件头部内容\n */\nfunction readFileHead(filePath: string, size: number = HEAD_SIZE): string {\n const fd = fs.openSync(filePath, 'r')\n const buffer = Buffer.alloc(size)\n const bytesRead = fs.readSync(fd, buffer, 0, size, 0)\n fs.closeSync(fd)\n return buffer.slice(0, bytesRead).toString('utf-8')\n}\n\n/**\n * 获取文件扩展名(小写)\n */\nfunction getExtension(filePath: string): string {\n return path.extname(filePath).toLowerCase()\n}\n\n/**\n * 检查文件头是否匹配签名\n */\nfunction matchHeadSignatures(headContent: string, patterns: RegExp[]): boolean {\n return patterns.some((pattern) => pattern.test(headContent))\n}\n\n/**\n * 检查文件名是否匹配签名\n */\nfunction matchFilenameSignatures(filePath: string, patterns: RegExp[]): boolean {\n const filename = path.basename(filePath)\n return patterns.some((pattern) => pattern.test(filename))\n}\n\n/**\n * 文件名只能作为补充特征;需要全文确认时再读取完整文件。\n */\nfunction matchFilenameFallbackPatterns(filePath: string, patterns: RegExp[]): boolean {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n return patterns.every((pattern) => pattern.test(content))\n } catch {\n return false\n }\n}\n\n/**\n * 检查必需字段是否存在\n */\nfunction matchRequiredFields(headContent: string, fields: string[]): boolean {\n // 简单检查:字段名是否出现在文件头中\n // 对于 JSON 文件,检查 \"fieldName\" 是否存在\n return fields.every((field) => {\n const pattern = new RegExp(`\"${field.replace('.', '\"\\\\s*:\\\\s*.*\"')}\"\\\\s*:`)\n return pattern.test(headContent) || headContent.includes(`\"${field}\"`)\n })\n}\n\n/**\n * 格式嗅探器\n * 管理所有格式特征,负责检测文件格式\n */\nexport class FormatSniffer {\n private formats: FormatModule[] = []\n\n /**\n * 注册格式模块\n */\n register(module: FormatModule): void {\n this.formats.push(module)\n // 按优先级排序(优先级数字越小越靠前)\n this.formats.sort((a, b) => a.feature.priority - b.feature.priority)\n }\n\n /**\n * 批量注册格式模块\n */\n registerAll(modules: FormatModule[]): void {\n for (const module of modules) {\n this.register(module)\n }\n }\n\n /**\n * 嗅探文件格式\n * @param filePath 文件路径\n * @returns 匹配的格式特征,如果无法识别则返回 null\n */\n sniff(filePath: string): FormatFeature | null {\n const ext = getExtension(filePath)\n const headContent = readFileHead(filePath)\n\n for (const { feature } of this.formats) {\n if (this.matchFeature(feature, ext, headContent, filePath)) {\n return feature\n }\n }\n\n return null\n }\n\n /**\n * 获取文件对应的解析器\n * @param filePath 文件路径\n * @returns 匹配的解析器,如果无法识别则返回 null\n */\n getParser(filePath: string): Parser | null {\n const ext = getExtension(filePath)\n const headContent = readFileHead(filePath)\n\n for (const { feature, parser } of this.formats) {\n if (this.matchFeature(feature, ext, headContent, filePath)) {\n return parser\n }\n }\n\n return null\n }\n\n /**\n * 嗅探所有匹配的格式(按优先级排序)\n * 用于 fallback 机制:当第一个格式解析失败时尝试下一个\n * @param filePath 文件路径\n * @returns 所有匹配的格式特征列表\n */\n sniffAll(filePath: string): FormatFeature[] {\n const ext = getExtension(filePath)\n const headContent = readFileHead(filePath)\n const results: FormatFeature[] = []\n\n for (const { feature } of this.formats) {\n if (this.matchFeature(feature, ext, headContent, filePath)) {\n results.push(feature)\n }\n }\n\n return results\n }\n\n /**\n * 获取所有匹配的解析器(按优先级排序)\n * 用于 fallback 机制\n * @param filePath 文件路径\n * @returns 所有匹配的解析器列表\n */\n getParserCandidates(filePath: string): Parser[] {\n const ext = getExtension(filePath)\n const headContent = readFileHead(filePath)\n const results: Parser[] = []\n\n for (const { feature, parser } of this.formats) {\n if (this.matchFeature(feature, ext, headContent, filePath)) {\n results.push(parser)\n }\n }\n\n return results\n }\n\n /**\n * 根据格式 ID 获取解析器\n */\n getParserById(formatId: string): Parser | null {\n const canonicalId = normalizeParserFormatId(formatId)\n const module = this.formats.find((m) => m.feature.id === canonicalId)\n return module?.parser || null\n }\n\n /**\n * Resolve canonical and released legacy format IDs to one canonical feature.\n */\n getFeatureById(formatId: string): FormatFeature | null {\n const canonicalId = normalizeParserFormatId(formatId)\n return this.formats.find((module) => module.feature.id === canonicalId)?.feature ?? null\n }\n\n /**\n * 获取所有支持的格式\n */\n getSupportedFormats(): FormatFeature[] {\n return this.formats.map((m) => m.feature)\n }\n\n /**\n * 检查特征是否匹配\n */\n private matchFeature(feature: FormatFeature, ext: string, headContent: string, filePath?: string): boolean {\n // 1. 检查扩展名\n if (!feature.extensions.includes(ext)) {\n return false\n }\n\n const { signatures } = feature\n\n // 2. 检查文件头签名(如果定义了)\n let headMatch = true\n if (signatures.head && signatures.head.length > 0) {\n headMatch = matchHeadSignatures(headContent, signatures.head)\n }\n\n // 3. 检查文件名签名(如果定义了,作为文件头匹配失败的补充)\n let filenameMatch = false\n if (signatures.filename && signatures.filename.length > 0 && filePath) {\n filenameMatch = matchFilenameSignatures(filePath, signatures.filename)\n }\n\n // 文件头签名或文件名签名至少有一个匹配\n if (!headMatch && !filenameMatch) {\n // 如果两个都没定义,则认为匹配(只检查扩展名)\n if ((signatures.head && signatures.head.length > 0) || (signatures.filename && signatures.filename.length > 0)) {\n return false\n }\n }\n\n if (!headMatch && filenameMatch && signatures.filenameFallbackPatterns) {\n if (!filePath || !matchFilenameFallbackPatterns(filePath, signatures.filenameFallbackPatterns)) {\n return false\n }\n }\n\n // 4. 检查必需字段(如果定义了)\n if (signatures.requiredFields && signatures.requiredFields.length > 0) {\n if (!matchRequiredFields(headContent, signatures.requiredFields)) {\n return false\n }\n }\n\n // 5. 检查字段值模式(如果定义了)\n if (signatures.fieldPatterns) {\n for (const [, pattern] of Object.entries(signatures.fieldPatterns)) {\n if (!pattern.test(headContent)) {\n return false\n }\n }\n }\n\n return true\n }\n}\n\n/**\n * 创建并返回全局嗅探器实例\n */\nexport function createSniffer(): FormatSniffer {\n return new FormatSniffer()\n}\n","export const PARSER_FORMAT_IDS = {\n CHATLAB: 'chatlab',\n CHATLAB_JSONL: 'chatlab-jsonl',\n QQ_SHUAKAMI: 'qq-shuakami',\n QQ_SHUAKAMI_CHUNKED: 'qq-shuakami-chunked',\n WEFLOW: 'weflow',\n ECHOTRACE: 'echotrace',\n DISCORD_TYRRRZ: 'discord-tyrrrz',\n TELEGRAM_NATIVE: 'telegram-native',\n TELEGRAM_NATIVE_SINGLE: 'telegram-native-single',\n GOOGLE_CHAT_NATIVE: 'google-chat-native',\n INSTAGRAM_NATIVE: 'instagram-native',\n WHATSAPP_NATIVE: 'whatsapp-native',\n QQ_NATIVE: 'qq-native',\n LINE_NATIVE: 'line-native',\n} as const\n\nexport type ParserFormatId = (typeof PARSER_FORMAT_IDS)[keyof typeof PARSER_FORMAT_IDS]\n\nexport const LEGACY_PARSER_FORMAT_ID_ALIASES = {\n 'shuakami-qq-exporter': PARSER_FORMAT_IDS.QQ_SHUAKAMI,\n 'shuakami-qq-exporter-chunked': PARSER_FORMAT_IDS.QQ_SHUAKAMI_CHUNKED,\n 'ycccccccy-echotrace': PARSER_FORMAT_IDS.ECHOTRACE,\n 'tyrrrz-discord-exporter': PARSER_FORMAT_IDS.DISCORD_TYRRRZ,\n 'whatsapp-native-txt': PARSER_FORMAT_IDS.WHATSAPP_NATIVE,\n 'qq-native-txt': PARSER_FORMAT_IDS.QQ_NATIVE,\n 'line-native-txt': PARSER_FORMAT_IDS.LINE_NATIVE,\n 'google-chat-takeout': PARSER_FORMAT_IDS.GOOGLE_CHAT_NATIVE,\n} as const satisfies Record<string, ParserFormatId>\n\nexport function normalizeParserFormatId(formatId: string): string {\n return LEGACY_PARSER_FORMAT_ID_ALIASES[formatId as keyof typeof LEGACY_PARSER_FORMAT_ID_ALIASES] ?? formatId\n}\n\nexport function isParserFormatId(formatId: string): formatId is ParserFormatId {\n return Object.values(PARSER_FORMAT_IDS).some((candidate) => candidate === formatId)\n}\n","/**\n * ChatLab JSON 格式\n * ChatLab 专属的统一格式(通过 chatlab 对象识别)\n *\n * 特征:\n * - 文件头包含 \"chatlab\" 字段\n * - 有 version 版本号\n * - 消息结构已经是标准化的\n */\n\nimport * as fs from 'fs'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\nimport streamChain from 'stream-chain'\nimport { KNOWN_PLATFORMS, ChatType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { getFileSize, createProgress, readFileHeadBytes } from '../utils'\nimport * as path from 'path'\n\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\nconst { chain } = streamChain\n\n// ==================== 辅助函数 ====================\n\n/**\n * 从文件名提取群名\n * 返回不含扩展名的文件名\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath)\n // 移除 .json 扩展名\n const name = basename.replace(/\\.json$/i, '')\n return name || '未知群聊'\n}\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.CHATLAB,\n name: 'ChatLab JSON',\n platform: KNOWN_PLATFORMS.UNKNOWN, // ChatLab 格式可能包含多平台数据\n priority: 50, // 低优先级,让其他格式先匹配\n extensions: ['.json'],\n signatures: {\n // 只要求 chatlab 字段在文件头,其他字段在解析时验证\n head: [/\"chatlab\"\\s*:\\s*\\{/],\n requiredFields: ['chatlab'],\n },\n}\n\n// ==================== 消息结构 ====================\n\ninterface ChatLabMessage {\n platformMessageId?: string // 消息的平台原始 ID(用于回复关联查询)\n sender: string // platformId\n accountName: string // 发送时的账号名称\n groupNickname?: string // 发送时的群昵称\n timestamp: number // 秒级时间戳\n type: number // MessageType\n content: string | null\n replyToMessageId?: string // 回复的目标消息 ID(平台原始 ID)\n}\n\ninterface ChatLabMember {\n platformId: string\n accountName: string // 账号名称\n groupNickname?: string // 群昵称\n aliases?: string[]\n avatar?: string // 头像(base64 Data URL)\n roles?: Array<{ id: string; name?: string }> // 成员角色\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseChatLab(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n const totalBytes = getFileSize(filePath)\n let bytesRead = 0\n let messagesProcessed = 0\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n // 记录解析开始\n onLog?.('info', `开始解析 ChatLab 格式文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 读取文件头获取 meta 信息\n const headContent = readFileHeadBytes(filePath, 200000)\n\n // 解析 meta\n let meta: ParsedMeta = {\n name: '未知群聊',\n platform: KNOWN_PLATFORMS.UNKNOWN,\n type: ChatType.GROUP,\n }\n try {\n // 使用更健壮的方式解析嵌套 JSON 对象\n // 因为 meta 可能包含 sources 数组(嵌套对象),简单的正则无法正确匹配\n const metaStartMatch = headContent.match(/\"meta\"\\s*:\\s*\\{/)\n if (metaStartMatch && metaStartMatch.index !== undefined) {\n const startIndex = metaStartMatch.index + metaStartMatch[0].length - 1 // 指向 {\n let depth = 0\n let endIndex = startIndex\n\n // 遍历字符找到匹配的闭合 }\n for (let i = startIndex; i < headContent.length; i++) {\n const char = headContent[i]\n if (char === '{') {\n depth++\n } else if (char === '}') {\n depth--\n if (depth === 0) {\n endIndex = i\n break\n }\n }\n }\n\n if (endIndex > startIndex) {\n const metaJson = headContent.slice(startIndex, endIndex + 1)\n const metaObj = JSON.parse(metaJson)\n meta = {\n name: metaObj.name || '未知群聊',\n platform: metaObj.platform || KNOWN_PLATFORMS.UNKNOWN,\n type: (metaObj.type as ChatType) || ChatType.GROUP,\n groupId: metaObj.groupId,\n groupAvatar: metaObj.groupAvatar,\n ownerId: metaObj.ownerId,\n sourceSessionId: metaObj.sourceSessionId,\n }\n }\n }\n } catch {\n // 使用默认值\n }\n\n // 如果群名仍是默认值,使用文件名作为后备\n if (meta.name === '未知群聊') {\n meta.name = extractNameFromFilePath(filePath)\n }\n\n yield { type: 'meta', data: meta }\n\n // 收集成员和消息\n const members: ParsedMember[] = []\n const memberMapFromMessages = new Map<string, ParsedMember>()\n const messageBatch: ParsedMessage[] = []\n\n // 流式解析\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n readStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n // 单次流式遍历同时读取成员和消息,避免用正则截断含嵌套数组的 members。\n const pipeline = chain([readStream, parser(), pick({ filter: /^(?:members|messages)\\.\\d+$/ }), streamValues()])\n\n // 用于收集批次的临时数组\n const batchCollector: ParsedMessage[] = []\n\n pipeline.on('data', ({ value }: { value: ChatLabMember | ChatLabMessage }) => {\n if (!('sender' in value)) {\n members.push({\n platformId: value.platformId,\n accountName: value.accountName,\n groupNickname: value.groupNickname,\n aliases: value.aliases,\n avatar: value.avatar,\n roles: value.roles,\n })\n return\n }\n\n const msg = value\n\n // 如果前面没解析到 members,从消息中收集\n if (members.length === 0) {\n memberMapFromMessages.set(msg.sender, {\n platformId: msg.sender,\n accountName: msg.accountName,\n groupNickname: msg.groupNickname,\n })\n }\n\n batchCollector.push({\n senderPlatformId: msg.sender,\n senderAccountName: msg.accountName,\n senderGroupNickname: msg.groupNickname,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content,\n platformMessageId: msg.platformMessageId,\n replyToMessageId: msg.replyToMessageId,\n })\n\n messagesProcessed++\n\n // 达到批次大小\n if (batchCollector.length >= batchSize) {\n messageBatch.push(...batchCollector)\n batchCollector.length = 0\n\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n onProgress?.(progress)\n }\n })\n\n pipeline.on('end', () => {\n // 收集剩余消息\n if (batchCollector.length > 0) {\n messageBatch.push(...batchCollector)\n }\n resolve()\n })\n\n pipeline.on('error', reject)\n })\n\n // 发送成员\n if (members.length > 0) {\n yield { type: 'members', data: members }\n } else if (memberMapFromMessages.size > 0) {\n yield { type: 'members', data: Array.from(memberMapFromMessages.values()) }\n }\n\n // 分批发送消息\n for (let i = 0; i < messageBatch.length; i += batchSize) {\n const batch = messageBatch.slice(i, i + batchSize)\n yield { type: 'messages', data: batch }\n }\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n // 记录解析摘要\n const memberCount = members.length > 0 ? members.length : memberMapFromMessages.size\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberCount} 个成员`)\n\n yield {\n type: 'done',\n data: {\n messageCount: messagesProcessed,\n memberCount,\n },\n }\n}\n\n// ==================== 导出解析器 ====================\n\nimport { withNativeChatlab } from '../native/chatlab-native'\n\n// parseChatLabAccelerated:优先走 Rust 内核,native 不可用/失败时自动回退本文件的 TS 实现\nexport { parseChatLab }\nexport const parseChatLabAccelerated = withNativeChatlab(parseChatLab)\n\nexport const parser_: Parser = {\n feature,\n parse: parseChatLabAccelerated,\n}\n\n// ==================== 导出格式模块 ====================\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","/**\n * Parser V2 - 工具函数\n */\n\nimport * as fs from 'fs'\nimport type { CreateProgress } from './types'\n\n/**\n * 获取文件大小\n */\nexport function getFileSize(filePath: string): number {\n return fs.statSync(filePath).size\n}\n\n/**\n * 读取文件头部指定字节数\n */\nexport function readFileHeadBytes(filePath: string, size: number): string {\n const fd = fs.openSync(filePath, 'r')\n const buffer = Buffer.alloc(size)\n const bytesRead = fs.readSync(fd, buffer, 0, size, 0)\n fs.closeSync(fd)\n return buffer.slice(0, bytesRead).toString('utf-8')\n}\n\n/**\n * 创建进度对象\n */\nexport const createProgress: CreateProgress = (stage, bytesRead, totalBytes, messagesProcessed, message) => {\n const percentage = totalBytes > 0 ? Math.min(99, Math.round((bytesRead / totalBytes) * 100)) : 0\n return {\n stage,\n bytesRead,\n totalBytes,\n messagesProcessed,\n percentage: stage === 'done' ? 100 : percentage,\n message,\n }\n}\n\n/**\n * 解析 ISO 时间戳或毫秒时间戳\n * @returns 秒级时间戳,如果解析失败返回 null\n */\nexport function parseTimestamp(value: string | number): number | null {\n if (typeof value === 'string') {\n // ISO 字符串格式:2017-12-30T03:24:36.000Z\n const parsed = Date.parse(value)\n if (isNaN(parsed)) return null\n return Math.floor(parsed / 1000)\n } else if (typeof value === 'number') {\n // 毫秒时间戳\n if (isNaN(value)) return null\n return Math.floor(value / 1000)\n }\n return null\n}\n\n/**\n * 验证年份是否合理(2000年以后)\n */\nexport function isValidYear(timestampSeconds: number): boolean {\n return new Date(timestampSeconds * 1000).getFullYear() >= 2000\n}\n\n/**\n * 格式化文件大小\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n","/**\n * Loader for the optional Rust parsing kernels (@openchatlab/parser-native).\n *\n * The native module is a local-build artifact: contributors without a Rust\n * toolchain, published CLI installs and unsupported platforms simply fall\n * back to the pure-TS parsers. Loading uses a runtime require so bundlers\n * (electron-vite / tsup) never try to inline the .node binary.\n */\n\nimport { createRequire } from 'node:module'\n\nimport type { NativeParser } from '@openchatlab/parser-native'\nimport { PARSER_FORMAT_IDS, normalizeParserFormatId } from '../format-ids'\n\nexport interface NativeParserModule {\n NativeParser: typeof NativeParser\n}\n\n/** Load-state snapshot for startup observability (see node-runtime logNativeParserStatus). */\nexport interface NativeParserStatus {\n available: boolean\n /** true when CHATLAB_DISABLE_NATIVE_PERF=1 forces the TS parsers. */\n disabled: boolean\n /** Load failure reason when unavailable (e.g. module not shipped, ABI mismatch). */\n error?: string\n}\n\nlet cachedModule: NativeParserModule | null | undefined\nlet cachedLoadError: string | undefined\n\nfunction isNativeDisabled(): boolean {\n return process.env.CHATLAB_DISABLE_NATIVE_PERF === '1'\n}\n\nfunction isNativeParserModule(value: unknown): value is NativeParserModule {\n return typeof (value as { NativeParser?: unknown } | null)?.NativeParser === 'function'\n}\n\nfunction requireNativeModule(): NativeParserModule {\n const requireFn = createRequire(import.meta.url)\n const module = requireFn('@openchatlab/parser-native') as unknown\n if (!isNativeParserModule(module)) {\n throw new Error('Native parser module missing NativeParser export')\n }\n return module\n}\n\n/**\n * Returns the native module, or null when disabled/unavailable.\n * The module itself is cached; the env switch is evaluated on every call so\n * tests can toggle it at runtime.\n */\nexport function loadNativeParser(): NativeParserModule | null {\n if (isNativeDisabled()) return null\n if (cachedModule !== undefined) return cachedModule\n try {\n cachedModule = requireNativeModule()\n } catch (error) {\n cachedModule = null\n cachedLoadError = error instanceof Error ? error.message : String(error)\n }\n return cachedModule\n}\n\n/** Report whether the Rust kernels can load in this process, without throwing. */\nexport function getNativeParserStatus(): NativeParserStatus {\n if (isNativeDisabled()) return { available: false, disabled: true }\n const module = loadNativeParser()\n return { available: module !== null, disabled: false, error: module ? undefined : cachedLoadError }\n}\n\n/**\n * Report whether the currently loaded binary understands one kernel id.\n *\n * This is intentionally stricter than module availability: an older `.node`\n * can load successfully while not containing a newly added format. The\n * constructor only validates the id and stores the path; it does not read the\n * file, so an empty probe path has no side effects.\n */\nexport function isNativeFormatAvailable(formatId: string): boolean {\n const module = loadNativeParser()\n if (!module) return false\n\n try {\n new module.NativeParser(resolveNativeKernelId(formatId), '')\n return true\n } catch {\n return false\n }\n}\n\nfunction resolveNativeKernelId(formatId: string): string {\n const canonicalId = normalizeParserFormatId(formatId)\n if (canonicalId === PARSER_FORMAT_IDS.QQ_SHUAKAMI) return 'shuakami-qq-exporter'\n if (canonicalId === PARSER_FORMAT_IDS.ECHOTRACE) return 'weflow'\n return canonicalId\n}\n","/**\n * Unified Rust-accelerated parsing with transparent TS fallback.\n *\n * One wrapper serves every native-first format: it loads the native module,\n * constructs the shared Rust `NativeParser`, polls progress, pumps members\n * and message batches, and falls back to the pure-TS parser when the native\n * module is unavailable, disabled via CHATLAB_DISABLE_NATIVE_PERF=1, or the\n * Rust kernel fails (kernels fail before any data event is emitted, so a TS\n * restart is always safe).\n *\n * Per-format specifics live in a small `NativeFormatAdapter`: the Rust kernel\n * id, plus mappers from the unified native structs (and the kernel's meta\n * JSON) to ParseEvent payloads. Event order and payload shapes must stay\n * identical to the TS parser of the same format (verified by parity tests).\n */\n\nimport type { NativeMember, NativeMessage, NativeParser } from '@openchatlab/parser-native'\nimport type { ParseEvent, ParseOptions, ParsedMember, ParsedMessage, ParsedMeta } from '../types'\nimport { getFileSize, createProgress } from '../utils'\nimport { loadNativeParser } from './loader'\n\nconst PROGRESS_POLL_INTERVAL_MS = 200\n\nexport type ParseGenerator = (options: ParseOptions) => AsyncGenerator<ParseEvent, void, unknown>\n\nexport interface NativeFormatAdapter {\n /** Kernel id understood by the Rust NativeParser constructor. */\n kernelId: string\n /** Human-readable format label used in import logs. */\n label: string\n /** Map the kernel's meta JSON (parsed) to the ParsedMeta event payload. */\n mapMeta: (metaJson: unknown) => ParsedMeta\n /** Map native members to the ParsedMember event payload. */\n mapMembers: (members: NativeMember[], metaJson: unknown) => ParsedMember[]\n /** Map one native message to the ParsedMessage event payload. */\n mapMessage: (message: NativeMessage, metaJson: unknown) => ParsedMessage\n /** Optional format-specific summary lines emitted after a successful parse. */\n completionLogs?: (metaJson: unknown) => string[]\n}\n\nasync function* pumpNativeParser(\n adapter: NativeFormatAdapter,\n options: ParseOptions,\n parser: NativeParser,\n fallbackAfterNativeFailure: ParseGenerator\n): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n const totalBytes = getFileSize(filePath)\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n onLog?.(\n 'info',\n `[NativeParser] Parsing ${adapter.label} with Rust kernel, size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`\n )\n\n const progressTimer = setInterval(() => {\n const progress = parser.progress()\n onProgress?.(\n createProgress(\n 'parsing',\n progress.bytesRead,\n totalBytes,\n progress.messagesProcessed,\n `已处理 ${progress.messagesProcessed} 条消息...`\n )\n )\n }, PROGRESS_POLL_INTERVAL_MS)\n\n try {\n await parser.parse()\n } catch (error) {\n onLog?.(\n 'warn',\n `[NativeParser] Rust parse failed, falling back to TS parser: ${error instanceof Error ? error.message : String(error)}`\n )\n // Only progress events have been emitted so far — safe to restart with TS.\n yield* fallbackAfterNativeFailure(options)\n return\n } finally {\n clearInterval(progressTimer)\n }\n\n const metaJson: unknown = JSON.parse(parser.metaJson())\n yield { type: 'meta', data: adapter.mapMeta(metaJson) }\n\n const members = adapter.mapMembers(parser.takeMembers(), metaJson)\n yield { type: 'members', data: members }\n\n let messagesProcessed = 0\n while (true) {\n const batch = parser.takeBatch(batchSize)\n if (!batch) break\n messagesProcessed += batch.length\n yield { type: 'messages', data: batch.map((message) => adapter.mapMessage(message, metaJson)) }\n // The consumer typically does synchronous DB writes per batch; yield a\n // macrotask so the event loop can flush pending I/O (e.g. SSE progress\n // events) instead of being starved until all batches are delivered.\n await new Promise((resolve) => setImmediate(resolve))\n }\n\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${members.length} 个成员`)\n for (const message of adapter.completionLogs?.(metaJson) ?? []) {\n onLog?.('info', message)\n }\n\n yield { type: 'done', data: { messageCount: messagesProcessed, memberCount: members.length } }\n}\n\n/** Wrap a TS parse generator with native acceleration for the given format. */\nexport function createNativeFirstParser(\n adapter: NativeFormatAdapter,\n fallback: ParseGenerator,\n fallbackAfterNativeFailure: ParseGenerator = fallback\n): ParseGenerator {\n return async function* (options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const native = loadNativeParser()\n if (!native) {\n yield* fallback(options)\n return\n }\n\n let parser: NativeParser\n try {\n parser = new native.NativeParser(\n adapter.kernelId,\n options.filePath,\n options.formatOptions ? JSON.stringify(options.formatOptions) : undefined\n )\n } catch (error) {\n options.onLog?.(\n 'warn',\n `[NativeParser] Failed to create Rust parser, falling back to TS parser: ${error instanceof Error ? error.message : String(error)}`\n )\n yield* fallback(options)\n return\n }\n\n yield* pumpNativeParser(adapter, options, parser, fallbackAfterNativeFailure)\n }\n}\n","/**\n * ChatLab JSON adapter for the unified native-first wrapper.\n *\n * Payload shapes are identical to the pure-TS parser in formats/chatlab.ts\n * for spec-compliant files (verified by parity tests). The Rust kernel is\n * strict about the format spec: off-spec files (wrong field types, missing\n * required fields) make parse() fail, and the wrapper falls back to the TS\n * parser which replicates all JS passthrough quirks.\n */\n\nimport type { ChatType, MessageType } from '@openchatlab/shared-types'\nimport type { NativeMember, NativeMessage } from '@openchatlab/parser-native'\nimport type { ParsedMember, ParsedMessage, ParsedMeta } from '../types'\nimport { createNativeFirstParser, type NativeFormatAdapter, type ParseGenerator } from './create-native-parser'\n\n/** Shape of metaJson() from the Rust chatlab kernel. */\ninterface ChatlabMetaJson {\n name: string\n chatType: string\n platform: string\n groupId: string | null\n groupAvatar: string | null\n ownerId: string | null\n sourceSessionId: string | null\n /**\n * true when members came from the top-level `members` array;\n * false when collected from messages (TS emits different object shapes).\n */\n membersFromHead: boolean\n}\n\nfunction toParsedMember(member: NativeMember, fromHead: boolean): ParsedMember {\n if (!fromHead) {\n // Members collected from messages carry only these three keys in the TS\n // parser; keep the object shape identical.\n return {\n platformId: member.platformId,\n accountName: member.accountName,\n groupNickname: member.groupNickname,\n }\n }\n return {\n platformId: member.platformId,\n accountName: member.accountName,\n groupNickname: member.groupNickname,\n aliases: member.aliases,\n avatar: member.avatar,\n // Role objects pass through with their original key set ({id} or {id, name}).\n roles: member.roles?.map((role) => (role.name !== undefined ? { id: role.id, name: role.name } : { id: role.id })),\n }\n}\n\nconst chatlabAdapter: NativeFormatAdapter = {\n kernelId: 'chatlab',\n label: 'ChatLab JSON',\n\n mapMeta(metaJson: unknown): ParsedMeta {\n const meta = metaJson as ChatlabMetaJson\n return {\n name: meta.name,\n platform: meta.platform,\n type: meta.chatType as ChatType,\n groupId: meta.groupId ?? undefined,\n groupAvatar: meta.groupAvatar ?? undefined,\n ownerId: meta.ownerId ?? undefined,\n sourceSessionId: meta.sourceSessionId ?? undefined,\n }\n },\n\n mapMembers(members: NativeMember[], metaJson: unknown): ParsedMember[] {\n const fromHead = (metaJson as ChatlabMetaJson).membersFromHead\n return members.map((member) => toParsedMember(member, fromHead))\n },\n\n mapMessage(message: NativeMessage): ParsedMessage {\n return {\n senderPlatformId: message.senderPlatformId,\n senderAccountName: message.senderAccountName,\n senderGroupNickname: message.senderGroupNickname,\n // The kernel rejects non-number timestamps, so this is always set.\n timestamp: message.timestamp as number,\n type: message.messageType as MessageType,\n // The kernel rejects a missing content key, so undefined here always\n // means the source value was JSON null.\n content: message.content ?? null,\n platformMessageId: message.platformMessageId,\n replyToMessageId: message.replyToMessageId,\n }\n },\n}\n\n/** Wrap the TS ChatLab parse generator with native acceleration. */\nexport function withNativeChatlab(fallback: ParseGenerator): ParseGenerator {\n return createNativeFirstParser(chatlabAdapter, fallback)\n}\n","/**\n * ChatLab JSONL 格式\n * 支持超大规模聊天记录的流式格式\n *\n * 格式特点:\n * - 每行一个 JSON 对象\n * - 第一行必须是 header(包含 chatlab 和 meta)\n * - 成员行(可选)在消息行之前\n * - 消息行按时间顺序排列\n * - 通过 _type 字段区分行类型:header / member / message\n *\n * 优势:\n * - 内存占用恒定,支持 GB 级文件\n * - 可逐行处理,支持流式导入\n * - 便于追加写入\n */\n\nimport * as fs from 'fs'\nimport * as readline from 'readline'\nimport * as path from 'path'\nimport { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { getFileSize, createProgress } from '../utils'\n\n// ==================== JSONL 行类型定义 ====================\n\n/** Header 行结构 */\ninterface JsonlHeader {\n _type: 'header'\n chatlab: {\n version: string\n exportedAt: number\n generator?: string\n description?: string\n }\n meta: {\n name: string\n platform: string\n type: string\n groupId?: string\n groupAvatar?: string\n ownerId?: string\n sourceSessionId?: string\n }\n}\n\n/** Member 行结构 */\ninterface JsonlMember {\n _type: 'member'\n platformId: string\n accountName: string\n groupNickname?: string\n aliases?: string[]\n avatar?: string\n roles?: Array<{ id: string; name?: string }>\n}\n\n/** Message 行结构 */\ninterface JsonlMessage {\n _type: 'message'\n sender: string\n platformMessageId?: string\n accountName: string\n groupNickname?: string\n timestamp: number\n type: number\n content: string | null\n replyToMessageId?: string\n}\n\n/** 任意 JSONL 行 */\ntype JsonlLine = JsonlHeader | JsonlMember | JsonlMessage\n\n// ==================== 辅助函数 ====================\n\n/**\n * 从文件名提取群名\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath)\n const name = basename.replace(/\\.jsonl$/i, '')\n return name || '未知群聊'\n}\n\n/**\n * 解析单行 JSONL\n */\nfunction parseLine(line: string): JsonlLine | null {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#')) {\n // 空行或注释行,跳过\n return null\n }\n try {\n return JSON.parse(trimmed) as JsonlLine\n } catch {\n // 解析失败,跳过该行\n return null\n }\n}\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.CHATLAB_JSONL,\n name: 'ChatLab JSONL',\n platform: KNOWN_PLATFORMS.UNKNOWN,\n priority: 51, // 低优先级,让其他格式先匹配\n extensions: ['.jsonl'],\n signatures: {\n // 跳过格式允许的空行和注释后,分别匹配首条数据中的 header 类型和 chatlab 对象。\n head: [\n /^\\uFEFF?(?:[ \\t]*(?:#[^\\r\\n]*)?\\r?\\n)*[ \\t]*\\{(?=[^\\r\\n]*\"_type\"\\s*:\\s*\"header\")(?=[^\\r\\n]*\"chatlab\"\\s*:)[^\\r\\n]*\\}/,\n ],\n },\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseChatLabJsonl(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n const totalBytes = getFileSize(filePath)\n let bytesRead = 0\n let messagesProcessed = 0\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n // 记录解析开始\n onLog?.('info', `开始解析 ChatLab JSONL 文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 用于收集成员和消息\n const members: ParsedMember[] = []\n const memberMap = new Map<string, ParsedMember>()\n const messageBatch: ParsedMessage[] = []\n let explicitMembersEmitted = false\n let meta: ParsedMeta | null = null\n let headerParsed = false\n\n // 创建逐行读取流\n const fileStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n const rl = readline.createInterface({\n input: fileStream,\n crlfDelay: Infinity,\n })\n\n // 跟踪已读取字节数\n fileStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n // 逐行解析\n for await (const line of rl) {\n const parsed = parseLine(line)\n if (!parsed) continue\n\n switch (parsed._type) {\n case 'header':\n if (!headerParsed) {\n meta = {\n name: parsed.meta.name || extractNameFromFilePath(filePath),\n platform: parsed.meta.platform || KNOWN_PLATFORMS.UNKNOWN,\n type: (parsed.meta.type as ChatType) || ChatType.GROUP,\n groupId: parsed.meta.groupId,\n groupAvatar: parsed.meta.groupAvatar,\n ownerId: parsed.meta.ownerId,\n sourceSessionId: parsed.meta.sourceSessionId,\n }\n headerParsed = true\n yield { type: 'meta', data: meta }\n }\n break\n\n case 'member':\n {\n const member: ParsedMember = {\n platformId: parsed.platformId,\n accountName: parsed.accountName,\n groupNickname: parsed.groupNickname,\n aliases: parsed.aliases,\n avatar: parsed.avatar,\n roles: parsed.roles,\n }\n members.push(member)\n memberMap.set(parsed.platformId, member)\n }\n break\n\n case 'message':\n // 如果还没有 header,尝试从文件名推断\n if (!meta) {\n meta = {\n name: extractNameFromFilePath(filePath),\n platform: KNOWN_PLATFORMS.UNKNOWN,\n type: ChatType.GROUP,\n }\n yield { type: 'meta', data: meta }\n }\n\n // 如果没有成员列表,从消息中收集\n if (!memberMap.has(parsed.sender)) {\n const inferredMember: ParsedMember = {\n platformId: parsed.sender,\n accountName: parsed.accountName,\n groupNickname: parsed.groupNickname,\n }\n memberMap.set(parsed.sender, inferredMember)\n }\n\n messageBatch.push({\n senderPlatformId: parsed.sender,\n senderAccountName: parsed.accountName,\n senderGroupNickname: parsed.groupNickname,\n timestamp: parsed.timestamp,\n type: parsed.type as MessageType,\n content: parsed.content,\n platformMessageId: parsed.platformMessageId,\n replyToMessageId: parsed.replyToMessageId,\n })\n messagesProcessed++\n\n // Flush each batch immediately so large files do not retain every message or flood progress IPC.\n if (messageBatch.length >= batchSize) {\n // JSONL 规范要求成员行位于消息之前;先发送显式成员,避免下游创建缺少头像的占位成员。\n if (members.length > 0 && !explicitMembersEmitted) {\n yield { type: 'members', data: members.slice() }\n explicitMembersEmitted = true\n }\n yield { type: 'messages', data: messageBatch.splice(0) }\n\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed.toLocaleString()} 条消息...`\n )\n yield { type: 'progress', data: progress }\n onProgress?.(progress)\n }\n break\n }\n }\n\n // 如果没有解析到 header,使用默认值\n if (!meta) {\n meta = {\n name: extractNameFromFilePath(filePath),\n platform: KNOWN_PLATFORMS.UNKNOWN,\n type: ChatType.GROUP,\n }\n yield { type: 'meta', data: meta }\n }\n\n // 发送成员\n if (members.length > 0) {\n if (!explicitMembersEmitted) {\n yield { type: 'members', data: members }\n }\n } else if (memberMap.size > 0) {\n yield { type: 'members', data: Array.from(memberMap.values()) }\n }\n\n // 发送不足一个批次的剩余消息\n if (messageBatch.length > 0) {\n yield { type: 'messages', data: messageBatch }\n }\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n // 记录解析摘要\n const memberCount = members.length > 0 ? members.length : memberMap.size\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberCount} 个成员`)\n\n yield {\n type: 'done',\n data: {\n messageCount: messagesProcessed,\n memberCount,\n },\n }\n}\n\n// ==================== 导出解析器 ====================\n\nexport const parser: Parser = {\n feature,\n parse: parseChatLabJsonl,\n}\n\n// ==================== 导出格式模块 ====================\n\nconst module_: FormatModule = {\n feature,\n parser,\n}\n\nexport default module_\n","/**\n * shuakami/qq-chat-exporter 格式解析器\n * 适配项目: https://github.com/shuakami/qq-chat-exporter\n * 版本: 通用(支持 V4.x 及后续版本)\n *\n * 文件结构:\n * - metadata: 元数据\n * - chatInfo: 聊天信息(包含群头像 avatar)\n * - statistics: 统计信息\n * - messages: 消息数组\n * - avatars: 用户头像(在 messages 之后)\n *\n * 名字字段说明:\n * - sendNickName: QQ原始昵称(始终存在)\n * - sendMemberName: 群昵称(可选)\n *\n * 显示名优先级: sendMemberName > sendNickName\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\nimport streamChain from 'stream-chain'\nimport { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { getFileSize, createProgress, readFileHeadBytes, parseTimestamp, isValidYear } from '../utils'\n\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\nconst { chain } = streamChain\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.QQ_SHUAKAMI,\n name: 'shuakami/qq-chat-exporter',\n platform: KNOWN_PLATFORMS.QQ,\n priority: 10,\n extensions: ['.json'],\n signatures: {\n head: [/QQChatExporter(?:\\s+V\\d+)?/, /\"version\"\\s*:\\s*\"\\d+\\./],\n requiredFields: ['metadata', 'chatInfo'],\n },\n}\n\n// ==================== 类型定义 ====================\n\ninterface V4RawMessage {\n senderUin?: string\n senderUid?: string\n sendNickName?: string\n sendMemberName?: string\n}\n\ninterface V4Message {\n messageId?: string // 消息的唯一 ID\n timestamp: string\n sender: {\n uid?: string\n uin?: string\n name: string\n }\n messageType?: number\n /** Legacy field name (pre-V6 exports). */\n isSystemMessage?: boolean\n /** Legacy field name (pre-V6 exports). */\n isRecalled?: boolean\n /** Current field name (V6+ exports). */\n system?: boolean\n /** Current field name (V6+ exports). */\n recalled?: boolean\n content: {\n text: string\n resources?: Array<{ type: string }>\n emojis?: Array<{ type: string }>\n reply?: {\n messageId?: string\n referencedMessageId?: string // 被引用消息的 ID\n senderName?: string\n content?: string\n }\n }\n rawMessage?: V4RawMessage\n}\n\ninterface MemberInfo {\n platformId: string\n accountName: string\n groupNickname: string | undefined\n avatar: string | undefined\n}\n\ninterface ChatInfo {\n name: string\n type: string\n avatar?: string\n selfUid?: string\n selfUin?: string\n}\n\n// ==================== 辅助函数 ====================\n\n/**\n * 从字符串中提取 JSON 对象(处理嵌套和转义)\n */\nfunction extractJsonObject(content: string, key: string): string | null {\n const searchStr = `\"${key}\":`\n const startIdx = content.indexOf(searchStr)\n if (startIdx === -1) return null\n\n let i = startIdx + searchStr.length\n while (i < content.length && /\\s/.test(content[i])) i++\n\n if (content[i] !== '{') return null\n\n let braceDepth = 0\n let inString = false\n let escape = false\n const objStart = i\n\n for (; i < content.length; i++) {\n const char = content[i]\n\n if (escape) {\n escape = false\n continue\n }\n\n if (char === '\\\\' && inString) {\n escape = true\n continue\n }\n\n if (char === '\"') {\n inString = !inString\n continue\n }\n\n if (!inString) {\n if (char === '{') braceDepth++\n if (char === '}') {\n braceDepth--\n if (braceDepth === 0) {\n return content.slice(objStart, i + 1)\n }\n }\n }\n }\n\n return null\n}\n\n/**\n * 从文件名提取名称\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath)\n return basename.replace(/\\.json$/i, '') || '未知群聊'\n}\n\n/**\n * 判断发送者 ID 是否是系统消息占位符(无真实发送者)\n */\nfunction isPlaceholderSenderId(id: string): boolean {\n return id === '0' || id === '未知'\n}\n\n/**\n * 计算真实 senders 数量以判断聊天类型(排除系统消息占位发送者)\n */\nfunction countSenders(content: string): number {\n const sendersStart = content.indexOf('\"senders\"')\n if (sendersStart === -1) return 0\n\n const arrayStart = content.indexOf('[', sendersStart)\n if (arrayStart === -1 || arrayStart - sendersStart > 20) return 0\n\n // 找到匹配的 ]\n let depth = 1\n let i = arrayStart + 1\n while (i < content.length && depth > 0) {\n if (content[i] === '[') depth++\n else if (content[i] === ']') depth--\n i++\n }\n\n if (depth !== 0) return 0\n\n const sendersContent = content.slice(arrayStart + 1, i - 1)\n const uidMatches = sendersContent.matchAll(/\"uid\"\\s*:\\s*\"([^\"]*)\"/g)\n let count = 0\n for (const match of uidMatches) {\n const uid = match[1]\n if (uid && !isPlaceholderSenderId(uid)) count++\n }\n return count\n}\n\n/**\n * 判断聊天类型:优先使用可信的 chatInfo.type,并用真实发送者数量纠正冲突的私聊标记。\n */\nfunction resolveChatType(chatInfoType: string | undefined, headContent: string): ChatType {\n if (chatInfoType === 'group') return ChatType.GROUP\n\n const sendersCount = countSenders(headContent)\n // QQ 私聊最多只有两个真实发送者;出现更多发送者时,显式 private 标记不可信。\n if (chatInfoType === 'private') return sendersCount > 2 ? ChatType.GROUP : ChatType.PRIVATE\n\n return sendersCount > 2 ? ChatType.GROUP : sendersCount > 0 ? ChatType.PRIVATE : ChatType.GROUP\n}\n\n// ==================== 消息类型转换 ====================\n\nfunction convertMessageType(\n messageType: number | undefined,\n content: V4Message['content'],\n isRecalled?: boolean\n): MessageType {\n if (isRecalled) return MessageType.RECALL\n\n // 检查资源类型\n if (content.resources?.length) {\n const resourceType = content.resources[0].type\n switch (resourceType) {\n case 'image':\n return MessageType.IMAGE\n case 'video':\n return MessageType.VIDEO\n case 'voice':\n case 'audio':\n return MessageType.VOICE\n case 'file':\n return MessageType.FILE\n case 'location':\n return MessageType.LOCATION\n }\n }\n\n if (content.emojis?.length) return MessageType.EMOJI\n\n // 根据文本内容判断\n const text = content.text?.trim() || ''\n if (text.includes('QQ红包') || text.includes('发出了红包') || text === '[红包]') return MessageType.RED_PACKET\n if (text.includes('转账') || text === '[转账]') return MessageType.TRANSFER\n if (text.includes('拍了拍') || text.includes('戳了戳') || text === '[拍一拍]') return MessageType.POKE\n if (text.includes('语音通话') || text.includes('视频通话') || text.includes('通话时长')) return MessageType.CALL\n if (text === '[分享]' || text === '[音乐]' || text === '[小程序]') return MessageType.SHARE\n if (text === '[链接]' || text === '[卡片消息]') return MessageType.LINK\n if (text === '[位置]' || text === '[地理位置]') return MessageType.LOCATION\n if (text === '[转发]' || text === '[聊天记录]') return MessageType.FORWARD\n\n // 根据 messageType 判断\n if (messageType === 3) return MessageType.IMAGE\n if (messageType === 7) return MessageType.VIDEO\n if (messageType === 9) return MessageType.REPLY\n\n return MessageType.TEXT\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseShuakamiQqV4(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n const totalBytes = getFileSize(filePath)\n let bytesRead = 0\n let messagesProcessed = 0\n let skippedMessages = 0 // 跳过的无效消息计数\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n // 记录解析开始\n onLog?.('info', `开始解析 shuakami/qq-chat-exporter 文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 读取文件头获取 meta 信息(增加到 500KB 以包含 chatInfo.avatar)\n const headContent = readFileHeadBytes(filePath, 500000)\n\n // 解析 chatInfo(包含群头像)\n let chatInfo: ChatInfo = { name: '未知群聊', type: 'group' }\n try {\n const chatInfoStr = extractJsonObject(headContent, 'chatInfo')\n if (chatInfoStr) {\n chatInfo = JSON.parse(chatInfoStr)\n }\n } catch {\n // 使用默认值\n }\n\n // 判断聊天类型(优先 chatInfo.type,senders 计数兜底)\n const chatType = resolveChatType(chatInfo.type, headContent)\n\n // QCE only exposes observed senders and can mix UIN/UID namespaces, so it\n // cannot guarantee that a chatInfo self ID resolves to an emitted member.\n // 发送 meta(包含群头像)\n const meta: ParsedMeta = {\n name: chatInfo.name || extractNameFromFilePath(filePath),\n platform: KNOWN_PLATFORMS.QQ,\n type: chatType,\n groupAvatar: chatInfo.avatar, // 从 chatInfo.avatar 提取群头像\n }\n yield { type: 'meta', data: meta }\n\n // 收集成员和消息\n const memberMap = new Map<string, MemberInfo>()\n const messageBatch: ParsedMessage[] = []\n\n // 流式解析消息\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n readStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n const pipeline = chain([readStream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n\n pipeline.on('data', ({ value }: { value: V4Message }) => {\n // 获取 platformId\n const platformId =\n value.sender.uin || value.sender.uid || value.rawMessage?.senderUin || value.rawMessage?.senderUid\n if (!platformId || isPlaceholderSenderId(platformId)) {\n skippedMessages++\n return\n }\n\n // 获取名字信息\n const raw = value.rawMessage\n const accountName = raw?.sendNickName || value.sender.name || platformId\n const groupNickname = raw?.sendMemberName || undefined\n\n // 更新成员信息\n const existingMember = memberMap.get(platformId)\n if (!existingMember) {\n memberMap.set(platformId, { platformId, accountName, groupNickname, avatar: undefined })\n } else {\n existingMember.accountName = accountName\n if (groupNickname) existingMember.groupNickname = groupNickname\n }\n\n // 解析时间戳\n const timestamp = parseTimestamp(value.timestamp)\n if (timestamp === null || !isValidYear(timestamp)) {\n skippedMessages++\n return\n }\n\n // 消息类型(system/recalled 为当前字段名,isSystemMessage/isRecalled 为旧字段名)\n const isSystem = value.system ?? value.isSystemMessage\n const isRecalled = value.recalled ?? value.isRecalled\n const type = isSystem ? MessageType.SYSTEM : convertMessageType(value.messageType, value.content, isRecalled)\n\n // 文本内容\n let textContent = value.content?.text || ''\n if (isRecalled) textContent = '[已撤回] ' + textContent\n\n messageBatch.push({\n platformMessageId: value.messageId, // 消息的平台原始 ID\n senderPlatformId: platformId,\n senderAccountName: accountName,\n senderGroupNickname: groupNickname,\n timestamp,\n type,\n content: textContent || null,\n replyToMessageId: value.content?.reply?.referencedMessageId, // 被引用消息的 ID\n })\n\n messagesProcessed++\n if (messagesProcessed % batchSize === 0) {\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n onProgress?.(progress)\n }\n })\n\n pipeline.on('end', resolve)\n pipeline.on('error', reject)\n })\n\n // 消息解析完成后,读取文件末尾的 avatars 对象\n const avatarsMap = new Map<string, string>()\n try {\n // 读取文件末尾(avatars 通常在最后)\n const stats = fs.statSync(filePath)\n const tailSize = Math.min(stats.size, 5000000) // 最多读取 5MB\n const fd = fs.openSync(filePath, 'r')\n const buffer = Buffer.alloc(tailSize)\n fs.readSync(fd, buffer, 0, tailSize, stats.size - tailSize)\n fs.closeSync(fd)\n\n const tailContent = buffer.toString('utf-8')\n const avatarsStr = extractJsonObject(tailContent, 'avatars')\n if (avatarsStr) {\n const avatarsObj = JSON.parse(avatarsStr) as Record<string, string>\n for (const [uin, avatar] of Object.entries(avatarsObj)) {\n if (avatar && typeof avatar === 'string' && avatar.startsWith('data:image/')) {\n avatarsMap.set(uin, avatar)\n }\n }\n }\n } catch {\n // avatars 解析失败,继续不带头像\n }\n\n // 将头像关联到成员\n for (const member of memberMap.values()) {\n const avatar = avatarsMap.get(member.platformId)\n if (avatar) member.avatar = avatar\n }\n\n // 发送成员\n const members: ParsedMember[] = Array.from(memberMap.values()).map((m) => ({\n platformId: m.platformId,\n accountName: m.accountName,\n groupNickname: m.groupNickname,\n avatar: m.avatar,\n }))\n yield { type: 'members', data: members }\n\n // 分批发送消息\n for (let i = 0; i < messageBatch.length; i += batchSize) {\n const batch = messageBatch.slice(i, i + batchSize)\n yield { type: 'messages', data: batch }\n }\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n // 记录解析摘要\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberMap.size} 个成员`)\n if (skippedMessages > 0) {\n onLog?.('info', `跳过 ${skippedMessages} 条无效消息(缺少发送者ID或时间戳无效)`)\n }\n\n yield {\n type: 'done',\n data: { messageCount: messagesProcessed, memberCount: memberMap.size },\n }\n}\n\n// ==================== 导出 ====================\n\nimport { withShuakamiQqNative } from '../native/shuakami-qq-native'\n\nexport { parseShuakamiQqV4 }\nexport const parseShuakamiQqV4Accelerated = withShuakamiQqNative(parseShuakamiQqV4)\n\nexport const parser_: Parser = {\n feature,\n parse: parseShuakamiQqV4Accelerated,\n}\n\nimport { shuakamiQqPreprocessor } from './shuakami-qq-preprocessor'\nexport const preprocessor = shuakamiQqPreprocessor\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n preprocessor: shuakamiQqPreprocessor,\n}\n\nexport default module_\n","/**\n * shuakami/qq-chat-exporter 格式预处理器\n * 适配项目: https://github.com/shuakami/qq-chat-exporter\n *\n * 功能:移除 content.html、content.raw 等冗余字段,减小文件体积\n * 阈值:>50MB 自动触发预处理\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as os from 'os'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\nimport streamChain from 'stream-chain'\nimport type { ParseProgress, Preprocessor } from '../types'\nimport { getFileSize, createProgress, readFileHeadBytes } from '../utils'\n\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\nconst { chain } = streamChain\n\n/** 预处理阈值:50MB */\nconst PREPROCESS_THRESHOLD = 50 * 1024 * 1024\n\n/**\n * 获取临时目录\n */\nfunction getTempDir(): string {\n const configuredRoot = process.env.CHATLAB_TEMP_ROOT?.trim()\n const root = configuredRoot\n ? path.resolve(configuredRoot)\n : path.resolve(process.platform === 'darwin' ? '/private/tmp/chatlab' : path.join(os.tmpdir(), 'chatlab'))\n return path.join(root, 'parser')\n}\n\n/**\n * 确保目录存在\n */\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n }\n}\n\n/**\n * 从字符串中提取 JSON 对象(处理嵌套和转义)\n */\nfunction extractJsonObject(content: string, key: string): string | null {\n const searchStr = `\"${key}\":`\n const startIdx = content.indexOf(searchStr)\n if (startIdx === -1) return null\n\n let i = startIdx + searchStr.length\n while (i < content.length && /\\s/.test(content[i])) i++\n\n if (content[i] !== '{') return null\n\n let braceDepth = 0\n let inString = false\n let escape = false\n const objStart = i\n\n for (; i < content.length; i++) {\n const char = content[i]\n\n if (escape) {\n escape = false\n continue\n }\n\n if (char === '\\\\' && inString) {\n escape = true\n continue\n }\n\n if (char === '\"') {\n inString = !inString\n continue\n }\n\n if (!inString) {\n if (char === '{') braceDepth++\n if (char === '}') {\n braceDepth--\n if (braceDepth === 0) {\n return content.slice(objStart, i + 1)\n }\n }\n }\n }\n\n return null\n}\n\n/**\n * 从文件末尾读取 avatars 对象\n */\nfunction readAvatarsFromFile(filePath: string): string | null {\n try {\n const stats = fs.statSync(filePath)\n const tailSize = Math.min(stats.size, 5000000) // 最多读取 5MB\n const fd = fs.openSync(filePath, 'r')\n const buffer = Buffer.alloc(tailSize)\n fs.readSync(fd, buffer, 0, tailSize, stats.size - tailSize)\n fs.closeSync(fd)\n\n const tailContent = buffer.toString('utf-8')\n return extractJsonObject(tailContent, 'avatars')\n } catch {\n return null\n }\n}\n\n/**\n * shuakami/qq-chat-exporter JSON 消息的精简结构\n */\ninterface ShuakamiQqSlimMessage {\n id?: string\n messageId?: string\n timestamp: number | string\n sender: {\n uid?: string\n uin?: string\n name: string\n }\n type?: string\n messageType?: number\n content: {\n text: string\n elements?: Array<{ type: string }>\n resources?: Array<{ type: string }>\n emojis?: Array<{ type: string }>\n reply?: {\n referencedMessageId?: unknown\n }\n }\n recalled?: boolean | null\n isRecalled?: boolean | null\n system?: boolean | null\n isSystemMessage?: boolean | null\n rawMessage?: {\n sendNickName?: string\n sendMemberName?: string\n senderUin?: string\n senderUid?: string\n }\n}\n\n/**\n * 精简 shuakami/qq-chat-exporter JSON 消息对象\n */\nfunction slimMessage(msg: Record<string, unknown>): ShuakamiQqSlimMessage {\n const sender = msg.sender as { uin?: string; uid?: string; name?: string } | undefined\n const content = msg.content as Record<string, unknown> | undefined\n const rawMessage = msg.rawMessage as Record<string, unknown> | undefined\n\n const slimContent: ShuakamiQqSlimMessage['content'] = {\n text: (content?.text as string) || '',\n }\n\n if (content?.elements && Array.isArray(content.elements)) {\n slimContent.elements = (content.elements as Array<{ type: string }>).map((e) => ({\n type: e.type,\n }))\n }\n\n if (content?.resources && Array.isArray(content.resources)) {\n slimContent.resources = (content.resources as Array<{ type: string }>).map((r) => ({\n type: r.type,\n }))\n }\n\n if (content?.emojis && Array.isArray(content.emojis)) {\n slimContent.emojis = (content.emojis as Array<{ type: string }>).map((e) => ({\n type: e.type,\n }))\n }\n\n if (content?.reply && typeof content.reply === 'object') {\n const reply = content.reply as Record<string, unknown>\n if (reply.referencedMessageId !== undefined) {\n slimContent.reply = { referencedMessageId: reply.referencedMessageId }\n }\n }\n\n const slimMsg: ShuakamiQqSlimMessage = {\n timestamp: msg.timestamp as number | string,\n sender: { name: sender?.name || '' },\n content: slimContent,\n }\n\n // 旧格式字段\n if (msg.id) slimMsg.id = msg.id as string\n if (msg.type) slimMsg.type = msg.type as string\n if (msg.recalled !== undefined) slimMsg.recalled = msg.recalled as boolean | null\n if (msg.system !== undefined) slimMsg.system = msg.system as boolean | null\n\n // V4 新格式字段\n if (msg.messageId !== undefined) slimMsg.messageId = msg.messageId as string\n if (msg.messageType !== undefined) slimMsg.messageType = msg.messageType as number\n if (msg.isRecalled !== undefined) slimMsg.isRecalled = msg.isRecalled as boolean | null\n if (msg.isSystemMessage !== undefined) slimMsg.isSystemMessage = msg.isSystemMessage as boolean | null\n\n // sender 字段\n if (sender?.uin) slimMsg.sender.uin = sender.uin\n if (sender?.uid) slimMsg.sender.uid = sender.uid\n\n // V4 新增:保留 rawMessage 中的关键名字字段\n if (rawMessage) {\n slimMsg.rawMessage = {}\n if (rawMessage.sendNickName) slimMsg.rawMessage.sendNickName = rawMessage.sendNickName as string\n if (rawMessage.sendMemberName) slimMsg.rawMessage.sendMemberName = rawMessage.sendMemberName as string\n if (rawMessage.senderUin) slimMsg.rawMessage.senderUin = rawMessage.senderUin as string\n if (rawMessage.senderUid) slimMsg.rawMessage.senderUid = rawMessage.senderUid as string\n }\n\n return slimMsg\n}\n\n/**\n * 预处理 shuakami/qq-chat-exporter JSON 文件\n */\nasync function preprocessShuakamiQqJson(\n inputPath: string,\n onProgress?: (progress: ParseProgress) => void\n): Promise<string> {\n const totalBytes = getFileSize(inputPath)\n let bytesRead = 0\n let messagesProcessed = 0\n\n const tempDir = getTempDir()\n ensureDir(tempDir)\n const outputFilename = `slim_${Date.now()}_${path.basename(inputPath)}`\n const outputPath = path.join(tempDir, outputFilename)\n\n onProgress?.(createProgress('parsing', 0, totalBytes, 0, ''))\n\n // 先从原文件读取 avatars(因为它在文件末尾,消息处理时可能无法访问)\n const avatarsStr = readAvatarsFromFile(inputPath)\n\n // Keep the same byte window as parseShuakamiQqV4. Reading decoded stream chunks and\n // counting string.length can include substantially more than 500,000 bytes\n // when the head contains multi-byte UTF-8 text.\n const headContent = readFileHeadBytes(inputPath, 500000)\n let chatInfo: Record<string, unknown> = { name: '未知群聊', type: 'group' }\n let metadata: Record<string, unknown> | undefined\n let statistics: Record<string, unknown> | undefined\n\n try {\n const chatInfoJson = extractJsonObject(headContent, 'chatInfo')\n if (chatInfoJson) chatInfo = JSON.parse(chatInfoJson)\n } catch {\n // 使用默认值\n }\n if (!chatInfo.name) {\n chatInfo.name = path.basename(inputPath).replace(/\\.json$/i, '') || '未知群聊'\n }\n\n try {\n const metadataMatch = headContent.match(/\"metadata\"\\s*:\\s*(\\{[^}]+\\})/)\n if (metadataMatch) metadata = JSON.parse(metadataMatch[1])\n } catch {\n // 忽略\n }\n\n try {\n const statisticsMatch = headContent.match(/\"statistics\"\\s*:\\s*(\\{[\\s\\S]*?\\})\\s*,\\s*\"messages\"/)\n if (statisticsMatch) statistics = JSON.parse(statisticsMatch[1])\n } catch {\n // 解析失败时忽略\n }\n\n return new Promise((resolve, reject) => {\n onProgress?.(createProgress('parsing', 0, totalBytes, 0, ''))\n\n const readStream = fs.createReadStream(inputPath, { encoding: 'utf-8' })\n const writeStream = fs.createWriteStream(outputPath, { encoding: 'utf-8' })\n\n readStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n const header = { metadata, chatInfo, statistics, messages: [] }\n const headerJson = JSON.stringify(header)\n // 移除最后的 ]} 保留 [\n writeStream.write(headerJson.slice(0, -2) + '\\n')\n\n let isFirstMessage = true\n\n const pipeline = chain([readStream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n\n pipeline.on('data', ({ value }: { value: Record<string, unknown> }) => {\n const slimMsg = slimMessage(value)\n const msgJson = JSON.stringify(slimMsg)\n\n if (isFirstMessage) {\n writeStream.write(msgJson)\n isFirstMessage = false\n } else {\n writeStream.write(',\\n' + msgJson)\n }\n\n messagesProcessed++\n\n if (messagesProcessed % 10000 === 0) {\n onProgress?.(\n createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `预处理:已精简 ${messagesProcessed} 条消息...`\n )\n )\n }\n })\n\n pipeline.on('end', () => {\n // 关闭 messages 数组\n writeStream.write('\\n]')\n\n // 添加 avatars 对象(如果存在)\n if (avatarsStr) {\n writeStream.write(',\"avatars\":' + avatarsStr)\n }\n\n // 关闭 JSON 对象\n writeStream.write('}')\n writeStream.end()\n\n writeStream.on('finish', () => {\n onProgress?.(createProgress('done', totalBytes, totalBytes, messagesProcessed, ''))\n resolve(outputPath)\n })\n })\n\n pipeline.on('error', (err) => {\n writeStream.destroy()\n if (fs.existsSync(outputPath)) {\n fs.unlinkSync(outputPath)\n }\n onProgress?.(createProgress('error', bytesRead, totalBytes, messagesProcessed, err.message))\n reject(err)\n })\n })\n}\n\n/**\n * 清理临时文件\n */\nfunction cleanupTempFile(filePath: string): void {\n try {\n if (fs.existsSync(filePath) && path.dirname(path.resolve(filePath)) === path.resolve(getTempDir())) {\n fs.unlinkSync(filePath)\n }\n } catch {\n // 忽略清理错误\n }\n}\n\n/**\n * shuakami/qq-chat-exporter 预处理器\n */\nexport const shuakamiQqPreprocessor: Preprocessor = {\n needsPreprocess(_filePath: string, fileSize: number): boolean {\n return fileSize > PREPROCESS_THRESHOLD\n },\n\n preprocess: preprocessShuakamiQqJson,\n\n cleanup: cleanupTempFile,\n}\n","/** shuakami/qq-chat-exporter V4 adapter for the unified native-first wrapper. */\n\nimport { KNOWN_PLATFORMS, ChatType, type MessageType } from '@openchatlab/shared-types'\nimport type { NativeMember, NativeMessage } from '@openchatlab/parser-native'\nimport type { ParseEvent, ParseOptions, ParsedMember, ParsedMessage, ParsedMeta } from '../types'\nimport { getFileSize } from '../utils'\nimport { shuakamiQqPreprocessor } from '../formats/shuakami-qq-preprocessor'\nimport { createNativeFirstParser, type NativeFormatAdapter, type ParseGenerator } from './create-native-parser'\n\ninterface ShuakamiQqMetaJson {\n name: string\n chatType: string\n groupAvatar: string | null\n ownerId: string | null\n skippedMessages: number\n}\n\nexport const shuakamiQqAdapter: NativeFormatAdapter = {\n kernelId: 'shuakami-qq-exporter',\n label: 'shuakami/qq-chat-exporter',\n\n mapMeta(metaJson: unknown): ParsedMeta {\n const meta = metaJson as ShuakamiQqMetaJson\n return {\n name: meta.name,\n platform: KNOWN_PLATFORMS.QQ,\n type: meta.chatType === 'private' ? ChatType.PRIVATE : ChatType.GROUP,\n groupAvatar: meta.groupAvatar ?? undefined,\n ...(meta.ownerId ? { ownerId: meta.ownerId } : {}),\n }\n },\n\n mapMembers(members: NativeMember[]): ParsedMember[] {\n return members.map((member) => ({\n platformId: member.platformId,\n accountName: member.accountName,\n groupNickname: member.groupNickname,\n avatar: member.avatar,\n }))\n },\n\n mapMessage(message: NativeMessage): ParsedMessage {\n return {\n platformMessageId: message.platformMessageId,\n senderPlatformId: message.senderPlatformId,\n senderAccountName: message.senderAccountName,\n senderGroupNickname: message.senderGroupNickname,\n timestamp: message.timestamp as number,\n type: message.messageType as MessageType,\n content: message.content ?? null,\n replyToMessageId: message.replyToMessageId,\n }\n },\n\n completionLogs(metaJson: unknown): string[] {\n const skipped = (metaJson as ShuakamiQqMetaJson).skippedMessages\n return skipped > 0\n ? [\n `[NativeParser] Skipped ${skipped} shuakami/qq-chat-exporter messages with a missing sender ID or invalid timestamp`,\n ]\n : []\n },\n}\n\nasync function* fallbackAfterNativeFailure(\n fallback: ParseGenerator,\n options: ParseOptions\n): AsyncGenerator<ParseEvent, void, unknown> {\n if (!shuakamiQqPreprocessor.needsPreprocess(options.filePath, getFileSize(options.filePath))) {\n yield* fallback(options)\n return\n }\n\n let slimFilePath: string | null = null\n try {\n options.onLog?.('info', '[NativeParser] Preprocessing large export before TypeScript fallback')\n slimFilePath = await shuakamiQqPreprocessor.preprocess(options.filePath, options.onProgress)\n } catch (error) {\n options.onLog?.(\n 'warn',\n `[NativeParser] Fallback preprocessing failed; retrying the original export: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n yield* fallback(options)\n return\n }\n\n try {\n yield* fallback({ ...options, filePath: slimFilePath })\n } finally {\n if (slimFilePath) shuakamiQqPreprocessor.cleanup(slimFilePath)\n }\n}\n\n/** Wrap the pure TypeScript shuakami/qq-chat-exporter V4 parser with N-API acceleration. */\nexport function withShuakamiQqNative(fallback: ParseGenerator): ParseGenerator {\n return createNativeFirstParser(shuakamiQqAdapter, fallback, (options) =>\n fallbackAfterNativeFailure(fallback, options)\n )\n}\n","/**\n * shuakami/qq-chat-exporter chunked-jsonl 格式解析器\n * 适配项目: https://github.com/shuakami/qq-chat-exporter\n * 版本: 5.x(chunked-jsonl 分块格式)\n *\n * 文件结构:\n * - manifest.json: 元数据和分块信息\n * - metadata: 导出元数据\n * - chatInfo: 聊天信息\n * - chunked.chunks[]: 分块文件列表\n * - avatars?: 头像文件信息(V5.5+)\n * - chunks/: 分块目录\n * - chunk_0001.jsonl: 每行一条消息\n * - chunk_0002.jsonl: ...\n * - avatars.json: 头像数据(V5.5+,可选)\n *\n * 消息格式(sender 字段):\n * - uid: 用户 UID\n * - uin: QQ 号\n * - name: 展示名(优先群昵称,否则QQ昵称)\n * - nickname: QQ 昵称\n * - groupCard: 群昵称(群聊时存在)\n * - remark: 好友备注\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as readline from 'readline'\nimport { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { createProgress, parseTimestamp, isValidYear } from '../utils'\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.QQ_SHUAKAMI_CHUNKED,\n name: 'shuakami/qq-chat-exporter (chunked)',\n platform: KNOWN_PLATFORMS.QQ,\n priority: 5, // 比单文件版本优先级更高\n extensions: ['.json'],\n signatures: {\n // V5 使用 metadata.format,0.1.x 将格式标识移至 chunked.format。\n head: [/\"format\"\\s*:\\s*\"chunked-jsonl\"/, /\"chunked\"\\s*:\\s*\\{[^{}]*\"format\"\\s*:\\s*\"jsonl\"/],\n filename: [/^manifest\\.json$/i],\n filenameFallbackPatterns: [/\"chunked\"\\s*:\\s*\\{[\\s\\S]*?\"format\"\\s*:\\s*\"jsonl\"/],\n requiredFields: ['metadata', 'chatInfo'],\n },\n}\n\n// ==================== 类型定义 ====================\n\ninterface ChunkInfo {\n // V5.0 格式\n file?: string\n messages?: number\n bytes?: number\n // V5.5+ 格式\n index?: number\n fileName?: string\n relativePath?: string\n count?: number\n start?: string\n end?: string\n}\n\ninterface Manifest {\n metadata: {\n name?: string\n copyright?: string\n exportTime?: string\n version: string\n format?: string\n }\n chatInfo: {\n name: string\n type: string\n selfUid?: string\n selfUin?: string\n selfName?: string\n }\n statistics: {\n totalMessages: number\n chunkCount?: number\n timeRange?: {\n start: string\n end: string\n durationDays: number\n }\n messageTypes?: Record<string, number>\n senders?: Array<{\n uid: string\n name: string\n messageCount: number\n percentage: number\n }>\n }\n chunked: {\n format: string\n chunksDir: string\n chunkFileExt: string\n maxMessagesPerChunk: number\n maxBytesPerChunk: number\n chunks: ChunkInfo[]\n }\n avatars?: {\n file: string\n count: number\n }\n}\n\ninterface ChunkedMessage {\n id?: string\n seq?: string\n timestamp: number\n time?: string\n sender: {\n uid?: string\n uin?: string\n name: string\n nickname?: string // QQ 昵称\n groupCard?: string // 群昵称\n remark?: string // 好友备注\n }\n type: string\n content: {\n text: string\n html?: string\n elements?: Array<{ type: string; data?: Record<string, unknown> }>\n resources?: Array<{ type: string }>\n mentions?: Array<{ uid: string; name: string }>\n }\n recalled?: boolean\n system?: boolean\n}\n\ninterface MemberInfo {\n platformId: string\n accountName: string\n groupNickname: string | undefined\n avatar: string | undefined\n}\n\n// ==================== 消息类型转换 ====================\n\nfunction convertMessageType(msgType: string, content: ChunkedMessage['content'], isRecalled?: boolean): MessageType {\n if (isRecalled) return MessageType.RECALL\n\n // 系统消息\n if (msgType === 'system') return MessageType.SYSTEM\n\n // 检查资源类型\n if (content.resources?.length) {\n const resourceType = content.resources[0].type\n switch (resourceType) {\n case 'image':\n return MessageType.IMAGE\n case 'video':\n return MessageType.VIDEO\n case 'voice':\n case 'audio':\n return MessageType.VOICE\n case 'file':\n return MessageType.FILE\n case 'location':\n return MessageType.LOCATION\n }\n }\n\n // 检查表情\n if (content.elements?.some((e) => e.type === 'face' || e.type === 'market_face' || e.type === 'marketFace')) {\n return MessageType.EMOJI\n }\n\n // 根据文本内容判断\n const text = content.text?.trim() || ''\n if (text.includes('QQ红包') || text.includes('发出了红包') || text === '[红包]') return MessageType.RED_PACKET\n if (text.includes('转账') || text === '[转账]') return MessageType.TRANSFER\n if (text.includes('拍了拍') || text.includes('戳了戳') || text === '[拍一拍]') return MessageType.POKE\n if (text.includes('语音通话') || text.includes('视频通话') || text.includes('通话时长')) return MessageType.CALL\n if (text === '[分享]' || text === '[音乐]' || text === '[小程序]') return MessageType.SHARE\n if (text === '[链接]' || text === '[卡片消息]') return MessageType.LINK\n if (text === '[位置]' || text === '[地理位置]') return MessageType.LOCATION\n if (text === '[转发]' || text === '[聊天记录]') return MessageType.FORWARD\n\n return MessageType.TEXT\n}\n\n// ==================== 辅助函数 ====================\n\n/**\n * 读取并解析 manifest.json\n */\nfunction readManifest(manifestPath: string): Manifest {\n const content = fs.readFileSync(manifestPath, 'utf-8')\n return JSON.parse(content) as Manifest\n}\n\n/**\n * 获取 chunk 文件的相对路径(兼容新旧格式)\n */\nfunction getChunkRelativePath(chunk: ChunkInfo): string {\n // V5.5+ 使用 relativePath\n if (chunk.relativePath) return chunk.relativePath\n // V5.0 使用 file\n if (chunk.file) return chunk.file\n // 后备:使用 fileName 拼接\n if (chunk.fileName) return `chunks/${chunk.fileName}`\n throw new Error('无法获取 chunk 文件路径')\n}\n\n/**\n * 获取 chunk 的消息数量(兼容新旧格式)\n */\nfunction getChunkMessageCount(chunk: ChunkInfo): number {\n // V5.5+ 使用 count\n if (chunk.count !== undefined) return chunk.count\n // V5.0 使用 messages\n if (chunk.messages !== undefined) return chunk.messages\n return 0\n}\n\n/**\n * 计算所有 chunk 文件的总字节数\n */\nfunction calculateTotalBytes(manifest: Manifest, baseDir: string): number {\n let total = 0\n for (const chunk of manifest.chunked.chunks) {\n const relativePath = getChunkRelativePath(chunk)\n const chunkPath = path.join(baseDir, relativePath)\n if (fs.existsSync(chunkPath)) {\n total += fs.statSync(chunkPath).size\n }\n }\n return total\n}\n\n/**\n * 读取 avatars.json 文件\n */\nfunction readAvatars(baseDir: string, avatarsInfo?: { file: string; count: number }): Map<string, string> {\n const avatarsMap = new Map<string, string>()\n if (!avatarsInfo?.file) return avatarsMap\n\n const avatarsPath = path.join(baseDir, avatarsInfo.file)\n if (!fs.existsSync(avatarsPath)) return avatarsMap\n\n try {\n const content = fs.readFileSync(avatarsPath, 'utf-8')\n const avatars = JSON.parse(content) as Record<string, string>\n for (const [uin, avatar] of Object.entries(avatars)) {\n if (avatar && typeof avatar === 'string' && avatar.startsWith('data:image/')) {\n avatarsMap.set(uin, avatar)\n }\n }\n } catch {\n // 头像读取失败,继续不带头像\n }\n\n return avatarsMap\n}\n\n/**\n * 流式读取 JSONL 文件(带详细错误处理)\n */\nasync function* readJsonlFile(\n filePath: string,\n onParseError?: (lineNumber: number, error: string) => void\n): AsyncGenerator<ChunkedMessage> {\n let fileStream: fs.ReadStream | null = null\n let rl: readline.Interface | null = null\n let lineNumber = 0\n let parseErrorCount = 0\n const MAX_PARSE_ERRORS_TO_LOG = 10 // 最多记录前 10 个解析错误\n\n try {\n fileStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n rl = readline.createInterface({\n input: fileStream,\n crlfDelay: Infinity,\n })\n\n for await (const line of rl) {\n lineNumber++\n const trimmed = line.trim()\n if (!trimmed) continue\n\n try {\n yield JSON.parse(trimmed) as ChunkedMessage\n } catch (error) {\n parseErrorCount++\n // 只记录前几个解析错误,避免日志爆炸\n if (parseErrorCount <= MAX_PARSE_ERRORS_TO_LOG && onParseError) {\n const errorMsg = error instanceof Error ? error.message : String(error)\n onParseError(lineNumber, errorMsg)\n }\n // 继续处理下一行\n }\n }\n } finally {\n // 确保资源被正确清理\n if (rl) {\n rl.close()\n }\n if (fileStream) {\n fileStream.destroy()\n }\n }\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseChunkedJsonl(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n // 确定 manifest.json 路径和基础目录\n const manifestPath = filePath\n const baseDir = path.dirname(manifestPath)\n\n onLog?.('info', `开始解析 manifest: ${manifestPath}`)\n onLog?.('info', `基础目录: ${baseDir}`)\n\n // 读取 manifest\n let manifest: Manifest\n try {\n manifest = readManifest(manifestPath)\n onLog?.('info', `manifest 读取成功`)\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error)\n onLog?.('error', `无法读取 manifest.json: ${errorMsg}`)\n yield { type: 'error', data: new Error(`无法读取 manifest.json: ${errorMsg}`) }\n return\n }\n\n // 验证格式\n if (manifest.metadata.format !== 'chunked-jsonl' && manifest.chunked.format !== 'jsonl') {\n const format = manifest.metadata.format ?? manifest.chunked.format ?? 'unknown'\n onLog?.('error', `不支持的格式: ${format}`)\n yield { type: 'error', data: new Error(`不支持的格式: ${format}`) }\n return\n }\n\n const totalBytes = calculateTotalBytes(manifest, baseDir)\n let bytesRead = 0\n let messagesProcessed = 0\n let skippedMessages = 0\n let parseErrors = 0\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n onLog?.(\n 'info',\n `开始解析 chunked-jsonl 格式 (V${manifest.metadata.version}),共 ${manifest.chunked.chunks.length} 个分块,预计 ${manifest.statistics.totalMessages} 条消息,总大小约 ${Math.round(totalBytes / 1024 / 1024)}MB`\n )\n\n // 读取头像文件(如果存在)\n let avatarsMap: Map<string, string>\n try {\n avatarsMap = readAvatars(baseDir, manifest.avatars)\n if (avatarsMap.size > 0) {\n onLog?.('info', `已加载 ${avatarsMap.size} 个用户头像`)\n }\n } catch (error) {\n onLog?.('error', `读取头像文件失败: ${error instanceof Error ? error.message : String(error)}`)\n avatarsMap = new Map()\n }\n\n // QCE only exposes observed senders and can mix UIN/UID namespaces, so it\n // cannot guarantee that a chatInfo self ID resolves to an emitted member.\n // 发送 meta\n const chatType = manifest.chatInfo.type === 'group' ? ChatType.GROUP : ChatType.PRIVATE\n const meta: ParsedMeta = {\n name: manifest.chatInfo.name || '未知群聊',\n platform: KNOWN_PLATFORMS.QQ,\n type: chatType,\n }\n yield { type: 'meta', data: meta }\n onLog?.('info', `Meta 信息: name=${meta.name}, type=${meta.type}`)\n\n // 收集成员信息(不再收集所有消息到内存)\n const memberMap = new Map<string, MemberInfo>()\n\n // 当前消息批次(真正的流式处理:边读取边发送)\n let currentBatch: ParsedMessage[] = []\n\n // 遍历所有 chunk 文件\n for (let chunkIndex = 0; chunkIndex < manifest.chunked.chunks.length; chunkIndex++) {\n const chunkInfo = manifest.chunked.chunks[chunkIndex]\n const relativePath = getChunkRelativePath(chunkInfo)\n const chunkPath = path.join(baseDir, relativePath)\n const chunkMessageCount = getChunkMessageCount(chunkInfo)\n\n if (!fs.existsSync(chunkPath)) {\n onLog?.('error', `分块文件不存在: ${chunkPath}`)\n continue\n }\n\n const chunkSize = fs.statSync(chunkPath).size\n let chunkMessagesRead = 0\n let chunkParseErrors = 0\n\n onLog?.(\n 'info',\n `正在解析分块 [${chunkIndex + 1}/${manifest.chunked.chunks.length}]: ${relativePath} (预计 ${chunkMessageCount} 条消息, ${Math.round(chunkSize / 1024)}KB)`\n )\n\n // 流式读取 JSONL 文件(带错误处理)\n try {\n const parseErrorCallback = (lineNumber: number, errorMsg: string) => {\n onLog?.('error', `分块 ${relativePath} 第 ${lineNumber} 行 JSON 解析失败: ${errorMsg}`)\n }\n\n for await (const msg of readJsonlFile(chunkPath, parseErrorCallback)) {\n chunkMessagesRead++\n\n // 获取 platformId\n const platformId = msg.sender?.uin || msg.sender?.uid\n if (!platformId || platformId === '0' || platformId === '未知') {\n skippedMessages++\n continue\n }\n\n // 获取名字信息\n // nickname: QQ 昵称(原始昵称)\n // groupCard: 群昵称\n // name: 展示名(一般是 groupCard || nickname)\n const accountName = msg.sender?.nickname || msg.sender?.name || platformId\n const groupNickname = msg.sender?.groupCard || undefined\n\n // 更新成员信息\n const existingMember = memberMap.get(platformId)\n if (!existingMember) {\n memberMap.set(platformId, {\n platformId,\n accountName,\n groupNickname,\n avatar: avatarsMap.get(platformId),\n })\n } else {\n existingMember.accountName = accountName\n if (groupNickname) existingMember.groupNickname = groupNickname\n if (!existingMember.avatar) existingMember.avatar = avatarsMap.get(platformId)\n }\n\n // 解析时间戳(chunked 格式的时间戳是毫秒)\n const timestamp =\n typeof msg.timestamp === 'number' ? Math.floor(msg.timestamp / 1000) : parseTimestamp(msg.timestamp)\n\n if (timestamp === null || !isValidYear(timestamp)) {\n skippedMessages++\n continue\n }\n\n // 消息类型\n const type = msg.system ? MessageType.SYSTEM : convertMessageType(msg.type, msg.content, msg.recalled)\n\n // 文本内容\n let textContent = msg.content?.text || ''\n if (msg.recalled) textContent = '[已撤回] ' + textContent\n\n // 添加到当前批次\n currentBatch.push({\n platformMessageId: msg.id,\n senderPlatformId: platformId,\n senderAccountName: accountName,\n senderGroupNickname: groupNickname,\n timestamp,\n type,\n content: textContent || null,\n })\n\n messagesProcessed++\n\n // 达到批次大小时立即发送(真正的流式处理)\n if (currentBatch.length >= batchSize) {\n yield { type: 'messages', data: currentBatch }\n currentBatch = [] // 清空批次,释放内存\n\n // 发送进度\n const chunkProgress = chunkMessageCount > 0 ? chunkMessagesRead / chunkMessageCount : 0\n const chunkBytesRead = Math.floor(chunkProgress * chunkSize)\n const currentBytesRead = bytesRead + chunkBytesRead\n const progress = createProgress(\n 'parsing',\n currentBytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n yield { type: 'progress', data: progress }\n onProgress?.(progress)\n }\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error)\n onLog?.('error', `解析分块 ${relativePath} 时发生错误: ${errorMsg}`)\n chunkParseErrors++\n parseErrors++\n // 继续处理下一个分块,不中断整个解析\n }\n\n // 更新总字节读取\n bytesRead += chunkSize\n\n // 记录分块处理结果\n onLog?.(\n 'info',\n `分块 ${relativePath} 解析完成: 读取 ${chunkMessagesRead} 条, 有效 ${messagesProcessed} 条, 跳过 ${skippedMessages} 条${chunkParseErrors > 0 ? `, 错误 ${chunkParseErrors} 个` : ''}`\n )\n }\n\n // 发送剩余的消息批次\n if (currentBatch.length > 0) {\n yield { type: 'messages', data: currentBatch }\n onLog?.('info', `发送最后一批消息: ${currentBatch.length} 条`)\n }\n\n // 发送成员(包含头像)\n const members: ParsedMember[] = Array.from(memberMap.values()).map((m) => ({\n platformId: m.platformId,\n accountName: m.accountName,\n groupNickname: m.groupNickname,\n avatar: m.avatar,\n }))\n yield { type: 'members', data: members }\n onLog?.('info', `发送成员列表: ${members.length} 个成员`)\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberMap.size} 个成员`)\n if (skippedMessages > 0) {\n onLog?.('info', `跳过 ${skippedMessages} 条无效消息(缺少发送者ID或时间戳无效)`)\n }\n if (parseErrors > 0) {\n onLog?.('error', `解析过程中发生 ${parseErrors} 个错误`)\n }\n\n yield {\n type: 'done',\n data: { messageCount: messagesProcessed, memberCount: memberMap.size },\n }\n}\n\n// ==================== 导出 ====================\n\nexport const parser_: Parser = {\n feature,\n parse: parseChunkedJsonl,\n}\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","/**\n * WeFlow 导出格式解析器\n * 适配项目: WeFlow 聊天记录导出工具\n *\n * 特征:\n * - 顶层包含 weflow、session 和 messages 字段\n * - weflow 对象包含版本信息和导出时间\n * - session.wxid: ID(群聊以 @chatroom 结尾)\n * - session.type: \"群聊\" 或 \"私聊\"\n * - session.avatar: 群/用户头像(base64 Data URL)\n * - messages[].isSend: 1=发送者本人, 0=接收, null=系统\n * - messages[].senderUsername: 发送者ID\n * - messages[].senderDisplayName: 发送者显示名\n *\n * 注意:localType 字段不可信,不使用\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\nimport streamChain from 'stream-chain'\nimport { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { getFileSize, createProgress, readFileHeadBytes } from '../utils'\n\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\nconst { chain } = streamChain\n\n// ==================== 辅助函数 ====================\n\n/**\n * 从文件名提取聊天名称\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath)\n const name = basename.replace(/\\.json$/i, '')\n return name || '未知聊天'\n}\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.WEFLOW,\n name: 'WeFlow 导出',\n platform: KNOWN_PLATFORMS.WECHAT,\n priority: 15,\n extensions: ['.json'],\n signatures: {\n // weflow 对象是唯一识别特征\n // 注意:session.avatar 包含 base64 图片,可能很大,所以 messages 字段可能不在 8KB 文件头中\n // 只检测 weflow 和 session(它们在文件开头)\n head: [/\"weflow\"\\s*:\\s*\\{/],\n requiredFields: ['weflow', 'session'],\n },\n}\n\n// ==================== 数据结构 ====================\n\ninterface WeFlowSession {\n wxid: string\n nickname: string\n remark: string\n displayName: string\n type: '群聊' | '私聊'\n lastTimestamp: number\n messageCount: number\n avatar?: string // 群/用户头像(base64 Data URL)\n}\n\ninterface WeFlowMessage {\n localId: number\n createTime: number // Unix 时间戳(秒)\n formattedTime: string\n type: string // 中文消息类型\n localType: number // 不可信,不使用\n content: string\n isSend: number | null // 0=接收, 1=发送, null=系统\n senderUsername: string // 发送者ID\n senderDisplayName: string // 发送者显示名\n senderAvatarKey: string // 头像查找 key(通常与 senderUsername 相同)\n source: string\n}\n\n// ==================== 消息类型映射 ====================\n\n/**\n * 将 WeFlow 中文消息类型转换为标准 MessageType\n */\nfunction convertMessageType(typeStr: string): MessageType {\n switch (typeStr) {\n case '文本消息':\n return MessageType.TEXT\n case '图片消息':\n return MessageType.IMAGE\n case '语音消息':\n return MessageType.VOICE\n case '视频消息':\n return MessageType.VIDEO\n case '文件消息':\n return MessageType.FILE\n case '动画表情':\n return MessageType.EMOJI\n case '名片消息':\n return MessageType.CONTACT\n case '卡片式链接':\n case '图文消息':\n return MessageType.LINK\n case '位置消息':\n return MessageType.LOCATION\n case '红包卡片':\n return MessageType.RED_PACKET\n case '转账卡片':\n return MessageType.TRANSFER\n case '小程序分享':\n case '视频号直播卡片':\n return MessageType.SHARE\n case '引用消息':\n return MessageType.REPLY\n case '聊天记录合并转发':\n return MessageType.FORWARD\n case '系统消息':\n return MessageType.SYSTEM\n default:\n // 未知类型(xxxxx) 或其他\n return MessageType.OTHER\n }\n}\n\n// ==================== 头像信息结构 ====================\n// WeFlow 的 avatars 对象直接存储 base64 Data URL 字符串\n// 格式:{ \"wxid\": \"data:image/jpeg;base64,...\" }\n\n// ==================== 成员信息追踪 ====================\n\ninterface MemberInfo {\n platformId: string\n accountName: string\n avatar: string | undefined // 头像(base64 Data URL)\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseWeFlow(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n const totalBytes = getFileSize(filePath)\n let bytesRead = 0\n let messagesProcessed = 0\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n // 记录解析开始\n onLog?.('info', `开始解析 WeFlow 导出文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 读取文件头获取基本信息\n const headContent = readFileHeadBytes(filePath, 5000)\n\n // 使用流式读取获取完整的 session 对象(因为 session.avatar 可能很大)\n let session: WeFlowSession | null = null\n try {\n await new Promise<void>((resolve) => {\n const sessionStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n let sessionContent = ''\n let inSession = false\n let braceDepth = 0\n let inString = false\n let escape = false\n\n sessionStream.on('data', (chunk: string | Buffer) => {\n const str = typeof chunk === 'string' ? chunk : chunk.toString()\n\n for (let i = 0; i < str.length; i++) {\n const char = str[i]\n\n if (!inSession) {\n // 查找 \"session\": 的位置\n const searchStr = '\"session\":'\n if (str.slice(i, i + searchStr.length) === searchStr) {\n inSession = true\n i += searchStr.length - 1\n continue\n }\n } else {\n sessionContent += char\n\n if (escape) {\n escape = false\n continue\n }\n\n if (char === '\\\\' && inString) {\n escape = true\n continue\n }\n\n if (char === '\"') {\n inString = !inString\n continue\n }\n\n if (!inString) {\n if (char === '{') braceDepth++\n if (char === '}') {\n braceDepth--\n if (braceDepth === 0) {\n sessionStream.destroy()\n return\n }\n }\n }\n }\n }\n })\n\n sessionStream.on('close', () => {\n if (sessionContent) {\n try {\n session = JSON.parse(sessionContent) as WeFlowSession\n } catch {\n // 解析失败\n }\n }\n resolve()\n })\n\n sessionStream.on('error', () => resolve())\n })\n } catch {\n // 使用默认值\n }\n\n // 确定聊天类型\n // 1. 优先使用 session.type\n // 2. 或者通过 wxid 是否以 @chatroom 结尾判断\n let chatType = ChatType.GROUP\n // 使用局部变量避免 TypeScript 控制流分析问题\n const sessionData = session as WeFlowSession | null\n if (sessionData) {\n if (sessionData.type === '私聊') {\n chatType = ChatType.PRIVATE\n } else if (sessionData.type === '群聊') {\n chatType = ChatType.GROUP\n } else if (sessionData.wxid && !sessionData.wxid.endsWith('@chatroom')) {\n chatType = ChatType.PRIVATE\n }\n }\n\n // 确定聊天名称\n const chatName = sessionData?.displayName || sessionData?.nickname || extractNameFromFilePath(filePath)\n\n // 提取群ID(群聊类型时有值)\n // 群ID 格式:以 @chatroom 结尾\n const groupId = chatType === ChatType.GROUP && sessionData?.wxid ? sessionData.wxid : undefined\n\n // 解析 avatars 对象(头像)\n // avatars 格式:{ \"wxid\": { \"displayName\": \"...\", \"base64\": \"...\" } }\n // 注意:base64 不包含 Data URL 前缀,需要添加\n const avatarsMap = new Map<string, string>()\n\n /**\n * 从字符串中提取 avatars 对象内容\n * 正确处理 JSON 字符串中的花括号匹配(考虑字符串内的转义字符)\n */\n function extractAvatarsObject(content: string): string | null {\n const searchStr = '\"avatars\":'\n const startIdx = content.indexOf(searchStr)\n if (startIdx === -1) return null\n\n let i = startIdx + searchStr.length\n // 跳过空白字符\n while (i < content.length && /\\s/.test(content[i])) i++\n\n if (content[i] !== '{') return null\n\n // 从 { 开始匹配\n let braceDepth = 0\n let inString = false\n let escape = false\n const objStart = i\n\n for (; i < content.length; i++) {\n const char = content[i]\n\n if (escape) {\n escape = false\n continue\n }\n\n if (char === '\\\\' && inString) {\n escape = true\n continue\n }\n\n if (char === '\"') {\n inString = !inString\n continue\n }\n\n if (!inString) {\n if (char === '{') braceDepth++\n if (char === '}') {\n braceDepth--\n if (braceDepth === 0) {\n return content.slice(objStart, i + 1)\n }\n }\n }\n }\n\n return null\n }\n\n try {\n // 先尝试从文件头解析(适用于成员较少的聊天)\n const avatarsContent = extractAvatarsObject(headContent)\n if (avatarsContent) {\n // WeFlow 的 avatars 值直接是 base64 Data URL 字符串\n const avatarsObj = JSON.parse(avatarsContent) as Record<string, string>\n for (const [wxid, avatarDataUrl] of Object.entries(avatarsObj)) {\n if (avatarDataUrl && typeof avatarDataUrl === 'string') {\n avatarsMap.set(wxid, avatarDataUrl)\n }\n }\n }\n } catch {\n // avatars 解析失败,继续不带头像\n }\n\n // 如果文件头没有完整的 avatars(可能超出 5000 字节),尝试流式读取\n if (avatarsMap.size === 0) {\n try {\n await new Promise<void>((resolve) => {\n const avatarStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n let avatarsContent = ''\n let inAvatars = false\n let braceDepth = 0\n let inString = false\n let escape = false\n\n avatarStream.on('data', (chunk: string | Buffer) => {\n const str = typeof chunk === 'string' ? chunk : chunk.toString()\n\n for (let i = 0; i < str.length; i++) {\n const char = str[i]\n\n if (!inAvatars) {\n // 查找 \"avatars\": 的位置\n const searchStr = '\"avatars\":'\n if (str.slice(i, i + searchStr.length) === searchStr) {\n inAvatars = true\n // 跳过 \"avatars\": 和可能的空白\n i += searchStr.length - 1\n continue\n }\n } else {\n // 开始收集 avatars 对象内容\n avatarsContent += char\n\n if (escape) {\n escape = false\n continue\n }\n\n if (char === '\\\\' && inString) {\n escape = true\n continue\n }\n\n if (char === '\"') {\n inString = !inString\n continue\n }\n\n if (!inString) {\n if (char === '{') braceDepth++\n if (char === '}') {\n braceDepth--\n if (braceDepth === 0) {\n // avatars 对象结束\n avatarStream.destroy()\n return\n }\n }\n }\n }\n }\n })\n\n avatarStream.on('close', () => {\n if (avatarsContent) {\n try {\n // WeFlow 的 avatars 值直接是 base64 Data URL 字符串\n const avatarsObj = JSON.parse(avatarsContent) as Record<string, string>\n for (const [wxid, avatarDataUrl] of Object.entries(avatarsObj)) {\n if (avatarDataUrl && typeof avatarDataUrl === 'string') {\n avatarsMap.set(wxid, avatarDataUrl)\n }\n }\n } catch {\n // 解析失败\n }\n }\n resolve()\n })\n\n avatarStream.on('error', () => resolve())\n })\n } catch {\n // 流式解析失败,继续不带头像\n }\n }\n\n // 提取群头像(优先从 session.avatar,其次从 avatars 中获取群ID对应的头像)\n const groupAvatar = sessionData?.avatar || (groupId ? avatarsMap.get(groupId) : undefined)\n\n // 快速扫描获取 ownerId(通过 isSend === 1 推断)\n let ownerId: string | undefined\n try {\n await new Promise<void>((resolve) => {\n const scanStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n const scanPipeline = chain([scanStream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n\n scanPipeline.on('data', ({ value }: { value: WeFlowMessage }) => {\n if (value.isSend === 1 && value.senderUsername && !value.senderUsername.endsWith('@chatroom')) {\n ownerId = value.senderUsername\n scanStream.destroy() // 找到后立即停止扫描\n }\n })\n\n scanStream.on('close', () => resolve())\n scanPipeline.on('end', () => resolve())\n scanPipeline.on('error', () => resolve())\n })\n } catch {\n // 扫描失败,ownerId 保持 undefined\n }\n\n // 发送 meta(包含推断的 ownerId)\n const meta: ParsedMeta = {\n name: chatName,\n platform: KNOWN_PLATFORMS.WECHAT,\n type: chatType,\n groupId,\n groupAvatar,\n ownerId,\n }\n yield { type: 'meta', data: meta }\n\n // 收集成员和消息\n const memberMap = new Map<string, MemberInfo>()\n const messageBatch: ParsedMessage[] = []\n\n // 流式解析\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n readStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n const pipeline = chain([readStream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n\n const processMessage = (msg: WeFlowMessage): ParsedMessage | null => {\n // 验证必要字段\n if (!msg.senderUsername || msg.createTime === undefined) {\n return null\n }\n\n const platformId = msg.senderUsername\n\n // 跳过群\"成员\"(群ID以 @chatroom 结尾的消息)\n // 这些通常是系统消息,发送者是群本身,不是真正的成员\n if (platformId.endsWith('@chatroom')) {\n return null\n }\n\n const accountName = msg.senderDisplayName || platformId\n\n // 获取头像(通过 senderAvatarKey 从 avatarsMap 查找)\n const avatarKey = msg.senderAvatarKey || msg.senderUsername\n const avatar = avatarsMap.get(avatarKey)\n\n // 更新成员信息\n if (!memberMap.has(platformId)) {\n memberMap.set(platformId, {\n platformId,\n accountName,\n avatar,\n })\n } else {\n // 更新为最新的显示名\n const existing = memberMap.get(platformId)!\n existing.accountName = accountName\n // 头像使用最新的(覆盖更新)\n if (avatar) {\n existing.avatar = avatar\n }\n }\n\n // 转换消息类型\n const type = convertMessageType(msg.type)\n\n // 确保 content 是字符串类型(防止某些消息类型的 content 是对象)\n // 同时去除开头和结尾的空白字符\n let content: string | null = null\n if (msg.content != null) {\n const rawContent = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)\n content = rawContent.trim() || null\n }\n\n return {\n platformMessageId: String(msg.localId), // 消息的平台原始 ID(用于回复关联查询)\n senderPlatformId: platformId,\n senderAccountName: accountName,\n // WeFlow 格式没有单独的群昵称字段\n senderGroupNickname: undefined,\n timestamp: msg.createTime,\n type,\n content,\n // 注意:WeFlow 导出格式不包含被引用消息的 ID,所以 replyToMessageId 为空\n }\n }\n\n // 用于收集批次的临时数组\n const batchCollector: ParsedMessage[] = []\n\n pipeline.on('data', ({ value }: { value: WeFlowMessage }) => {\n const parsed = processMessage(value)\n if (parsed) {\n batchCollector.push(parsed)\n messagesProcessed++\n\n // 达到批次大小\n if (batchCollector.length >= batchSize) {\n messageBatch.push(...batchCollector)\n batchCollector.length = 0\n\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n onProgress?.(progress)\n }\n }\n })\n\n pipeline.on('end', () => {\n // 收集剩余消息\n if (batchCollector.length > 0) {\n messageBatch.push(...batchCollector)\n }\n resolve()\n })\n\n pipeline.on('error', reject)\n })\n\n // 发送成员\n const members: ParsedMember[] = Array.from(memberMap.values()).map((m) => ({\n platformId: m.platformId,\n accountName: m.accountName,\n avatar: m.avatar,\n }))\n yield { type: 'members', data: members }\n\n // 分批发送消息\n for (let i = 0; i < messageBatch.length; i += batchSize) {\n const batch = messageBatch.slice(i, i + batchSize)\n yield { type: 'messages', data: batch }\n }\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n // 记录解析摘要\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberMap.size} 个成员`)\n\n yield {\n type: 'done',\n data: { messageCount: messagesProcessed, memberCount: memberMap.size },\n }\n}\n\n// ==================== 导出解析器 ====================\n\nimport { withNativeWeflow } from '../native/weflow-native'\n\n// 导出解析函数供其他格式复用(如 echotrace)\n// parseWeFlowAccelerated:优先走 Rust 内核,native 不可用/失败时自动回退本文件的 TS 实现\nexport { parseWeFlow }\nexport const parseWeFlowAccelerated = withNativeWeflow(parseWeFlow)\n\nexport const parser_: Parser = {\n feature,\n parse: parseWeFlowAccelerated,\n}\n\n// ==================== 预处理器(预留) ====================\n\nimport { weflowPreprocessor } from './weflow-preprocessor'\nexport const preprocessor = weflowPreprocessor\n\n// ==================== 导出格式模块 ====================\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n preprocessor: weflowPreprocessor,\n}\n\nexport default module_\n","/**\n * WeFlow adapter for the unified native-first wrapper.\n *\n * Payload shapes are identical to the pure-TS parser in formats/weflow.ts\n * (verified by parity tests). Shared by the echotrace format module\n * (same data structure, same Rust kernel).\n */\n\nimport { KNOWN_PLATFORMS, ChatType, type MessageType } from '@openchatlab/shared-types'\nimport type { NativeMember, NativeMessage } from '@openchatlab/parser-native'\nimport type { ParsedMember, ParsedMessage, ParsedMeta } from '../types'\nimport { createNativeFirstParser, type NativeFormatAdapter, type ParseGenerator } from './create-native-parser'\n\n/** Shape of metaJson() from the Rust weflow kernel. */\ninterface WeflowMetaJson {\n name: string\n chatType: string\n groupId: string | null\n groupAvatar: string | null\n ownerId: string | null\n}\n\nconst weflowAdapter: NativeFormatAdapter = {\n kernelId: 'weflow',\n label: 'WeFlow export',\n\n mapMeta(metaJson: unknown): ParsedMeta {\n const meta = metaJson as WeflowMetaJson\n return {\n name: meta.name,\n platform: KNOWN_PLATFORMS.WECHAT,\n type: meta.chatType === 'private' ? ChatType.PRIVATE : ChatType.GROUP,\n groupId: meta.groupId ?? undefined,\n groupAvatar: meta.groupAvatar ?? undefined,\n ownerId: meta.ownerId ?? undefined,\n }\n },\n\n mapMembers(members: NativeMember[]): ParsedMember[] {\n return members.map((member) => ({\n platformId: member.platformId,\n accountName: member.accountName,\n avatar: member.avatar,\n }))\n },\n\n mapMessage(message: NativeMessage): ParsedMessage {\n return {\n // The kernel always emits an id string (String(localId), JS semantics).\n platformMessageId: message.platformMessageId,\n senderPlatformId: message.senderPlatformId,\n senderAccountName: message.senderAccountName,\n // WeFlow has no separate group nickname field (same as the TS parser).\n senderGroupNickname: undefined,\n // The TS parser passes createTime through as-is, including null.\n timestamp: (message.timestamp ?? null) as unknown as number,\n type: message.messageType as MessageType,\n content: message.content ?? null,\n }\n },\n}\n\n/**\n * Wrap a TS parse generator with native acceleration. Used by the WeFlow and\n * echotrace format modules (they share the same data structure).\n */\nexport function withNativeWeflow(fallback: ParseGenerator): ParseGenerator {\n return createNativeFirstParser(weflowAdapter, fallback)\n}\n","/**\n * WeFlow 格式预处理器\n * 用于大文件预处理,移除冗余字段\n *\n * 当前为预留实现,WeFlow 格式的字段结构较为简洁,\n * 暂不需要复杂的预处理逻辑。\n *\n * 如果未来发现性能问题,可在此添加:\n * - 移除 formattedTime 字段(可由 createTime 计算)\n * - 移除 source 字段(通常为空)\n * - 移除 localType 字段(不可信)\n */\n\nimport type { Preprocessor, ParseProgress } from '../types'\n\n/**\n * WeFlow 预处理器\n * 当前为预留实现,返回不需要预处理\n */\nexport const weflowPreprocessor: Preprocessor = {\n /**\n * 判断是否需要预处理\n * 当前策略:暂不需要预处理\n * 如果未来发现大文件性能问题,可调整阈值\n */\n needsPreprocess(_filePath: string, _fileSize: number): boolean {\n // 预留:如果文件超过 100MB 可能需要预处理\n // const THRESHOLD = 100 * 1024 * 1024 // 100MB\n // return fileSize > THRESHOLD\n return false\n },\n\n /**\n * 执行预处理\n * 当前为预留实现,直接返回原文件路径\n */\n async preprocess(filePath: string, _onProgress?: (progress: ParseProgress) => void): Promise<string> {\n // 预留:未来可在此实现精简逻辑\n // 1. 使用流式读取原文件\n // 2. 移除冗余字段 (formattedTime, source, localType)\n // 3. 写入临时文件\n // 4. 返回临时文件路径\n return filePath\n },\n\n /**\n * 清理临时文件\n */\n cleanup(_tempPath: string): void {\n // 预留:当 preprocess 返回临时文件时,在此清理\n // if (tempPath !== originalFilePath) {\n // fs.unlinkSync(tempPath)\n // }\n },\n}\n\nexport default weflowPreprocessor\n","/**\n * ycccccccy/echotrace 导出格式解析器\n * 适配项目: https://github.com/ycccccccy/echotrace\n *\n * 特征:\n * - 顶层包含 session 和 messages 字段(无 weflow 对象)\n * - session.wxid: ID(群聊以 @chatroom 结尾)\n * - session.type: \"群聊\" 或 \"私聊\"\n * - messages[].senderUsername: 发送者ID\n * - messages[].senderDisplayName: 发送者显示名\n *\n * 注意:此格式与 WeFlow 格式共享解析逻辑,区别在于签名检测\n */\n\nimport { KNOWN_PLATFORMS } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type { FormatFeature, FormatModule, Parser } from '../types'\nimport { parseWeFlowAccelerated } from './weflow'\nimport { weflowPreprocessor } from './weflow-preprocessor'\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.ECHOTRACE,\n name: 'ycccccccy/echotrace 导出',\n platform: KNOWN_PLATFORMS.WECHAT,\n priority: 16, // 比 WeFlow (15) 略低,优先检测 WeFlow\n extensions: ['.json'],\n signatures: {\n // echotrace 格式没有 weflow 对象,但有 session 和 senderUsername/senderDisplayName\n head: [/\"session\"\\s*:/, /\"senderUsername\"\\s*:/, /\"senderDisplayName\"\\s*:/],\n requiredFields: ['session', 'messages'],\n },\n}\n\n// ==================== 导出解析器 ====================\n\n// 复用 WeFlow 的解析逻辑(两种格式的数据结构相同)\nexport const parser_: Parser = {\n feature,\n parse: parseWeFlowAccelerated,\n}\n\n// ==================== 预处理器(复用 WeFlow) ====================\n\nexport const preprocessor = weflowPreprocessor\n\n// ==================== 导出格式模块 ====================\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n preprocessor: weflowPreprocessor,\n}\n\nexport default module_\n","/**\n * Tyrrrz/DiscordChatExporter 格式解析器\n * 支持 https://github.com/Tyrrrz/DiscordChatExporter 导出的 Discord 聊天记录\n *\n * 特征:\n * - 文件头包含 \"guild\" 和 \"channel\" 字段\n * - 消息结构包含 author.roles 数组\n * - 支持附件、嵌入、贴纸等 Discord 特有内容\n */\n\nimport * as fs from 'fs'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\nimport streamChain from 'stream-chain'\nimport { KNOWN_PLATFORMS, ChatType, MessageType, type MemberRole } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { getFileSize, createProgress, readFileHeadBytes } from '../utils'\n\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\nconst { chain } = streamChain\n\n// ==================== Discord 数据结构定义 ====================\n\ninterface DiscordGuild {\n id: string\n name: string\n iconUrl?: string\n}\n\ninterface DiscordChannel {\n id: string\n type: string\n categoryId?: string\n category?: string\n name: string\n topic?: string\n}\n\ninterface DiscordRole {\n id: string\n name: string\n color?: string\n position?: number\n}\n\ninterface DiscordAuthor {\n id: string\n name: string\n discriminator: string\n nickname?: string\n color?: string\n isBot: boolean\n roles: DiscordRole[]\n avatarUrl?: string\n}\n\ninterface DiscordAttachment {\n id: string\n url: string\n fileName: string\n fileSizeBytes?: number\n}\n\ninterface DiscordEmbed {\n title?: string\n url?: string\n description?: string\n thumbnail?: { url: string }\n}\n\ninterface DiscordSticker {\n id: string\n name: string\n format: string\n sourceUrl?: string\n}\n\ninterface DiscordMessage {\n id: string\n type: string\n timestamp: string\n timestampEdited?: string | null\n callEndedTimestamp?: string | null\n isPinned: boolean\n content: string\n author: DiscordAuthor\n attachments: DiscordAttachment[]\n embeds: DiscordEmbed[]\n stickers: DiscordSticker[]\n reference?: {\n messageId?: string\n channelId?: string\n guildId?: string | null\n }\n}\n\n// ==================== 消息类型映射 ====================\n\n/**\n * Discord 消息类型到 ChatLab 消息类型的映射\n */\nfunction mapDiscordMessageType(type: string): MessageType {\n switch (type) {\n // 内容消息\n case 'Default':\n case 'ThreadStarterMessage':\n return MessageType.TEXT\n case 'Reply':\n return MessageType.REPLY\n // 通话相关\n case 'Call':\n return MessageType.CALL\n // 系统消息\n case 'RecipientAdd':\n case 'RecipientRemove':\n case 'ChannelNameChange':\n case 'ChannelIconChange':\n case 'ChannelPinnedMessage':\n case 'UserJoin':\n case 'GuildBoost':\n case 'GuildBoostTier1':\n case 'GuildBoostTier2':\n case 'GuildBoostTier3':\n case 'ChannelFollowAdd':\n case 'ThreadCreated':\n case 'ChatInputCommand':\n case 'ContextMenuCommand':\n case 'AutoModerationAction':\n case 'StageStart':\n case 'StageEnd':\n case 'StageSpeaker':\n case 'StageTopic':\n case 'GuildDiscoveryDisqualified':\n case 'GuildDiscoveryRequalified':\n case 'GuildDiscoveryGracePeriodInitialWarning':\n case 'GuildDiscoveryGracePeriodFinalWarning':\n case 'GuildInviteReminder':\n case 'RoleSubscriptionPurchase':\n case 'InteractionPremiumUpsell':\n case 'GuildApplicationPremiumSubscription':\n return MessageType.SYSTEM\n default:\n return MessageType.OTHER\n }\n}\n\n/**\n * 根据附件判断消息类型\n */\nfunction getMessageTypeFromAttachment(attachment: DiscordAttachment): MessageType {\n const fileName = attachment.fileName.toLowerCase()\n\n // 图片\n if (/\\.(png|jpg|jpeg|gif|webp|bmp|svg)$/i.test(fileName)) {\n return MessageType.IMAGE\n }\n // 视频\n if (/\\.(mp4|webm|mov|avi|mkv)$/i.test(fileName)) {\n return MessageType.VIDEO\n }\n // 音频\n if (/\\.(mp3|wav|ogg|flac|m4a)$/i.test(fileName)) {\n return MessageType.VOICE\n }\n // 其他文件\n return MessageType.FILE\n}\n\n// ==================== 附件文本生成 ====================\n\n/**\n * 附件标记生成器\n * 使用英文标记,前端可根据需要翻译\n * 格式: [Type: filename] 或 [Type: description]\n */\nconst AttachmentMarkers = {\n image: (fileName: string) => `[Image: ${fileName}]`,\n video: (fileName: string) => `[Video: ${fileName}]`,\n audio: (fileName: string) => `[Audio: ${fileName}]`,\n file: (fileName: string) => `[File: ${fileName}]`,\n sticker: (name: string) => `[Sticker: ${name}]`,\n embed: (title: string) => `[Link: ${title}]`,\n}\n\n/**\n * 生成附件文本标记\n */\nfunction formatAttachment(attachment: DiscordAttachment): string {\n const fileName = attachment.fileName.toLowerCase()\n\n if (/\\.(png|jpg|jpeg|gif|webp|bmp|svg)$/i.test(fileName)) {\n return AttachmentMarkers.image(attachment.fileName)\n }\n if (/\\.(mp4|webm|mov|avi|mkv)$/i.test(fileName)) {\n return AttachmentMarkers.video(attachment.fileName)\n }\n if (/\\.(mp3|wav|ogg|flac|m4a)$/i.test(fileName)) {\n return AttachmentMarkers.audio(attachment.fileName)\n }\n return AttachmentMarkers.file(attachment.fileName)\n}\n\n/**\n * 生成嵌入文本标记\n */\nfunction formatEmbed(embed: DiscordEmbed): string {\n const title = embed.title || embed.url || embed.description?.slice(0, 30) || 'link'\n return AttachmentMarkers.embed(title)\n}\n\n/**\n * 生成贴纸文本标记\n */\nfunction formatSticker(sticker: DiscordSticker): string {\n return AttachmentMarkers.sticker(sticker.name)\n}\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.DISCORD_TYRRRZ,\n name: 'Tyrrrz/DiscordChatExporter',\n platform: KNOWN_PLATFORMS.DISCORD,\n priority: 20, // Discord 格式优先级\n extensions: ['.json'],\n signatures: {\n head: [/\"guild\"\\s*:\\s*\\{/, /\"channel\"\\s*:\\s*\\{/],\n requiredFields: ['guild', 'channel', 'messages'],\n },\n}\n\n// ==================== 辅助函数 ====================\n\n/**\n * 解析 ISO 8601 时间戳为秒级 Unix 时间戳\n */\nfunction parseTimestamp(isoString: string): number {\n const date = new Date(isoString)\n return Math.floor(date.getTime() / 1000)\n}\n\n/**\n * 转换 Discord 角色为 ChatLab MemberRole\n */\nfunction convertRoles(discordRoles: DiscordRole[]): MemberRole[] {\n return discordRoles.map((role) => ({\n id: role.id,\n name: role.name,\n }))\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseDiscordExporter(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n const totalBytes = getFileSize(filePath)\n let bytesRead = 0\n let messagesProcessed = 0\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n onLog?.('info', `开始解析 Discord 导出文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 读取文件头获取 guild 和 channel 信息\n const headContent = readFileHeadBytes(filePath, 10000)\n\n // 解析 guild 和 channel\n let guild: DiscordGuild | null = null\n let channel: DiscordChannel | null = null\n\n try {\n const guildMatch = headContent.match(/\"guild\"\\s*:\\s*(\\{[^}]+\\})/)\n if (guildMatch) {\n guild = JSON.parse(guildMatch[1])\n }\n\n const channelMatch = headContent.match(/\"channel\"\\s*:\\s*(\\{[^}]+\\})/)\n if (channelMatch) {\n channel = JSON.parse(channelMatch[1])\n }\n } catch (e) {\n onLog?.('error', `解析头部信息失败: ${e}`)\n }\n\n // 构建 meta\n const chatName = guild && channel ? `${guild.name} - ${channel.name}` : channel?.name || '未知频道'\n\n const meta: ParsedMeta = {\n name: chatName,\n platform: KNOWN_PLATFORMS.DISCORD,\n type: ChatType.GROUP, // Discord 频道都是群聊\n groupId: channel?.id,\n groupAvatar: guild?.iconUrl,\n }\n\n yield { type: 'meta', data: meta }\n onLog?.('info', `群聊名称: ${chatName}`)\n\n // 收集成员和消息\n const memberMap = new Map<string, ParsedMember>()\n const messageBatch: ParsedMessage[] = []\n\n // 流式解析消息\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n readStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n const pipeline = chain([readStream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n\n // 用于收集批次的临时数组\n const batchCollector: ParsedMessage[] = []\n\n pipeline.on('data', ({ value }: { value: DiscordMessage }) => {\n const msg = value\n const author = msg.author\n\n // 收集成员信息(包括角色)\n if (!memberMap.has(author.id)) {\n memberMap.set(author.id, {\n platformId: author.id,\n accountName: author.name,\n groupNickname: author.nickname || undefined,\n avatar: author.avatarUrl,\n roles: convertRoles(author.roles),\n })\n } else {\n // 更新角色(用户可能在不同消息中有不同角色)\n const existingMember = memberMap.get(author.id)!\n if (author.roles.length > (existingMember.roles?.length || 0)) {\n existingMember.roles = convertRoles(author.roles)\n }\n }\n\n // 确定消息类型\n let messageType = mapDiscordMessageType(msg.type)\n let content = msg.content || ''\n\n // 处理附件\n if (msg.attachments && msg.attachments.length > 0) {\n // 如果是纯附件消息,根据附件类型确定消息类型\n if (!content && msg.attachments.length === 1) {\n messageType = getMessageTypeFromAttachment(msg.attachments[0])\n }\n // 添加附件标记到内容\n const attachmentTexts = msg.attachments.map(formatAttachment)\n content = content ? `${content}\\n${attachmentTexts.join('\\n')}` : attachmentTexts.join('\\n')\n }\n\n // 处理嵌入\n if (msg.embeds && msg.embeds.length > 0) {\n const embedTexts = msg.embeds.map(formatEmbed)\n content = content ? `${content}\\n${embedTexts.join('\\n')}` : embedTexts.join('\\n')\n // 如果纯嵌入消息,标记为链接类型\n if (!msg.content && !msg.attachments?.length && msg.embeds.length > 0) {\n messageType = MessageType.LINK\n }\n }\n\n // 处理贴纸\n if (msg.stickers && msg.stickers.length > 0) {\n const stickerTexts = msg.stickers.map(formatSticker)\n content = content ? `${content}\\n${stickerTexts.join('\\n')}` : stickerTexts.join('\\n')\n // 纯贴纸消息\n if (!msg.content && !msg.attachments?.length && !msg.embeds?.length) {\n messageType = MessageType.EMOJI\n }\n }\n\n batchCollector.push({\n platformMessageId: msg.id, // 消息的平台原始 ID\n senderPlatformId: author.id,\n senderAccountName: author.name,\n senderGroupNickname: author.nickname || undefined,\n timestamp: parseTimestamp(msg.timestamp),\n type: messageType,\n content: content || null,\n replyToMessageId: msg.reference?.messageId || undefined,\n })\n\n messagesProcessed++\n\n // 达到批次大小\n if (batchCollector.length >= batchSize) {\n messageBatch.push(...batchCollector)\n batchCollector.length = 0\n\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n onProgress?.(progress)\n }\n })\n\n pipeline.on('end', () => {\n // 收集剩余消息\n if (batchCollector.length > 0) {\n messageBatch.push(...batchCollector)\n }\n resolve()\n })\n\n pipeline.on('error', reject)\n })\n\n // 发送成员\n const members = Array.from(memberMap.values())\n yield { type: 'members', data: members }\n onLog?.('info', `解析到 ${members.length} 个成员`)\n\n // 分批发送消息\n for (let i = 0; i < messageBatch.length; i += batchSize) {\n const batch = messageBatch.slice(i, i + batchSize)\n yield { type: 'messages', data: batch }\n }\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${members.length} 个成员`)\n\n yield {\n type: 'done',\n data: {\n messageCount: messagesProcessed,\n memberCount: members.length,\n },\n }\n}\n\n// ==================== 导出解析器 ====================\n\nexport const parser_: Parser = {\n feature,\n parse: parseDiscordExporter,\n}\n\n// ==================== 导出格式模块 ====================\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","/**\n * Telegram 官方全量导出 JSON 格式解析器\n * 适配 Telegram Desktop (macOS) 的「导出聊天记录」→ 全部导出\n *\n * 格式特征:\n * - 单个 JSON 文件包含用户所有聊天(contacts, chats 等)\n * - 聊天数据在 chats.list[] 下,每个聊天有独立的 messages 数组\n * - 消息的 text 字段可以是纯字符串或 mixed 数组(含富文本实体)\n * - 支持 personal_chat / private_group / private_supergroup / saved_messages 等类型\n *\n * 导入流程(多聊天选择器):\n * 1. 用户选择 telegram.json → 格式识别\n * 2. scanChats() 快速扫描提取聊天列表\n * 3. 用户选择要导入的聊天\n * 4. parser 使用 formatOptions.chatIndex 定位并流式解析选定聊天\n */\n\nimport * as fs from 'fs'\nimport streamChain from 'stream-chain'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\n\nimport { KNOWN_PLATFORMS, ChatType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { getFileSize, createProgress } from '../utils'\nimport { mapChatType, extractPlatformId, detectMessageType, buildContent } from './utils/telegram-utils'\nimport type { TelegramChat } from './utils/telegram-utils'\n\nconst { chain } = streamChain\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\n\n// ==================== 类型定义 ====================\n\n/** Telegram 聊天信息(扫描结果) */\nexport interface TelegramChatInfo {\n /** 在 chats.list[] 中的索引 */\n index: number\n /** 聊天名称 */\n name: string\n /** Telegram 聊天类型 */\n type: string\n /** Telegram 聊天 ID */\n id: number\n /** 消息数量 */\n messageCount: number\n}\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.TELEGRAM_NATIVE,\n name: 'Telegram 官方导出 (JSON)',\n platform: KNOWN_PLATFORMS.TELEGRAM,\n priority: 22,\n extensions: ['.json'],\n signatures: {\n // Telegram 导出 JSON 的特征(语言无关:品牌名在所有语言导出中都存在)\n head: [/Telegram/i],\n // 注意:personal_information 在某些导出配置中是可选的,不能作为必需字段\n requiredFields: ['chats'],\n },\n multiChat: true,\n}\n\n// ==================== 扫描函数 ====================\n\n/**\n * 快速扫描 Telegram 导出 JSON,提取聊天列表\n * 使用 stream-json 流式处理,避免全量加载大文件到内存\n *\n * @param filePath 文件路径\n * @returns 聊天列表信息\n */\nexport async function scanChats(filePath: string): Promise<TelegramChatInfo[]> {\n const chats: TelegramChatInfo[] = []\n\n return new Promise<TelegramChatInfo[]>((resolve, reject) => {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n // 使用 stream-json 解析 chats.list 数组中的每个聊天对象\n // ignore 过滤掉 messages 的实际内容以加速扫描\n const pipeline = chain([readStream, parser(), pick({ filter: /^chats\\.list\\.\\d+$/ }), streamValues()])\n\n pipeline.on('data', ({ value }: { value: TelegramChat }) => {\n const chat = value\n chats.push({\n index: chats.length,\n name: chat.name || `Chat ${chat.id}`,\n type: chat.type,\n id: chat.id,\n messageCount: Array.isArray(chat.messages) ? chat.messages.length : 0,\n })\n })\n\n pipeline.on('end', () => {\n resolve(chats)\n })\n\n pipeline.on('error', (err: Error) => {\n reject(new Error(`扫描 Telegram 文件失败: ${err.message}`))\n })\n })\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseTelegram(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, formatOptions, onProgress, onLog } = options\n\n // 获取目标聊天索引\n const chatIndex = (formatOptions?.chatIndex as number) ?? 0\n\n const totalBytes = getFileSize(filePath)\n let bytesRead = 0\n let messagesProcessed = 0\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n onLog?.('info', `开始解析 Telegram JSON 文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n onLog?.('info', `目标聊天索引: ${chatIndex}`)\n\n // 使用 stream-json 流式解析目标聊天\n // 定位到 chats.list[chatIndex] 对象\n const chatPathFilter = `chats.list.${chatIndex}`\n\n const chatData = await new Promise<TelegramChat | null>((resolve, reject) => {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n readStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n const pipeline = chain([\n readStream,\n parser(),\n pick({ filter: new RegExp(`^${chatPathFilter.replace('.', '\\\\.')}$`) }),\n streamValues(),\n ])\n\n let found = false\n\n pipeline.on('data', ({ value }: { value: TelegramChat }) => {\n found = true\n resolve(value)\n })\n\n pipeline.on('end', () => {\n if (!found) resolve(null)\n })\n\n pipeline.on('error', reject)\n })\n\n if (!chatData) {\n onLog?.('error', `未找到索引 ${chatIndex} 对应的聊天`)\n yield { type: 'error', data: new Error(`未找到索引 ${chatIndex} 对应的聊天`) }\n return\n }\n\n onLog?.('info', `找到聊天: \"${chatData.name}\", 类型: ${chatData.type}, 消息数: ${chatData.messages?.length || 0}`)\n\n // 确定聊天类型\n const chatType = mapChatType(chatData.type)\n\n // 发送 meta\n const meta: ParsedMeta = {\n name: chatData.name || `Telegram Chat ${chatData.id}`,\n platform: KNOWN_PLATFORMS.TELEGRAM,\n type: chatType,\n groupId: chatType === ChatType.GROUP ? String(chatData.id) : undefined,\n }\n yield { type: 'meta', data: meta }\n\n // 收集成员和消息\n const memberMap = new Map<string, ParsedMember>()\n const messageBatch: ParsedMessage[] = []\n const messages = chatData.messages || []\n\n for (const msg of messages) {\n // 提取发送者信息\n let senderPlatformId: string\n let senderName: string\n\n if (msg.type === 'service') {\n // Service 消息使用 actor 信息\n senderPlatformId = extractPlatformId(msg.actor_id)\n senderName = msg.actor || '系统'\n } else {\n senderPlatformId = extractPlatformId(msg.from_id)\n senderName = msg.from || senderPlatformId\n }\n\n // 更新成员\n if (!memberMap.has(senderPlatformId) && senderPlatformId !== 'unknown') {\n memberMap.set(senderPlatformId, {\n platformId: senderPlatformId,\n accountName: senderName,\n })\n }\n\n // 解析时间戳\n const timestamp = parseInt(msg.date_unixtime, 10)\n if (isNaN(timestamp)) continue\n\n // 构建消息\n const parsedMsg: ParsedMessage = {\n platformMessageId: String(msg.id),\n senderPlatformId,\n senderAccountName: senderName,\n timestamp,\n type: detectMessageType(msg),\n content: buildContent(msg),\n replyToMessageId: msg.reply_to_message_id ? String(msg.reply_to_message_id) : undefined,\n }\n\n messageBatch.push(parsedMsg)\n messagesProcessed++\n\n // 分批 yield 消息\n if (messageBatch.length >= batchSize) {\n yield { type: 'messages', data: [...messageBatch] }\n messageBatch.length = 0\n\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n yield { type: 'progress', data: progress }\n onProgress?.(progress)\n }\n }\n\n // 发送成员\n yield { type: 'members', data: Array.from(memberMap.values()) }\n\n // 发送剩余消息\n if (messageBatch.length > 0) {\n yield { type: 'messages', data: messageBatch }\n }\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberMap.size} 个成员, 类型: ${chatType}`)\n\n yield {\n type: 'done',\n data: { messageCount: messagesProcessed, memberCount: memberMap.size },\n }\n}\n\n// ==================== 导出 ====================\n\nexport const parser_: Parser = {\n feature,\n parse: parseTelegram,\n}\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n scanChats,\n}\n\nexport default module_\n","/**\n * Telegram 格式解析共享工具\n * 供 telegram-native(全量导出)和 telegram-native-single(单聊天导出)共用\n */\n\nimport { ChatType, MessageType } from '@openchatlab/shared-types'\n\n// ==================== 共享类型 ====================\n\n/** Telegram 消息结构 */\nexport interface TelegramMessage {\n id: number\n type: 'message' | 'service'\n date: string\n date_unixtime: string\n from?: string | null\n from_id?: string\n actor?: string | null\n actor_id?: string\n action?: string\n text: string | Array<string | { type: string; text: string }>\n text_entities?: Array<{ type: string; text: string }>\n reply_to_message_id?: number\n forwarded_from?: string | null\n forwarded_from_id?: string\n photo?: string\n file?: string\n file_name?: string\n media_type?: string\n sticker_emoji?: string\n mime_type?: string\n members?: string[]\n}\n\n/** Telegram 聊天结构 */\nexport interface TelegramChat {\n name: string\n type: string\n id: number\n messages: TelegramMessage[]\n}\n\n// ==================== 共享辅助函数 ====================\n\n/**\n * 将 Telegram 聊天类型映射到 ChatLab 聊天类型\n */\nexport function mapChatType(telegramType: string): ChatType {\n switch (telegramType) {\n case 'personal_chat':\n case 'bot_chat':\n case 'saved_messages':\n return ChatType.PRIVATE\n case 'private_group':\n case 'public_group':\n case 'private_supergroup':\n case 'public_supergroup':\n case 'private_channel':\n case 'public_channel':\n return ChatType.GROUP\n default:\n return ChatType.GROUP\n }\n}\n\n/**\n * 提取 Telegram text 字段为纯文本\n * text 可以是字符串或 mixed 数组\n */\nexport function extractText(text: string | Array<string | { type: string; text: string }>): string {\n if (typeof text === 'string') return text\n if (!Array.isArray(text)) return ''\n\n return text\n .map((part) => {\n if (typeof part === 'string') return part\n if (part && typeof part === 'object' && 'text' in part) return part.text\n return ''\n })\n .join('')\n}\n\n/**\n * 从 from_id 提取平台 ID\n * 格式:user123456 → 123456\n */\nexport function extractPlatformId(fromId: string | undefined): string {\n if (!fromId) return 'unknown'\n // 移除 \"user\" / \"channel\" 等前缀,保留数字部分\n return fromId.replace(/^(user|channel)/, '')\n}\n\n/**\n * 检测消息类型\n */\nexport function detectMessageType(msg: TelegramMessage): MessageType {\n // Service 消息 → 系统消息\n if (msg.type === 'service') return MessageType.SYSTEM\n\n // 贴纸\n if (msg.media_type === 'sticker') return MessageType.IMAGE\n // 图片\n if (msg.photo) return MessageType.IMAGE\n // 动画 (GIF)\n if (msg.media_type === 'animation') return MessageType.IMAGE\n // 视频\n if (msg.media_type === 'video_file') return MessageType.VIDEO\n // 语音\n if (msg.media_type === 'voice_message') return MessageType.VOICE\n // 视频留言\n if (msg.media_type === 'video_message') return MessageType.VIDEO\n // 文件\n if (msg.file && !msg.media_type) return MessageType.FILE\n\n // 默认文本\n return MessageType.TEXT\n}\n\n/**\n * 构建消息内容\n */\nexport function buildContent(msg: TelegramMessage): string | null {\n const text = extractText(msg.text)\n\n // Service 消息\n if (msg.type === 'service') {\n const action = msg.action || ''\n const members = msg.members?.join(', ') || ''\n if (members) return `[${action}] ${members}`\n // action 为空时回退到文本,避免常量表达式触发 lint 规则。\n if (action) return `[${action}]`\n return text || null\n }\n\n // 贴纸:使用 emoji 表示\n if (msg.media_type === 'sticker' && msg.sticker_emoji) {\n return text ? `[sticker ${msg.sticker_emoji}] ${text}` : `[sticker ${msg.sticker_emoji}]`\n }\n\n // 媒体消息带文字说明\n if (msg.photo || msg.file || msg.media_type) {\n const mediaLabel = msg.media_type || (msg.photo ? 'photo' : 'file')\n if (text) return `[${mediaLabel}] ${text}`\n return `[${mediaLabel}]`\n }\n\n return text || null\n}\n","import * as fs from 'fs'\nimport Assembler from 'stream-json/Assembler.js'\nimport streamJson from 'stream-json'\nimport { ChatType, KNOWN_PLATFORMS } from '@openchatlab/shared-types'\n\nimport { TELEGRAM_SINGLE_CHAT_TYPE_PATTERN, TELEGRAM_SINGLE_HEAD_SIGNATURES } from '../browser/telegram'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n ParsedMember,\n ParsedMessage,\n ParsedMeta,\n ParseEvent,\n ParseOptions,\n Parser,\n} from '../types'\nimport { createProgress, getFileSize } from '../utils'\nimport { buildContent, detectMessageType, extractPlatformId, mapChatType } from './utils/telegram-utils'\nimport type { TelegramMessage } from './utils/telegram-utils'\n\nconst { parser } = streamJson\n\ninterface JsonToken {\n name: string\n value?: string\n}\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.TELEGRAM_NATIVE_SINGLE,\n name: 'Telegram 单聊天导出 (JSON)',\n platform: KNOWN_PLATFORMS.TELEGRAM,\n priority: 23,\n extensions: ['.json'],\n signatures: {\n head: TELEGRAM_SINGLE_HEAD_SIGNATURES,\n requiredFields: ['messages'],\n fieldPatterns: { telegramChatType: TELEGRAM_SINGLE_CHAT_TYPE_PATTERN },\n },\n}\n\nasync function* parseTelegramSingle(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n const totalBytes = getFileSize(filePath)\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n onLog?.('info', `Starting Telegram single-chat JSON parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`)\n\n let messagesProcessed = 0\n const memberMap = new Map<string, ParsedMember>()\n const messageBatch: ParsedMessage[] = []\n\n try {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n const tokenStream = readStream.pipe(parser())\n let depth = 0\n let rootKey: string | null = null\n let chatName: string | null = null\n let telegramType: string | null = null\n let chatId: number | null = null\n let messagesArrayDepth: number | null = null\n let messageAssembler: Assembler | null = null\n let meta: ParsedMeta | null = null\n\n for await (const rawToken of tokenStream) {\n const token = rawToken as JsonToken\n\n // Assembler 只组装 messages 数组中的单条消息,避免重新构造完整 JSON 根对象。\n if (messageAssembler) {\n messageAssembler.consume(token)\n if (messageAssembler.done) {\n const message = messageAssembler.current as TelegramMessage\n messageAssembler = null\n const parsedMessage = parseTelegramMessage(message, memberMap)\n if (parsedMessage) {\n messageBatch.push(parsedMessage)\n messagesProcessed++\n }\n\n if (messageBatch.length >= batchSize) {\n yield { type: 'messages', data: messageBatch.splice(0) }\n const progress = createProgress(\n 'parsing',\n readStream.bytesRead,\n totalBytes,\n messagesProcessed,\n `Processed ${messagesProcessed} messages`\n )\n yield { type: 'progress', data: progress }\n onProgress?.(progress)\n }\n }\n } else if (token.name === 'keyValue' && depth === 1) {\n rootKey = token.value ?? null\n } else if (token.name === 'stringValue' && depth === 1) {\n if (rootKey === 'name') chatName = token.value ?? ''\n if (rootKey === 'type') telegramType = token.value ?? ''\n } else if (token.name === 'numberValue' && depth === 1 && rootKey === 'id') {\n const parsedId = Number(token.value)\n if (Number.isFinite(parsedId)) chatId = parsedId\n }\n\n if (token.name === 'startArray' && depth === 1 && rootKey === 'messages') {\n // Telegram 官方导出的元信息位于 messages 之前,可先输出 meta,再持续输出消息批次。\n if (chatName === null || telegramType === null || chatId === null) {\n throw new Error('Invalid Telegram single-chat JSON export')\n }\n const chatType = mapChatType(telegramType)\n meta = {\n name: chatName || `Telegram Chat ${chatId}`,\n platform: KNOWN_PLATFORMS.TELEGRAM,\n type: chatType,\n groupId: chatType === ChatType.GROUP ? String(chatId) : undefined,\n }\n messagesArrayDepth = depth + 1\n yield { type: 'meta', data: meta }\n } else if (token.name === 'startObject' && messagesArrayDepth !== null && depth === messagesArrayDepth) {\n messageAssembler = new Assembler()\n messageAssembler.consume(token)\n }\n\n // 无论 token 是否已被 Assembler 消费,都必须同步外层深度,确保下一条消息仍从数组层级开始。\n if (token.name === 'startObject' || token.name === 'startArray') {\n depth++\n } else if (token.name === 'endObject' || token.name === 'endArray') {\n depth--\n }\n }\n\n if (!meta || messagesArrayDepth === null || messageAssembler) {\n throw new Error('Invalid Telegram single-chat JSON export')\n }\n } catch (error) {\n const parseError = error instanceof Error ? error : new Error(String(error))\n onLog?.('error', 'Telegram single-chat JSON parsing failed')\n yield { type: 'error', data: parseError }\n return\n }\n\n yield { type: 'members', data: Array.from(memberMap.values()) }\n if (messageBatch.length > 0) {\n yield { type: 'messages', data: messageBatch }\n }\n\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n onLog?.(\n 'info',\n `Telegram single-chat JSON parsing completed: ${messagesProcessed} messages, ${memberMap.size} members`\n )\n yield {\n type: 'done',\n data: { messageCount: messagesProcessed, memberCount: memberMap.size },\n }\n}\n\nfunction parseTelegramMessage(message: TelegramMessage, memberMap: Map<string, ParsedMember>): ParsedMessage | null {\n const isService = message.type === 'service'\n const senderPlatformId = extractPlatformId(isService ? message.actor_id : message.from_id)\n const senderName = isService ? message.actor || 'System' : message.from || senderPlatformId\n\n if (!memberMap.has(senderPlatformId) && senderPlatformId !== 'unknown') {\n memberMap.set(senderPlatformId, {\n platformId: senderPlatformId,\n accountName: senderName,\n })\n }\n\n const timestamp = Number.parseInt(message.date_unixtime, 10)\n if (Number.isNaN(timestamp)) return null\n\n return {\n platformMessageId: String(message.id),\n senderPlatformId,\n senderAccountName: senderName,\n timestamp,\n type: detectMessageType(message),\n content: buildContent(message),\n replyToMessageId: message.reply_to_message_id ? String(message.reply_to_message_id) : undefined,\n }\n}\n\nexport const parser_: Parser = { feature, parse: parseTelegramSingle }\n\nconst module_: FormatModule = { feature, parser: parser_ }\n\nexport default module_\n","import { ChatType, KNOWN_PLATFORMS } from '@openchatlab/shared-types'\nimport type { ParsedMember, ParsedMessage } from '@openchatlab/shared-types'\n\nimport type { ParsedMeta } from '../types'\nimport { buildContent, detectMessageType, extractPlatformId, mapChatType } from '../formats/utils/telegram-utils'\nimport type { TelegramChat, TelegramMessage } from '../formats/utils/telegram-utils'\n\nexport const TELEGRAM_SINGLE_CHAT_TYPE_PATTERN =\n /\"type\"\\s*:\\s*\"(personal_chat|bot_chat|private_group|private_supergroup|public_group|public_supergroup|public_channel|private_channel|saved_messages)\"/\nexport const TELEGRAM_SINGLE_HEAD_SIGNATURES = [/^\\s*\\{\\s*(?:\\r?\\n\\s*)?\"name\"\\s*:/]\nexport const TELEGRAM_MULTI_CHAT_HEAD_SIGNATURES = [/^\\s*\\{\\s*(?:\\r?\\n\\s*)?\"about\"\\s*:/, /Telegram/i]\nconst TELEGRAM_MESSAGES_PATTERN = /\"messages\"\\s*:/\n\nexport interface TelegramJsonParseOptions {\n checkCancelled?: () => void\n onProgress?: (progress: { progress: number; messagesProcessed: number }) => void\n yieldEvery?: number\n}\n\nexport type TelegramSingleJsonParseOptions = TelegramJsonParseOptions\n\nexport interface TelegramJsonParseResult {\n meta: ParsedMeta\n members: ParsedMember[]\n messages: ParsedMessage[]\n}\n\nexport type TelegramSingleJsonParseResult = TelegramJsonParseResult\n\nexport interface TelegramChatInfo {\n index: number\n name: string\n type: string\n id: number\n messageCount: number\n}\n\ninterface TelegramFullExport {\n about: string\n chats: { list: TelegramChat[] }\n}\n\nexport function detectTelegramSingleJson(head: string, fileName: string): boolean {\n const basename = getBasename(fileName)\n return (\n basename.toLowerCase().endsWith('.json') &&\n TELEGRAM_SINGLE_HEAD_SIGNATURES.some((signature) => signature.test(head)) &&\n TELEGRAM_SINGLE_CHAT_TYPE_PATTERN.test(head) &&\n TELEGRAM_MESSAGES_PATTERN.test(head)\n )\n}\n\nexport function detectTelegramMultiChatJson(head: string, fileName: string): boolean {\n const basename = getBasename(fileName)\n return (\n basename.toLowerCase().endsWith('.json') &&\n TELEGRAM_MULTI_CHAT_HEAD_SIGNATURES.every((signature) => signature.test(head))\n )\n}\n\nexport async function scanTelegramChatsJson(\n content: string,\n options: Pick<TelegramJsonParseOptions, 'checkCancelled' | 'yieldEvery'> = {}\n): Promise<TelegramChatInfo[]> {\n options.checkCancelled?.()\n const value = parseTelegramFullExport(content)\n const chats: TelegramChatInfo[] = []\n const yieldEvery = Math.max(1, options.yieldEvery ?? 5000)\n\n for (let index = 0; index < value.chats.list.length; index += 1) {\n options.checkCancelled?.()\n const chat = value.chats.list[index]\n chats.push({\n index,\n name: chat.name || `Chat ${chat.id}`,\n type: chat.type,\n id: chat.id,\n messageCount: chat.messages.length,\n })\n if (index > 0 && index % yieldEvery === 0) {\n await yieldToEventLoop()\n options.checkCancelled?.()\n }\n }\n\n options.checkCancelled?.()\n return chats\n}\n\nexport async function parseTelegramMultiChatJson(\n content: string,\n chatIndex: number,\n options: TelegramJsonParseOptions = {}\n): Promise<TelegramJsonParseResult> {\n options.checkCancelled?.()\n const value = parseTelegramFullExport(content)\n if (!Number.isInteger(chatIndex) || chatIndex < 0 || chatIndex >= value.chats.list.length) {\n throw new Error(`Invalid Telegram chat index: ${chatIndex}`)\n }\n return parseTelegramChat(value.chats.list[chatIndex], options)\n}\n\nexport async function parseTelegramSingleJson(\n content: string,\n options: TelegramSingleJsonParseOptions = {}\n): Promise<TelegramSingleJsonParseResult> {\n let value: unknown\n try {\n value = JSON.parse(content)\n } catch (error) {\n throw new Error('Invalid Telegram single-chat JSON export', { cause: error })\n }\n if (!isTelegramChat(value)) {\n throw new Error('Invalid Telegram single-chat JSON export')\n }\n\n return parseTelegramChat(value, options)\n}\n\nasync function parseTelegramChat(\n value: TelegramChat,\n options: TelegramJsonParseOptions\n): Promise<TelegramJsonParseResult> {\n const memberMap = new Map<string, ParsedMember>()\n const messages: ParsedMessage[] = []\n const chatType = mapChatType(value.type)\n const yieldEvery = Math.max(1, options.yieldEvery ?? 5000)\n\n for (let index = 0; index < value.messages.length; index += 1) {\n options.checkCancelled?.()\n const message = value.messages[index]\n const { senderPlatformId, senderName } = resolveSender(message)\n if (!memberMap.has(senderPlatformId) && senderPlatformId !== 'unknown') {\n memberMap.set(senderPlatformId, {\n platformId: senderPlatformId,\n accountName: senderName,\n })\n }\n\n const timestamp = Number.parseInt(message.date_unixtime, 10)\n if (!Number.isNaN(timestamp)) {\n messages.push({\n platformMessageId: String(message.id),\n senderPlatformId,\n senderAccountName: senderName,\n timestamp,\n type: detectMessageType(message),\n content: buildContent(message),\n replyToMessageId: message.reply_to_message_id ? String(message.reply_to_message_id) : undefined,\n })\n }\n\n if (index > 0 && index % yieldEvery === 0) {\n options.onProgress?.({ progress: index / value.messages.length, messagesProcessed: messages.length })\n await yieldToEventLoop()\n options.checkCancelled?.()\n }\n }\n\n options.checkCancelled?.()\n const result: TelegramSingleJsonParseResult = {\n meta: {\n name: value.name || `Telegram Chat ${value.id}`,\n platform: KNOWN_PLATFORMS.TELEGRAM,\n type: chatType,\n groupId: chatType === ChatType.GROUP ? String(value.id) : undefined,\n },\n members: Array.from(memberMap.values()),\n messages,\n }\n options.onProgress?.({ progress: 1, messagesProcessed: messages.length })\n return result\n}\n\nfunction parseTelegramFullExport(content: string): TelegramFullExport {\n let value: unknown\n try {\n value = JSON.parse(content)\n } catch (error) {\n throw new Error('Invalid Telegram full export JSON', { cause: error })\n }\n if (!isTelegramFullExport(value)) throw new Error('Invalid Telegram full export JSON')\n return value\n}\n\nfunction isTelegramFullExport(value: unknown): value is TelegramFullExport {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false\n const candidate = value as { about?: unknown; chats?: { list?: unknown } }\n return (\n typeof candidate.about === 'string' &&\n /Telegram/i.test(candidate.about) &&\n Array.isArray(candidate.chats?.list) &&\n candidate.chats.list.every(isTelegramChat)\n )\n}\n\nfunction isTelegramChat(value: unknown): value is TelegramChat {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false\n const candidate = value as Partial<TelegramChat>\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.type === 'string' &&\n typeof candidate.id === 'number' &&\n Number.isFinite(candidate.id) &&\n Array.isArray(candidate.messages)\n )\n}\n\nfunction resolveSender(message: TelegramMessage): { senderPlatformId: string; senderName: string } {\n if (message.type === 'service') {\n const senderPlatformId = extractPlatformId(message.actor_id)\n return { senderPlatformId, senderName: message.actor || 'System' }\n }\n const senderPlatformId = extractPlatformId(message.from_id)\n return { senderPlatformId, senderName: message.from || senderPlatformId }\n}\n\nfunction getBasename(fileName: string): string {\n return fileName.split(/[\\\\/]/).pop() ?? fileName\n}\n\nfunction yieldToEventLoop(): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, 0))\n}\n","import * as fs from 'fs'\nimport * as readline from 'readline'\nimport { KNOWN_PLATFORMS, MessageType } from '@openchatlab/shared-types'\n\nimport { WHATSAPP_FILENAME_SIGNATURES, WHATSAPP_HEAD_SIGNATURES, WhatsAppTextAccumulator } from '../browser/whatsapp'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type { FormatFeature, FormatModule, ParseEvent, ParseOptions, Parser } from '../types'\nimport { createProgress, getFileSize } from '../utils'\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.WHATSAPP_NATIVE,\n name: 'WhatsApp 官方导出 (TXT)',\n platform: KNOWN_PLATFORMS.WHATSAPP,\n priority: 25,\n extensions: ['.txt'],\n signatures: {\n head: WHATSAPP_HEAD_SIGNATURES,\n filename: WHATSAPP_FILENAME_SIGNATURES,\n },\n}\n\nasync function* parseWhatsApp(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n const totalBytes = getFileSize(filePath)\n const accumulator = new WhatsAppTextAccumulator(filePath)\n let bytesRead = 0\n let lastReportedMessages = 0\n\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n onLog?.('info', `Starting WhatsApp TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`)\n\n const fileStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n const lines = readline.createInterface({ input: fileStream, crlfDelay: Infinity })\n fileStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n for await (const line of lines) {\n accumulator.pushLine(line)\n if (accumulator.messageCount >= lastReportedMessages + 500) {\n lastReportedMessages = accumulator.messageCount\n onProgress?.(\n createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n accumulator.messageCount,\n `Processed ${accumulator.messageCount} messages`\n )\n )\n }\n }\n\n const result = accumulator.finish()\n yield { type: 'meta', data: result.meta }\n yield { type: 'members', data: result.members }\n for (let index = 0; index < result.messages.length; index += batchSize) {\n yield { type: 'messages', data: result.messages.slice(index, index + batchSize) }\n }\n\n const doneProgress = createProgress('done', totalBytes, totalBytes, result.messages.length, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n const typeCounts = new Map<MessageType, number>()\n for (const message of result.messages) {\n typeCounts.set(message.type, (typeCounts.get(message.type) ?? 0) + 1)\n }\n onLog?.(\n 'info',\n `WhatsApp TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members, type ${result.meta.type}`\n )\n onLog?.(\n 'info',\n `Message types: ${Array.from(typeCounts.entries(), ([type, count]) => `${type}=${count}`).join(', ')}`\n )\n if (result.skippedLines > 0) onLog?.('info', `Skipped ${result.skippedLines} unrecognized lines`)\n\n yield {\n type: 'done',\n data: { messageCount: result.messages.length, memberCount: result.members.length },\n }\n}\n\nexport const parser_: Parser = {\n feature,\n parse: parseWhatsApp,\n}\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","import { ChatType, KNOWN_PLATFORMS, MessageType } from '@openchatlab/shared-types'\nimport type { ParsedMember, ParsedMessage } from '@openchatlab/shared-types'\n\nimport type { ParsedMeta } from '../types'\n\nexport const WHATSAPP_HEAD_SIGNATURES = [\n /消息和通话已进行端到端加密/,\n /訊息與通話已受端對端加密保護/,\n /Messages and calls are end-to-end encrypted/i,\n /你发送给自己的消息已进行端到端加密/,\n /\\d{1,4}\\/\\d{1,2}\\/\\d{2,4},?\\s+\\d{1,2}:\\d{2}(?::\\d{2})?(?:[\\s\\u2009\\u202F]*(?:AM|PM|A\\.M\\.|P\\.M\\.|\\u4e0a午|\\u4e0b午|\\u5348前|\\u5348後|오전|오후))?\\s+- /i,\n /\\[\\d{1,4}\\/\\d{1,2}\\/\\d{1,4}[\\s,].*\\d{1,2}:\\d{2}(?::\\d{2})?.*\\] /,\n]\n\nexport const WHATSAPP_FILENAME_SIGNATURES = [\n /^与.+的\\s*WhatsApp\\s*聊天\\.txt$/i,\n /^與.+的\\s*WhatsApp\\s*對話\\.txt$/i,\n /WhatsApp/i,\n]\n\nexport interface WhatsAppTextParseOptions {\n checkCancelled?: () => void\n onProgress?: (progress: { progress: number; messagesProcessed: number }) => void\n yieldEvery?: number\n}\n\nexport interface WhatsAppTextParseResult {\n meta: ParsedMeta\n members: ParsedMember[]\n messages: ParsedMessage[]\n skippedLines: number\n}\n\ninterface MemberInfo {\n platformId: string\n nickname: string\n}\n\ninterface PendingMessage {\n timestamp: number\n sender: string | null\n contentLines: string[]\n isSystem: boolean\n}\n\nconst MESSAGE_LINE_REGEX_V1 =\n /^(\\d{1,4}\\/\\d{1,2}\\/\\d{2,4},?\\s+\\d{1,2}:\\d{2}(?::\\d{2})?(?:[\\s\\u2009\\u202F]*(?:AM|PM|A\\.M\\.|P\\.M\\.|\\u4e0a午|\\u4e0b午|\\u5348前|\\u5348後|오전|오후))?) - (.+)$/i\nconst MESSAGE_LINE_REGEX_V2 = /^\\[([^\\]]+)\\] (.+)$/\nconst SENDER_CONTENT_REGEX = /^(.+?):[\\s\\u200E]*(.*)$/\n\nconst SYSTEM_MESSAGE_PATTERNS = [\n /消息和通话已进行端到端加密/,\n /创建了此群组/,\n /加入群组/,\n /添加了/,\n /退出了群组/,\n /移除了/,\n /更改了本群组/,\n /已将此群组的设置更改为/,\n /这条消息已删除/,\n /限时消息功能/,\n /正在等待此消息/,\n /訊息與通話已受端對端加密保護/,\n /建立了此群組/,\n /加入了群組/,\n /已新增/,\n /已離開群組/,\n /已移除/,\n /已變更本群組/,\n /此訊息已刪除/,\n /限時訊息/,\n /Messages and calls are end-to-end encrypted/i,\n /created this group/i,\n /joined the group/i,\n /added/i,\n /left the group/i,\n /removed/i,\n /changed this group/i,\n /This message was deleted/i,\n /disappearing messages/i,\n]\nconst SYSTEM_MESSAGE_REGEX = new RegExp(SYSTEM_MESSAGE_PATTERNS.map((pattern) => pattern.source).join('|'), 'i')\n\nconst TIMESTAMP_SPACING = String.raw`(?:\\s|\\u2009|\\u202F|\\uFEFF|\\u200E|\\u200F|\\u200B|\\u200C|\\u200D|\\u2060)`\nconst COMMON_TIMESTAMP_REGEX = new RegExp(\n String.raw`^${TIMESTAMP_SPACING}*(\\d{1,4})\\/(\\d{1,2})\\/(\\d{1,4})(?:,|${TIMESTAMP_SPACING})+(\\d{1,2}):(\\d{2})(?::(\\d{2}))?(?:${TIMESTAMP_SPACING}+(AM|PM|A\\.M\\.|P\\.M\\.|上午|下午|午前|午後|오전|오후))?${TIMESTAMP_SPACING}*$`,\n 'i'\n)\n\nconst AMPM_MARKERS: [RegExp, boolean][] = [\n [/\\bPM\\b/i, true],\n [/\\bP\\.M\\.(?!\\w)/i, true],\n [/下午/, true],\n [/午後/, true],\n [/오후/, true],\n [/\\bAM\\b/i, false],\n [/\\bA\\.M\\.(?!\\w)/i, false],\n [/上午/, false],\n [/午前/, false],\n [/오전/, false],\n]\n\nexport function detectWhatsAppText(head: string, fileName: string): boolean {\n const basename = getBasename(fileName)\n if (!basename.toLowerCase().endsWith('.txt')) return false\n return (\n WHATSAPP_HEAD_SIGNATURES.some((signature) => signature.test(head)) ||\n WHATSAPP_FILENAME_SIGNATURES.some((signature) => signature.test(basename))\n )\n}\n\nexport async function parseWhatsAppText(\n content: string,\n fileName: string,\n options: WhatsAppTextParseOptions = {}\n): Promise<WhatsAppTextParseResult> {\n const accumulator = new WhatsAppTextAccumulator(fileName)\n const lines = content.split('\\n')\n const yieldEvery = Math.max(1, options.yieldEvery ?? 5000)\n\n for (let index = 0; index < lines.length; index += 1) {\n options.checkCancelled?.()\n accumulator.pushLine(lines[index])\n if (index > 0 && index % yieldEvery === 0) {\n options.onProgress?.({ progress: index / lines.length, messagesProcessed: accumulator.messageCount })\n await new Promise<void>((resolve) => setTimeout(resolve, 0))\n options.checkCancelled?.()\n }\n }\n\n options.checkCancelled?.()\n const result = accumulator.finish()\n options.onProgress?.({ progress: 1, messagesProcessed: result.messages.length })\n return result\n}\n\nexport class WhatsAppTextAccumulator {\n private readonly fileChatName: string\n private readonly memberMap = new Map<string, MemberInfo>()\n private readonly messages: ParsedMessage[] = []\n private currentMessage: PendingMessage | null = null\n private skippedLines = 0\n\n constructor(private readonly fileName: string) {\n this.fileChatName = extractNameFromFileName(fileName)\n }\n\n get messageCount(): number {\n return this.messages.length + (this.currentMessage ? 1 : 0)\n }\n\n pushLine(line: string): void {\n const cleanedLine = cleanLine(line)\n const v1Match = cleanedLine.match(MESSAGE_LINE_REGEX_V1)\n if (v1Match) {\n this.saveCurrentMessage()\n this.currentMessage = createPendingMessage(parseFlexibleTimestamp(v1Match[1]) ?? 0, v1Match[2])\n return\n }\n\n const v2Match = cleanedLine.match(MESSAGE_LINE_REGEX_V2)\n if (v2Match) {\n const timestamp = parseFlexibleTimestamp(v2Match[1])\n if (timestamp !== null) {\n this.saveCurrentMessage()\n this.currentMessage = createPendingMessage(timestamp, v2Match[2])\n return\n }\n }\n\n if (this.currentMessage && cleanedLine) {\n this.currentMessage.contentLines.push(cleanedLine)\n } else if (cleanedLine) {\n this.skippedLines += 1\n }\n }\n\n finish(): WhatsAppTextParseResult {\n this.saveCurrentMessage()\n const chatType = this.memberMap.size > 2 ? ChatType.GROUP : ChatType.PRIVATE\n const chatName =\n chatType === ChatType.PRIVATE && shouldInferPrivateChatName(this.fileName, this.fileChatName)\n ? inferPrivateChatName(this.memberMap, this.fileChatName)\n : this.fileChatName\n\n return {\n meta: { name: chatName, platform: KNOWN_PLATFORMS.WHATSAPP, type: chatType },\n members: Array.from(this.memberMap.values(), (member) => ({\n platformId: member.platformId,\n accountName: member.nickname,\n })),\n messages: this.messages,\n skippedLines: this.skippedLines,\n }\n }\n\n private saveCurrentMessage(): void {\n if (!this.currentMessage) return\n const content = this.currentMessage.contentLines.join('\\n').trim()\n const senderPlatformId = this.currentMessage.sender ?? 'system'\n const senderName = this.currentMessage.sender ?? '系统消息'\n\n this.messages.push({\n senderPlatformId,\n senderAccountName: senderName,\n timestamp: this.currentMessage.timestamp,\n type: detectMessageType(content, this.currentMessage.isSystem),\n content: content || null,\n })\n if (this.currentMessage.sender) {\n this.memberMap.set(senderPlatformId, { platformId: senderPlatformId, nickname: senderName })\n }\n this.currentMessage = null\n }\n}\n\nfunction createPendingMessage(timestamp: number, restContent: string): PendingMessage {\n const senderMatch = restContent.match(SENDER_CONTENT_REGEX)\n const isSystem = isSystemMessage(restContent)\n if (senderMatch && !isSystem) {\n return { timestamp, sender: senderMatch[1].trim(), contentLines: [senderMatch[2]], isSystem }\n }\n return { timestamp, sender: null, contentLines: [restContent], isSystem }\n}\n\nfunction extractNameFromFileName(fileName: string): string {\n const basename = getBasename(fileName)\n const simplifiedChinese = basename.match(/^与(.+?)的\\s*WhatsApp\\s*聊天\\.txt$/i)\n if (simplifiedChinese) return simplifiedChinese[1].trim()\n const traditionalChinese = basename.match(/^與(.+?)的\\s*WhatsApp\\s*對話\\.txt$/i)\n if (traditionalChinese) return traditionalChinese[1].trim()\n return basename.replace(/\\.txt$/i, '') || '未知聊天'\n}\n\nfunction shouldInferPrivateChatName(fileName: string, fallbackName: string): boolean {\n const basename = getBasename(fileName)\n return /^_chat\\.txt$/i.test(basename) || /^whatsapp(?:[-_\\s].*)?$/i.test(fallbackName)\n}\n\nfunction inferPrivateChatName(memberMap: Map<string, MemberInfo>, fallbackName: string): string {\n return Array.from(memberMap.values()).find((member) => member.platformId !== 'system')?.nickname ?? fallbackName\n}\n\nfunction getBasename(fileName: string): string {\n return fileName.split(/[\\\\/]/).pop() ?? fileName\n}\n\nfunction cleanLine(line: string): string {\n return line.replace(/^(?:\\uFEFF|\\u200E|\\u200F|\\u200B|\\u200C|\\u200D|\\u2060)+/, '').trim()\n}\n\nfunction isSystemMessage(content: string): boolean {\n return SYSTEM_MESSAGE_REGEX.test(content)\n}\n\nfunction detectMessageType(content: string, knownSystem = false): MessageType {\n const trimmed = content.trim()\n if (\n trimmed === '<省略影音内容>' ||\n trimmed === '<已省略多媒體檔案>' ||\n trimmed === '圖片已略去' ||\n trimmed === '影片已略去' ||\n trimmed === '音訊已略去' ||\n trimmed === 'image omitted' ||\n trimmed === 'video omitted' ||\n trimmed === 'audio omitted'\n ) {\n return MessageType.IMAGE\n }\n if (trimmed.includes('<已附加:') || trimmed.includes('<附件:') || trimmed.includes('<已附加:')) {\n return MessageType.FILE\n }\n if (trimmed === '貼圖已忽略' || trimmed === '貼圖已略去') return MessageType.EMOJI\n if (trimmed === '这条消息已删除' || trimmed.startsWith('此訊息已刪除') || trimmed.startsWith('你已刪除此訊息')) {\n return MessageType.RECALL\n }\n if (knownSystem || isSystemMessage(trimmed)) return MessageType.SYSTEM\n return MessageType.TEXT\n}\n\nfunction parseFlexibleTimestamp(raw: string): number | null {\n const commonMatch = raw.match(COMMON_TIMESTAMP_REGEX)\n if (commonMatch) {\n return buildTimestamp(\n Number.parseInt(commonMatch[1], 10),\n Number.parseInt(commonMatch[2], 10),\n Number.parseInt(commonMatch[3], 10),\n Number.parseInt(commonMatch[4], 10),\n Number.parseInt(commonMatch[5], 10),\n Number.parseInt(commonMatch[6] ?? '0', 10),\n parseAmPm(commonMatch[7])\n )\n }\n\n let value = raw.replace(/(?:\\u2009|\\u202F|\\uFEFF|\\u200E|\\u200F|\\u200B|\\u200C|\\u200D|\\u2060)/g, ' ').trim()\n let isPm: boolean | null = null\n for (const [pattern, pm] of AMPM_MARKERS) {\n if (!pattern.test(value)) continue\n isPm = pm\n value = value.replace(pattern, '').trim()\n break\n }\n value = value.replace(/,/g, ' ').replace(/\\s+/g, ' ').trim()\n\n const match = value.match(/^(\\d{1,4}\\/\\d{1,2}\\/\\d{1,4})\\s+(\\d{1,2}:\\d{2}(?::\\d{2})?)$/)\n if (!match) return null\n const dateParts = match[1].split('/').map((part) => Number.parseInt(part, 10))\n const timeParts = match[2].split(':').map((part) => Number.parseInt(part, 10))\n\n return buildTimestamp(dateParts[0], dateParts[1], dateParts[2], timeParts[0], timeParts[1], timeParts[2] ?? 0, isPm)\n}\n\nfunction parseAmPm(marker: string | undefined): boolean | null {\n if (!marker) return null\n return /^(?:PM|P\\.M\\.|下午|午後|오후)$/i.test(marker)\n}\n\nfunction buildTimestamp(\n firstDatePart: number,\n secondDatePart: number,\n thirdDatePart: number,\n rawHour: number,\n minute: number,\n second: number,\n isPm: boolean | null\n): number | null {\n let year: number\n let month: number\n let day: number\n if (firstDatePart > 31) {\n year = firstDatePart\n month = secondDatePart\n day = thirdDatePart\n } else if (thirdDatePart > 31) {\n day = firstDatePart\n month = secondDatePart\n year = thirdDatePart\n } else {\n month = firstDatePart\n day = secondDatePart\n year = 2000 + thirdDatePart\n }\n\n let hour = rawHour\n if (isPm === true && hour !== 12) hour += 12\n if (isPm === false && hour === 12) hour = 0\n\n const date = new Date(year, month - 1, day, hour, minute, second)\n const timestamp = Math.floor(date.getTime() / 1000)\n return Number.isNaN(timestamp) ? null : timestamp\n}\n","import * as fs from 'fs'\nimport * as readline from 'readline'\nimport { KNOWN_PLATFORMS } from '@openchatlab/shared-types'\n\nimport { QQ_HEAD_SIGNATURES, QqTextAccumulator } from '../browser/qq'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type { FormatFeature, FormatModule, ParseEvent, ParseOptions, Parser } from '../types'\nimport { createProgress, getFileSize } from '../utils'\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.QQ_NATIVE,\n name: 'QQ 官方导出 (TXT)',\n platform: KNOWN_PLATFORMS.QQ,\n priority: 30,\n extensions: ['.txt'],\n signatures: { head: QQ_HEAD_SIGNATURES },\n}\n\nasync function* parseQq(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n const totalBytes = getFileSize(filePath)\n let bytesRead = 0\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n onLog?.('info', `Starting QQ TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`)\n\n const accumulator = new QqTextAccumulator(filePath)\n const fileStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n const lines = readline.createInterface({ input: fileStream, crlfDelay: Infinity })\n fileStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n let lastReportedCount = -1\n for await (const line of lines) {\n accumulator.pushLine(line)\n const messageCount = accumulator.messageCount\n if (messageCount > 0 && messageCount % 1000 === 0 && messageCount !== lastReportedCount) {\n lastReportedCount = messageCount\n onProgress?.(createProgress('parsing', bytesRead, totalBytes, messageCount, `Processed ${messageCount} messages`))\n }\n }\n\n const result = accumulator.finish()\n yield { type: 'meta', data: result.meta }\n yield { type: 'members', data: result.members }\n for (let index = 0; index < result.messages.length; index += batchSize) {\n yield { type: 'messages', data: result.messages.slice(index, index + batchSize) }\n }\n\n const doneProgress = createProgress('done', totalBytes, totalBytes, result.messages.length, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n onLog?.('info', `QQ TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members`)\n if (result.skippedLines > 0) {\n onLog?.('info', `Skipped ${result.skippedLines} unrecognized lines while parsing QQ TXT`)\n }\n yield {\n type: 'done',\n data: { messageCount: result.messages.length, memberCount: result.members.length },\n }\n}\n\nexport const parser_: Parser = { feature, parse: parseQq }\n\nconst module_: FormatModule = { feature, parser: parser_ }\n\nexport default module_\n","import { ChatType, KNOWN_PLATFORMS, MessageType } from '@openchatlab/shared-types'\nimport type { ParsedMember, ParsedMessage } from '@openchatlab/shared-types'\n\nimport type { ParsedMeta } from '../types'\n\nexport const QQ_HEAD_SIGNATURES = [/消息记录(此消息记录为文本格式/, /消息对象:/, /多人聊天/]\n\nexport interface QqTextParseOptions {\n checkCancelled?: () => void\n onProgress?: (progress: { progress: number; messagesProcessed: number }) => void\n yieldEvery?: number\n}\n\nexport interface QqTextParseResult {\n meta: ParsedMeta\n members: ParsedMember[]\n messages: ParsedMessage[]\n skippedLines: number\n}\n\ninterface MemberInfo {\n platformId: string\n nickname: string\n}\n\ninterface PendingMessage {\n timestamp: number\n platformId: string\n nickname: string\n contentLines: string[]\n}\n\nconst MESSAGE_HEADER_REGEX = /^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) (.+?)(?:\\(([^)]+)\\)|<([^>]+)>)?$/\nconst GROUP_NAME_REGEX = /^消息对象:(.+)$/\n\nexport function detectQqText(head: string, fileName: string): boolean {\n const basename = getBasename(fileName)\n return basename.toLowerCase().endsWith('.txt') && QQ_HEAD_SIGNATURES.some((signature) => signature.test(head))\n}\n\nexport async function parseQqText(\n content: string,\n fileName: string,\n options: QqTextParseOptions = {}\n): Promise<QqTextParseResult> {\n const accumulator = new QqTextAccumulator(fileName)\n const lines = content.split('\\n')\n const yieldEvery = Math.max(1, options.yieldEvery ?? 5000)\n\n for (let index = 0; index < lines.length; index += 1) {\n options.checkCancelled?.()\n accumulator.pushLine(lines[index].replace(/\\r$/, ''))\n if (index > 0 && index % yieldEvery === 0) {\n options.onProgress?.({ progress: index / lines.length, messagesProcessed: accumulator.messageCount })\n await new Promise<void>((resolve) => setTimeout(resolve, 0))\n options.checkCancelled?.()\n }\n }\n\n options.checkCancelled?.()\n const result = accumulator.finish()\n options.onProgress?.({ progress: 1, messagesProcessed: result.messages.length })\n return result\n}\n\nexport class QqTextAccumulator {\n private readonly fallbackName: string\n private readonly memberMap = new Map<string, MemberInfo>()\n private readonly lastValidNickname = new Map<string, string>()\n private readonly messages: ParsedMessage[] = []\n private groupName = '未知群聊'\n private currentMessage: PendingMessage | null = null\n private skippedLines = 0\n\n constructor(fileName: string) {\n this.fallbackName = extractNameFromFileName(fileName)\n }\n\n get messageCount(): number {\n return this.messages.length + (this.currentMessage ? 1 : 0)\n }\n\n pushLine(line: string): void {\n const groupMatch = line.match(GROUP_NAME_REGEX)\n if (groupMatch) {\n this.groupName = groupMatch[1].trim()\n return\n }\n\n const headerMatch = line.match(MESSAGE_HEADER_REGEX)\n if (headerMatch) {\n this.saveCurrentMessage()\n const platformId = headerMatch[3] || headerMatch[4] || cleanNickname(headerMatch[2].trim())\n let nickname = cleanNickname(headerMatch[2].trim())\n if (nickname === platformId && headerMatch[3]) {\n nickname = this.lastValidNickname.get(platformId) ?? nickname\n } else if (headerMatch[3] || headerMatch[4]) {\n this.lastValidNickname.set(platformId, nickname)\n }\n this.currentMessage = {\n timestamp: parseLocalTime(headerMatch[1]),\n platformId,\n nickname,\n contentLines: [],\n }\n return\n }\n\n if (this.currentMessage) {\n if (line.startsWith('=====') || line.startsWith('消息记录') || line.startsWith('消息分组')) return\n this.currentMessage.contentLines.push(line)\n return\n }\n\n const trimmed = line.trim()\n if (trimmed && !trimmed.startsWith('=====') && !trimmed.startsWith('消息记录') && !trimmed.startsWith('消息分组')) {\n this.skippedLines += 1\n }\n }\n\n finish(): QqTextParseResult {\n this.saveCurrentMessage()\n return {\n meta: {\n name: this.groupName === '未知群聊' ? this.fallbackName : this.groupName,\n platform: KNOWN_PLATFORMS.QQ,\n type: ChatType.GROUP,\n },\n members: Array.from(this.memberMap.values(), (member) => ({\n platformId: member.platformId,\n accountName: member.nickname,\n })),\n messages: this.messages,\n skippedLines: this.skippedLines,\n }\n }\n\n private saveCurrentMessage(): void {\n if (!this.currentMessage) return\n const content = this.currentMessage.contentLines.join('\\n').trim()\n this.messages.push({\n senderPlatformId: this.currentMessage.platformId,\n senderAccountName: this.currentMessage.nickname,\n timestamp: this.currentMessage.timestamp,\n type: detectMessageType(content),\n content: content || null,\n })\n this.memberMap.set(this.currentMessage.platformId, {\n platformId: this.currentMessage.platformId,\n nickname: this.currentMessage.nickname,\n })\n this.currentMessage = null\n }\n}\n\nfunction extractNameFromFileName(fileName: string): string {\n return getBasename(fileName).replace(/\\.txt$/i, '') || '未知群聊'\n}\n\nfunction getBasename(fileName: string): string {\n return fileName.split(/[\\\\/]/).pop() ?? fileName\n}\n\nfunction cleanNickname(nickname: string): string {\n return nickname.replace(/^(【[^】]*】\\s*)+/, '').trim()\n}\n\nfunction parseLocalTime(timeText: string): number {\n return Math.floor(new Date(timeText.replace(' ', 'T')).getTime() / 1000)\n}\n\nfunction detectMessageType(content: string): MessageType {\n const trimmed = content.trim()\n if (trimmed === '[图片]') return MessageType.IMAGE\n if (trimmed === '[表情]') return MessageType.EMOJI\n if (trimmed === '[语音]') return MessageType.VOICE\n if (trimmed === '[视频]') return MessageType.VIDEO\n if (trimmed === '[文件]') return MessageType.FILE\n if (trimmed === '[位置]' || trimmed === '[地理位置]') return MessageType.LOCATION\n if (trimmed === '[链接]' || trimmed === '[卡片消息]') return MessageType.LINK\n if (trimmed === '[红包]' || trimmed.includes('发出了红包')) return MessageType.RED_PACKET\n if (trimmed === '[转账]' || trimmed.includes('向你转账')) return MessageType.TRANSFER\n if (trimmed.includes('拍了拍') || trimmed === '[拍一拍]') return MessageType.POKE\n if (trimmed === '[语音通话]' || trimmed === '[视频通话]' || trimmed.includes('通话时长')) {\n return MessageType.CALL\n }\n if (trimmed === '[分享]' || trimmed === '[音乐]' || trimmed === '[小程序]') return MessageType.SHARE\n if (trimmed.startsWith('[回复]')) return MessageType.REPLY\n if (trimmed === '[转发]' || trimmed === '[聊天记录]') return MessageType.FORWARD\n if (trimmed.includes('撤回了一条消息') || trimmed === '[撤回]') return MessageType.RECALL\n if (\n trimmed.includes('加入了群聊') ||\n trimmed.includes('退出了群聊') ||\n trimmed.includes('被移出群聊') ||\n trimmed.includes('修改了群名称') ||\n trimmed.includes('成为新群主') ||\n trimmed.includes('群公告')\n ) {\n return MessageType.SYSTEM\n }\n if (trimmed.startsWith('[') && trimmed.endsWith(']')) return MessageType.OTHER\n return MessageType.TEXT\n}\n","/**\n * Instagram 官方导出格式解析器\n * 适配:Instagram 账号数据下载功能导出的 JSON 文件\n *\n * 文件结构:\n * - participants: 参与者数组 [{ name: string }]\n * - messages: 消息数组(逆序,最新在前)\n * - title: 对话标题(群名或对方用户名)\n * - thread_path: 线程路径,如 \"inbox/xxx_123456\"\n * - joinable_mode: 仅群聊有,包含入群链接\n *\n * 特殊处理:\n * - 编码问题:Instagram 将 UTF-8 字节按 Latin-1 编码后存储,需要解码\n * - 消息逆序:需要反转为正序\n * - 无用户 ID:使用用户名作为 platformId\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n Parser,\n ParseOptions,\n ParseEvent,\n ParsedMeta,\n ParsedMember,\n ParsedMessage,\n} from '../types'\nimport { getFileSize, createProgress } from '../utils'\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.INSTAGRAM_NATIVE,\n name: 'Instagram 官方导出',\n platform: KNOWN_PLATFORMS.INSTAGRAM,\n priority: 25,\n extensions: ['.json'],\n signatures: {\n // 使用 Instagram 特有的字段作为签名(在文件头部就能匹配)\n // is_geoblocked_for_viewer 是 Instagram 消息特有的字段\n requiredFields: ['participants', 'messages'],\n head: [/\"is_geoblocked_for_viewer\"\\s*:/],\n },\n}\n\n// ==================== 类型定义 ====================\n\ninterface InstagramParticipant {\n name: string\n}\n\ninterface InstagramPhoto {\n uri: string\n creation_timestamp?: number\n}\n\ninterface InstagramVideo {\n uri: string\n creation_timestamp?: number\n}\n\ninterface InstagramAudio {\n uri: string\n creation_timestamp?: number\n}\n\ninterface InstagramShare {\n link?: string\n share_text?: string\n original_content_owner?: string\n}\n\ninterface InstagramReaction {\n reaction: string\n actor: string\n}\n\ninterface InstagramMessage {\n sender_name: string\n timestamp_ms: number\n content?: string\n photos?: InstagramPhoto[]\n videos?: InstagramVideo[]\n audio_files?: InstagramAudio[]\n share?: InstagramShare\n reactions?: InstagramReaction[]\n is_geoblocked_for_viewer?: boolean\n is_unsent_image_by_messenger_kid_parent?: boolean\n}\n\ninterface InstagramData {\n participants: InstagramParticipant[]\n messages: InstagramMessage[]\n title: string\n is_still_participant?: boolean\n thread_path: string\n magic_words?: unknown[]\n joinable_mode?: {\n mode: number\n link: string\n }\n}\n\n// ==================== 辅助函数 ====================\n\nconst UTF8_DECODER = new TextDecoder('utf-8')\n\n/**\n * 解码 Instagram 特殊编码的文本\n * Instagram 将 UTF-8 字节按 Latin-1 编码后存储\n */\nfunction decodeInstagramText(text: string): string {\n try {\n // 将每个字符的 charCode 收集为字节数组\n const bytes = new Uint8Array(text.length)\n for (let i = 0; i < text.length; i++) {\n bytes[i] = text.charCodeAt(i)\n }\n // 用 UTF-8 解码\n return UTF8_DECODER.decode(bytes)\n } catch {\n return text // 解码失败则返回原文\n }\n}\n\n/**\n * 从文件名提取名称(备用)\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath)\n return basename.replace(/\\.json$/i, '') || '未知对话'\n}\n\n/**\n * 判断是否为系统消息\n */\nconst SYSTEM_MESSAGE_PATTERNS = [\n 'You created the group',\n 'created the group',\n 'added',\n 'to the group',\n 'left the group',\n 'removed',\n 'named the group',\n 'changed the group photo',\n 'Reacted',\n 'sent an attachment',\n 'liked a message',\n 'changed the theme',\n 'set the nickname',\n]\n\nfunction isSystemMessage(content: string): boolean {\n return SYSTEM_MESSAGE_PATTERNS.some((pattern) => content.includes(pattern))\n}\n\n/**\n * 判断消息类型\n */\nfunction detectMessageType(msg: InstagramMessage): MessageType {\n const content = msg.content || ''\n\n // 1. 系统消息判断\n if (content && isSystemMessage(content)) {\n return MessageType.SYSTEM\n }\n\n // 2. 媒体消息\n if (msg.photos?.length) return MessageType.IMAGE\n if (msg.videos?.length) return MessageType.VIDEO\n if (msg.audio_files?.length) return MessageType.VOICE\n\n // 3. 分享消息\n if (msg.share) {\n const link = msg.share.link || ''\n if (link.includes('giphy.com')) return MessageType.EMOJI\n return MessageType.LINK\n }\n\n // 4. 文本消息\n if (content) return MessageType.TEXT\n\n // 5. 空消息(位置分享、通话等已删除的消息)\n return MessageType.OTHER\n}\n\n/**\n * 获取消息内容\n */\nfunction getMessageContent(msg: InstagramMessage): string | null {\n // 文本内容\n if (msg.content) {\n return decodeInstagramText(msg.content)\n }\n\n // 图片\n if (msg.photos?.length) {\n return `[图片] ${msg.photos[0].uri}`\n }\n\n // 视频\n if (msg.videos?.length) {\n return `[视频] ${msg.videos[0].uri}`\n }\n\n // 语音\n if (msg.audio_files?.length) {\n return `[语音] ${msg.audio_files[0].uri}`\n }\n\n // 分享\n if (msg.share) {\n const link = msg.share.link || ''\n if (link.includes('giphy.com')) {\n return `[GIF] ${link}`\n }\n return `[链接] ${link}`\n }\n\n // 空消息\n return '[未知消息]'\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseInstagram(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n\n const totalBytes = getFileSize(filePath)\n let messagesProcessed = 0\n\n // 发送初始进度\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '正在解析 Instagram 聊天记录...')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n onLog?.('info', `开始解析 Instagram 聊天记录,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 读取并解析 JSON 文件\n let data: InstagramData\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n data = JSON.parse(content)\n } catch (error) {\n const err = new Error(`无法解析 Instagram JSON 文件: ${error}`)\n yield { type: 'error', data: err }\n return\n }\n\n // 判断聊天类型\n const isGroup = data.participants.length > 2 || !!data.joinable_mode\n const chatType = isGroup ? ChatType.GROUP : ChatType.PRIVATE\n\n // 判断 Owner\n let ownerId: string | undefined\n if (chatType === ChatType.PRIVATE) {\n // 私聊:title 是对方名字,另一个参与者是 owner\n const owner = data.participants.find((p) => decodeInstagramText(p.name) !== decodeInstagramText(data.title))\n ownerId = owner ? decodeInstagramText(owner.name) : undefined\n } else {\n // 群聊:找 \"You created the group.\" 消息的发送者\n const createMsg = data.messages.find((m) => m.content === 'You created the group.')\n ownerId = createMsg ? decodeInstagramText(createMsg.sender_name) : undefined\n }\n\n // 发送 meta\n const meta: ParsedMeta = {\n name: decodeInstagramText(data.title) || extractNameFromFilePath(filePath),\n platform: KNOWN_PLATFORMS.INSTAGRAM,\n type: chatType,\n ownerId,\n }\n yield { type: 'meta', data: meta }\n\n // 收集成员信息\n const memberMap = new Map<string, ParsedMember>()\n for (const participant of data.participants) {\n const name = decodeInstagramText(participant.name)\n memberMap.set(name, {\n platformId: name,\n accountName: name,\n })\n }\n\n // 发送成员\n const members = Array.from(memberMap.values())\n yield { type: 'members', data: members }\n\n // Instagram 消息是逆序的;从尾部遍历即可按时间正序输出,避免复制整份消息数组。\n const messageBatch: ParsedMessage[] = []\n\n for (let index = data.messages.length - 1; index >= 0; index -= 1) {\n const msg = data.messages[index]\n const senderName = decodeInstagramText(msg.sender_name)\n const timestamp = Math.floor(msg.timestamp_ms / 1000) // 毫秒转秒\n const type = detectMessageType(msg)\n const content = getMessageContent(msg)\n\n // 确保成员存在(处理消息中出现但不在 participants 中的情况)\n if (!memberMap.has(senderName)) {\n memberMap.set(senderName, {\n platformId: senderName,\n accountName: senderName,\n })\n }\n\n messageBatch.push({\n senderPlatformId: senderName,\n senderAccountName: senderName,\n timestamp,\n type,\n content,\n })\n\n messagesProcessed++\n\n // 分批输出消息\n if (messageBatch.length >= batchSize) {\n yield { type: 'messages', data: [...messageBatch] }\n messageBatch.length = 0\n\n const progress = createProgress(\n 'parsing',\n Math.floor((messagesProcessed / data.messages.length) * totalBytes),\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n onProgress?.(progress)\n }\n }\n\n // 发送剩余消息\n if (messageBatch.length > 0) {\n yield { type: 'messages', data: messageBatch }\n }\n\n // 完成\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '解析完成')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberMap.size} 个成员`)\n\n yield {\n type: 'done',\n data: { messageCount: messagesProcessed, memberCount: memberMap.size },\n }\n}\n\n// ==================== 导出 ====================\n\nexport const parser_: Parser = {\n feature,\n parse: parseInstagram,\n}\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","/**\n * Google Chat Takeout 内部导入格式解析器。\n *\n * ZIP 读取与会话选择由 node-runtime 负责;本解析器只读取其生成的固定 manifest\n * 和同目录 JSON 文件,从而复用现有 streaming importer。\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport streamChain from 'stream-chain'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\nimport { ChatType, KNOWN_PLATFORMS, MessageType } from '@openchatlab/shared-types'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type {\n FormatFeature,\n FormatModule,\n ParseEvent,\n ParseOptions,\n ParsedMember,\n ParsedMessage,\n Parser,\n} from '../types'\nimport { createProgress, getFileSize } from '../utils'\n\nconst { chain } = streamChain\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\n\ninterface GoogleChatManifest {\n format: string\n version: number\n chatId: string\n chatType: 'private' | 'group'\n chatName?: string\n userInfoFile: string\n groupInfoFile: string\n messagesFile: string\n}\n\ninterface GoogleChatUser {\n email?: string\n name?: string\n user_type?: string\n}\n\ninterface GoogleChatUserInfo {\n user?: GoogleChatUser\n}\n\ninterface GoogleChatGroupInfo {\n name?: string\n members?: GoogleChatUser[]\n}\n\ninterface GoogleChatAttachment {\n original_name?: string\n export_name?: string\n}\n\ninterface GoogleChatQuotedMessage {\n creator?: GoogleChatUser\n text?: string\n}\n\ninterface GoogleChatMessage {\n message_id?: string\n created_date?: string\n updated_date?: string\n creator?: GoogleChatUser\n text?: string\n attached_files?: GoogleChatAttachment[]\n quoted_message_metadata?: GoogleChatQuotedMessage\n}\n\nconst ENGLISH_MONTHS = new Map([\n ['january', 0],\n ['february', 1],\n ['march', 2],\n ['april', 3],\n ['may', 4],\n ['june', 5],\n ['july', 6],\n ['august', 7],\n ['september', 8],\n ['october', 9],\n ['november', 10],\n ['december', 11],\n])\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.GOOGLE_CHAT_NATIVE,\n name: 'Google Chat Takeout',\n platform: KNOWN_PLATFORMS.GOOGLE_CHAT,\n priority: 24,\n extensions: ['.json'],\n signatures: {\n head: [/\"format\"\\s*:\\s*\"chatlab-google-chat-takeout\"/],\n requiredFields: ['format', 'version', 'messagesFile'],\n },\n}\n\nfunction normalizeSpaces(value: string): string {\n return value\n .replace(/[\\u00a0\\u202f]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction createUtcTimestamp(\n year: number,\n monthIndex: number,\n day: number,\n hour: number,\n minute: number,\n second: number\n): number | null {\n const timestampMs = Date.UTC(year, monthIndex, day, hour, minute, second)\n const date = new Date(timestampMs)\n if (\n date.getUTCFullYear() !== year ||\n date.getUTCMonth() !== monthIndex ||\n date.getUTCDate() !== day ||\n date.getUTCHours() !== hour ||\n date.getUTCMinutes() !== minute ||\n date.getUTCSeconds() !== second\n ) {\n return null\n }\n return Math.floor(timestampMs / 1000)\n}\n\nfunction parseChineseUtcDate(value: string): number | null {\n const match = normalizeSpaces(value).match(\n /^(\\d{4})年(\\d{1,2})月(\\d{1,2})日(?:星期[一二三四五六日天])?\\s+UTC\\s+(\\d{1,2}):(\\d{2}):(\\d{2})$/\n )\n if (!match) return null\n return createUtcTimestamp(\n Number(match[1]),\n Number(match[2]) - 1,\n Number(match[3]),\n Number(match[4]),\n Number(match[5]),\n Number(match[6])\n )\n}\n\nfunction parseEnglishUtcDate(value: string): number | null {\n const match = normalizeSpaces(value).match(\n /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\\s+([A-Za-z]+)\\s+(\\d{1,2}),\\s+(\\d{4})\\s+at\\s+(\\d{1,2}):(\\d{2}):(\\d{2})\\s+(AM|PM)\\s+UTC$/i\n )\n if (!match) return null\n\n const monthIndex = ENGLISH_MONTHS.get(match[1].toLowerCase())\n if (monthIndex === undefined) return null\n\n let hour = Number(match[4])\n if (hour < 1 || hour > 12) return null\n if (match[7].toUpperCase() === 'AM') {\n if (hour === 12) hour = 0\n } else if (hour !== 12) {\n hour += 12\n }\n\n return createUtcTimestamp(Number(match[3]), monthIndex, Number(match[2]), hour, Number(match[5]), Number(match[6]))\n}\n\n/**\n * Google Takeout 会根据导出语言生成本地化日期文本。这里只接受已验证的\n * 中文和英文 UTC 形式,避免宿主机 locale 或 Date.parse 产生不稳定结果。\n */\nexport function parseGoogleChatDate(value: string): number | null {\n return parseChineseUtcDate(value) ?? parseEnglishUtcDate(value)\n}\n\nfunction normalizeIdentity(user: GoogleChatUser | undefined): { platformId: string; name: string } {\n const email = user?.email?.trim().toLowerCase()\n const name = user?.name?.trim() || email || 'Unknown'\n return {\n platformId: email || name.toLowerCase(),\n name,\n }\n}\n\nfunction readJson<T>(filePath: string): T {\n return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T\n}\n\nfunction resolveManifestFile(manifestDir: string, relativePath: string): string {\n const resolved = path.resolve(manifestDir, relativePath)\n const relative = path.relative(manifestDir, resolved)\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error(`Google Chat manifest path escapes its directory: ${relativePath}`)\n }\n return resolved\n}\n\nfunction readManifest(filePath: string): {\n manifest: GoogleChatManifest\n userInfoPath: string\n groupInfoPath: string\n messagesPath: string\n} {\n const manifest = readJson<GoogleChatManifest>(filePath)\n if (manifest.format !== 'chatlab-google-chat-takeout' || manifest.version !== 1) {\n throw new Error('Invalid Google Chat import manifest')\n }\n if (manifest.chatType !== 'private' && manifest.chatType !== 'group') {\n throw new Error('Invalid Google Chat chat type')\n }\n\n const manifestDir = path.dirname(filePath)\n return {\n manifest,\n userInfoPath: resolveManifestFile(manifestDir, manifest.userInfoFile),\n groupInfoPath: resolveManifestFile(manifestDir, manifest.groupInfoFile),\n messagesPath: resolveManifestFile(manifestDir, manifest.messagesFile),\n }\n}\n\nfunction getAttachmentName(attachment: GoogleChatAttachment): string | null {\n return attachment.original_name?.trim() || attachment.export_name?.trim() || null\n}\n\nfunction buildQuotePrefix(message: GoogleChatMessage): string | null {\n const quote = message.quoted_message_metadata\n const quoteText = quote?.text?.trim()\n if (!quoteText) return null\n const creator = quote?.creator?.name?.trim() || quote?.creator?.email?.trim()\n return creator ? `> ${creator}: ${quoteText}` : `> ${quoteText}`\n}\n\nfunction buildMessageContent(message: GoogleChatMessage): string {\n const parts: string[] = []\n const quote = buildQuotePrefix(message)\n if (quote) parts.push(quote)\n\n const text = message.text?.trim()\n if (text) parts.push(text)\n\n for (const attachment of message.attached_files ?? []) {\n const name = getAttachmentName(attachment)\n if (name) parts.push(`[附件] ${name}`)\n }\n\n return parts.length > 0 ? parts.join('\\n') : '[不支持的 Google Chat 消息]'\n}\n\nfunction detectMessageType(message: GoogleChatMessage): MessageType {\n if (message.text?.trim()) return MessageType.TEXT\n if ((message.attached_files ?? []).some((attachment) => getAttachmentName(attachment))) {\n return MessageType.FILE\n }\n return MessageType.OTHER\n}\n\nfunction deriveChatName(\n manifest: GoogleChatManifest,\n groupInfo: GoogleChatGroupInfo,\n ownerId: string | undefined\n): string {\n if (manifest.chatName?.trim()) return manifest.chatName.trim()\n if (manifest.chatType === 'group' && groupInfo.name?.trim()) return groupInfo.name.trim()\n\n const otherMember = (groupInfo.members ?? [])\n .map(normalizeIdentity)\n .find((member) => !ownerId || member.platformId !== ownerId)\n return (\n otherMember?.name ||\n groupInfo.members?.map((member) => normalizeIdentity(member).name).join(', ') ||\n manifest.chatId\n )\n}\n\nasync function* parseGoogleChat(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n const { manifest, userInfoPath, groupInfoPath, messagesPath } = readManifest(filePath)\n const userInfo = readJson<GoogleChatUserInfo>(userInfoPath)\n const groupInfo = readJson<GoogleChatGroupInfo>(groupInfoPath)\n const owner = userInfo.user ? normalizeIdentity(userInfo.user) : null\n const totalBytes = getFileSize(messagesPath)\n\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n yield {\n type: 'meta',\n data: {\n name: deriveChatName(manifest, groupInfo, owner?.platformId),\n platform: KNOWN_PLATFORMS.GOOGLE_CHAT,\n type: manifest.chatType === 'group' ? ChatType.GROUP : ChatType.PRIVATE,\n ...(manifest.chatType === 'group' ? { groupId: manifest.chatId } : {}),\n ownerId: owner?.platformId,\n },\n }\n\n const memberMap = new Map<string, ParsedMember>()\n for (const member of groupInfo.members ?? []) {\n const identity = normalizeIdentity(member)\n memberMap.set(identity.platformId, {\n platformId: identity.platformId,\n accountName: identity.name,\n })\n }\n if (owner && !memberMap.has(owner.platformId)) {\n memberMap.set(owner.platformId, {\n platformId: owner.platformId,\n accountName: owner.name,\n })\n }\n yield { type: 'members', data: Array.from(memberMap.values()) }\n\n const readStream = fs.createReadStream(messagesPath, { encoding: 'utf8' })\n let bytesRead = 0\n let messagesProcessed = 0\n readStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n const pipeline = chain([readStream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n const messageBatch: ParsedMessage[] = []\n\n try {\n for await (const item of pipeline as AsyncIterable<{ value: GoogleChatMessage }>) {\n const message = item.value\n const sender = normalizeIdentity(message.creator)\n messageBatch.push({\n platformMessageId: message.message_id ? String(message.message_id) : undefined,\n senderPlatformId: sender.platformId,\n senderAccountName: sender.name,\n timestamp: parseGoogleChatDate(message.created_date ?? message.updated_date ?? '') ?? Number.NaN,\n type: detectMessageType(message),\n content: buildMessageContent(message),\n })\n messagesProcessed++\n\n if (messageBatch.length >= batchSize) {\n yield { type: 'messages', data: messageBatch.splice(0) }\n const progress = createProgress('parsing', bytesRead, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: progress }\n onProgress?.(progress)\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n onLog?.('error', `Failed to parse Google Chat messages: ${message}`)\n yield { type: 'error', data: new Error(`Failed to parse Google Chat messages: ${message}`) }\n return\n }\n\n if (messageBatch.length > 0) {\n yield { type: 'messages', data: messageBatch }\n }\n\n const doneProgress = createProgress('done', totalBytes, totalBytes, messagesProcessed, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n yield { type: 'done', data: { messageCount: messagesProcessed, memberCount: memberMap.size } }\n}\n\nexport const parser_: Parser = {\n feature,\n parse: parseGoogleChat,\n}\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","import * as fs from 'fs'\nimport { KNOWN_PLATFORMS } from '@openchatlab/shared-types'\n\nimport { LINE_FILENAME_SIGNATURES, LINE_HEAD_SIGNATURES, parseLineText } from '../browser/line'\nimport { PARSER_FORMAT_IDS } from '../format-ids'\nimport type { FormatFeature, FormatModule, ParseEvent, ParseOptions, Parser } from '../types'\nimport { createProgress, getFileSize } from '../utils'\n\nexport const feature: FormatFeature = {\n id: PARSER_FORMAT_IDS.LINE_NATIVE,\n name: 'LINE 官方导出 TXT',\n platform: KNOWN_PLATFORMS.LINE,\n priority: 35,\n extensions: ['.txt'],\n signatures: {\n head: LINE_HEAD_SIGNATURES,\n filename: LINE_FILENAME_SIGNATURES,\n },\n}\n\nasync function* parseLine(options: ParseOptions): AsyncGenerator<ParseEvent, void, unknown> {\n const { filePath, batchSize = 5000, onProgress, onLog } = options\n const totalBytes = getFileSize(filePath)\n const initialProgress = createProgress('parsing', 0, totalBytes, 0, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n onLog?.('info', `Starting LINE TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`)\n\n const result = await parseLineText(fs.readFileSync(filePath, 'utf-8'), filePath, {\n onProgress: (progress) =>\n onProgress?.(\n createProgress(\n 'parsing',\n Math.round(progress.progress * totalBytes),\n totalBytes,\n progress.messagesProcessed,\n `Processed ${progress.messagesProcessed} messages`\n )\n ),\n })\n\n yield { type: 'meta', data: result.meta }\n yield { type: 'members', data: result.members }\n for (let index = 0; index < result.messages.length; index += batchSize) {\n yield { type: 'messages', data: result.messages.slice(index, index + batchSize) }\n }\n\n const doneProgress = createProgress('done', totalBytes, totalBytes, result.messages.length, '')\n yield { type: 'progress', data: doneProgress }\n onProgress?.(doneProgress)\n onLog?.('info', `LINE TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members`)\n yield {\n type: 'done',\n data: { messageCount: result.messages.length, memberCount: result.members.length },\n }\n}\n\nexport const parser_: Parser = {\n feature,\n parse: parseLine,\n}\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","import { ChatType, KNOWN_PLATFORMS, MessageType } from '@openchatlab/shared-types'\nimport type { ParsedMember, ParsedMessage } from '@openchatlab/shared-types'\n\nimport type { ParsedMeta } from '../types'\n\nexport const LINE_HEAD_SIGNATURES = [\n /^\\[LINE\\] /m,\n /^(?:\\[LINE\\] )?Chat history (?:with|in) /m,\n /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?)\\t[^\\t\\n]+\\t/m,\n /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?) [^\\s]+ /m,\n /^\\d{4}\\.\\d{2}\\.\\d{2}\\s+(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)/m,\n /^\\d{4}\\/\\d{1,2}\\/\\d{1,2}[((][月火水木金土日]/m,\n /^\\d{4}\\/\\d{1,2}\\/\\d{1,2}周/m,\n]\n\nexport const LINE_FILENAME_SIGNATURES = [/\\[LINE\\]/i]\n\nexport interface LineTextParseOptions {\n checkCancelled?: () => void\n onProgress?: (progress: { progress: number; messagesProcessed: number }) => void\n yieldEvery?: number\n now?: () => Date\n}\n\nexport interface LineTextParseResult {\n meta: ParsedMeta\n members: ParsedMember[]\n messages: ParsedMessage[]\n}\n\ninterface LineHeader {\n name: string\n isGroup: boolean\n}\n\nconst DATE_PATTERNS = [\n /^(\\d{4})\\.(\\d{2})\\.(\\d{2})\\s+(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)?/,\n /^(\\d{4})\\/(\\d{1,2})\\/(\\d{1,2})/,\n /^[A-Za-z]+,\\s*(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/,\n]\n\nconst TAB_MESSAGE_PATTERN = /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?)\\t([^\\t]+)\\t(.*)$/\nconst SPACE_MESSAGE_PATTERN = /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?) ([^\\s]+) (.*)$/\nconst SYSTEM_MESSAGE_PATTERN = /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?)\\t\\t(.+)$/\n\nconst SPECIAL_MESSAGE_TYPES: Record<string, MessageType> = {\n '[Photo]': MessageType.IMAGE,\n '[照片]': MessageType.IMAGE,\n '[写真]': MessageType.IMAGE,\n Photos: MessageType.IMAGE,\n '[Voice message]': MessageType.VOICE,\n '[语音信息]': MessageType.VOICE,\n '[語音訊息]': MessageType.VOICE,\n '[ボイスメッセージ]': MessageType.VOICE,\n Audio: MessageType.VOICE,\n '[Video]': MessageType.VIDEO,\n '[视频]': MessageType.VIDEO,\n '[影片]': MessageType.VIDEO,\n '[動画]': MessageType.VIDEO,\n Videos: MessageType.VIDEO,\n '[File]': MessageType.FILE,\n '[文件]': MessageType.FILE,\n '[檔案]': MessageType.FILE,\n '[ファイル]': MessageType.FILE,\n '[Sticker]': MessageType.EMOJI,\n '[贴图]': MessageType.EMOJI,\n '[貼圖]': MessageType.EMOJI,\n '[スタンプ]': MessageType.EMOJI,\n Stickers: MessageType.EMOJI,\n '[Location]': MessageType.LOCATION,\n '[位置]': MessageType.LOCATION,\n '[位置情報]': MessageType.LOCATION,\n '[Notes]': MessageType.TEXT,\n '[记事本]': MessageType.TEXT,\n '[記事本]': MessageType.TEXT,\n '[ノート]': MessageType.TEXT,\n}\nconst SPECIAL_MESSAGE_TYPE_ENTRIES = Object.entries(SPECIAL_MESSAGE_TYPES)\n\nexport function detectLineText(head: string, fileName: string): boolean {\n const basename = getBasename(fileName)\n if (!basename.toLowerCase().endsWith('.txt')) return false\n return (\n LINE_HEAD_SIGNATURES.some((signature) => signature.test(head)) ||\n LINE_FILENAME_SIGNATURES.some((signature) => signature.test(basename))\n )\n}\n\nexport async function parseLineText(\n content: string,\n fileName: string,\n options: LineTextParseOptions = {}\n): Promise<LineTextParseResult> {\n const lines = content.split('\\n')\n const memberMap = new Map<string, ParsedMember>()\n const messages: ParsedMessage[] = []\n const header = lines.length > 0 ? extractNameFromHeader(lines[0].replace(/\\r$/, '').trim()) : null\n const chatName = header?.name ?? extractNameFromFileName(fileName)\n let currentDate: Date | null = null\n let lastMessage: ParsedMessage | null = null\n let quotedMultiline = false\n let lineIndex = header ? 3 : 0\n let useTabSeparator = Boolean(header)\n const yieldEvery = Math.max(1, options.yieldEvery ?? 5000)\n\n if (!header) {\n for (const rawLine of lines) {\n const line = rawLine.replace(/\\r$/, '')\n if (TAB_MESSAGE_PATTERN.test(line)) {\n useTabSeparator = true\n break\n }\n if (SPACE_MESSAGE_PATTERN.test(line)) {\n useTabSeparator = false\n break\n }\n }\n }\n\n for (; lineIndex < lines.length; lineIndex += 1) {\n options.checkCancelled?.()\n const line = lines[lineIndex].replace(/\\r$/, '')\n const date = parseDateLine(line)\n if (date) {\n currentDate = date\n } else {\n const messagePattern = useTabSeparator ? TAB_MESSAGE_PATTERN : SPACE_MESSAGE_PATTERN\n const messageMatch = line.match(messagePattern)\n if (messageMatch) {\n const [, timeText, sender, rawContent] = messageMatch\n let messageContent = rawContent.trim()\n quotedMultiline = false\n if (messageContent.startsWith('\"')) {\n messageContent = messageContent.slice(1)\n quotedMultiline = !messageContent.endsWith('\"')\n if (!quotedMultiline) messageContent = messageContent.slice(0, -1)\n }\n\n if (!memberMap.has(sender)) {\n memberMap.set(sender, { platformId: sender, accountName: sender })\n }\n lastMessage = {\n senderPlatformId: sender,\n senderAccountName: sender,\n timestamp: buildTimestamp(currentDate, timeText, options.now),\n type: detectMessageType(messageContent),\n content: messageContent || null,\n }\n messages.push(lastMessage)\n } else {\n const systemMatch = line.match(SYSTEM_MESSAGE_PATTERN)\n if (systemMatch) {\n const [, timeText, rawContent] = systemMatch\n const messageContent = rawContent.trim()\n quotedMultiline = false\n lastMessage = {\n senderPlatformId: 'system',\n senderAccountName: '系統',\n timestamp: buildTimestamp(currentDate, timeText, options.now),\n type: MessageType.SYSTEM,\n content: messageContent || null,\n }\n messages.push(lastMessage)\n } else if (line.trim() && lastMessage) {\n let continuation = line\n if (quotedMultiline && continuation.endsWith('\"')) {\n continuation = continuation.slice(0, -1)\n quotedMultiline = false\n }\n lastMessage.content = lastMessage.content ? `${lastMessage.content}\\n${continuation}` : continuation\n }\n }\n }\n\n if (lineIndex > 0 && lineIndex % yieldEvery === 0) {\n options.onProgress?.({ progress: lineIndex / lines.length, messagesProcessed: messages.length })\n await new Promise<void>((resolve) => setTimeout(resolve, 0))\n options.checkCancelled?.()\n }\n }\n\n options.checkCancelled?.()\n const memberCount = memberMap.size\n const chatType = header\n ? header.isGroup\n ? ChatType.GROUP\n : ChatType.PRIVATE\n : memberCount <= 2\n ? ChatType.PRIVATE\n : ChatType.GROUP\n const result = {\n meta: { name: chatName, platform: KNOWN_PLATFORMS.LINE, type: chatType },\n members: Array.from(memberMap.values()),\n messages,\n }\n options.onProgress?.({ progress: 1, messagesProcessed: messages.length })\n return result\n}\n\nfunction extractNameFromFileName(fileName: string): string {\n const basename = getBasename(fileName).replace(/\\.txt$/, '')\n return basename.replace(/^\\[LINE\\]\\s*/i, '').trim() || '未知聊天'\n}\n\nfunction extractNameFromHeader(header: string): LineHeader | null {\n const englishPrivate = header.match(/^(?:\\[LINE\\] )?Chat history with (.+)$/m)\n if (englishPrivate) return { name: englishPrivate[1].trim(), isGroup: false }\n const englishGroup = header.match(/^(?:\\[LINE\\] )?Chat history in (.+)$/m)\n if (englishGroup) return { name: englishGroup[1].trim(), isGroup: true }\n const japanesePrivate = header.match(/^\\[LINE\\] (.+)とのトーク履歴/)\n if (japanesePrivate) return { name: japanesePrivate[1].trim(), isGroup: false }\n const japaneseGroup = header.match(/^\\[LINE\\] (.+)のトーク履歴/)\n if (japaneseGroup) return { name: japaneseGroup[1].trim(), isGroup: true }\n const simplifiedPrivate = header.match(/^\\[LINE\\] 与(.+)的聊天记录/)\n if (simplifiedPrivate) return { name: simplifiedPrivate[1].trim(), isGroup: false }\n const simplifiedGroup = header.match(/^\\[LINE\\] (.+)的聊天记录/)\n if (simplifiedGroup) return { name: simplifiedGroup[1].trim(), isGroup: true }\n const traditionalPrivate = header.match(/^\\[LINE\\] 與(.+)的聊天記錄/)\n if (traditionalPrivate) return { name: traditionalPrivate[1].trim(), isGroup: false }\n const traditionalGroup = header.match(/^\\[LINE\\] (.+)的聊天記錄/)\n return traditionalGroup ? { name: traditionalGroup[1].trim(), isGroup: true } : null\n}\n\nfunction getBasename(fileName: string): string {\n return fileName.split(/[\\\\/]/).pop() ?? fileName\n}\n\nfunction parseDateLine(line: string): Date | null {\n for (let index = 0; index < DATE_PATTERNS.length; index += 1) {\n const match = line.match(DATE_PATTERNS[index])\n if (!match) continue\n if (index < 2) {\n return new Date(Number.parseInt(match[1], 10), Number.parseInt(match[2], 10) - 1, Number.parseInt(match[3], 10))\n }\n return new Date(Number.parseInt(match[3], 10), Number.parseInt(match[1], 10) - 1, Number.parseInt(match[2], 10))\n }\n return null\n}\n\nfunction buildTimestamp(currentDate: Date | null, timeText: string, now: (() => Date) | undefined): number {\n const { hours, minutes } = parseTime(timeText)\n const value = currentDate ? new Date(currentDate) : (now?.() ?? new Date())\n value.setHours(hours, minutes, 0, 0)\n return Math.floor(value.getTime() / 1000)\n}\n\nfunction parseTime(timeText: string): { hours: number; minutes: number } {\n const prefix = timeText.match(/^(上午|下午|午前|午後)/)?.[1]\n const cleanTime = timeText.replace(/^(上午|下午|午前|午後)/, '')\n const match = cleanTime.match(/^(\\d{1,2}):(\\d{2})([AaPp][Mm])?$/i)\n if (!match) {\n const [hours, minutes] = timeText.split(':').map(Number)\n return { hours, minutes }\n }\n\n let hours = Number.parseInt(match[1], 10)\n const minutes = Number.parseInt(match[2], 10)\n const suffix = match[3]?.toLowerCase()\n if (suffix === 'pm' && hours < 12) hours += 12\n if (suffix === 'am' && hours === 12) hours = 0\n if ((prefix === '下午' || prefix === '午後') && hours < 12) hours += 12\n if ((prefix === '上午' || prefix === '午前') && hours === 12) hours = 0\n return { hours, minutes }\n}\n\nfunction detectMessageType(content: string): MessageType {\n for (const [pattern, type] of SPECIAL_MESSAGE_TYPE_ENTRIES) {\n if (content === pattern || content.startsWith(pattern)) return type\n }\n if (content.startsWith('[null]') && content.includes('maps.google.com')) return MessageType.LOCATION\n if (\n content.includes(' joined the group') ||\n content.includes('已加入该群') ||\n content.includes('已加入群組') ||\n content.includes('がグループに参加しました') ||\n content.includes(' added ') ||\n content.includes(' to the group') ||\n content.includes('已将') ||\n content.includes('添加至群') ||\n content.includes('添加到群') ||\n content.includes('已新增') ||\n content.includes('至群組') ||\n content.includes('をグループに追加しました') ||\n content.includes(' left the group') ||\n content.includes('已退群') ||\n content.includes('已離開群組') ||\n content.includes('がグループを退会しました') ||\n content.includes('made an announcement') ||\n content.includes('发布了通告') ||\n content.includes('已設定公告') ||\n content.includes('がアナウンスしました') ||\n content.includes('unsent a message') ||\n content === 'Message unsent.' ||\n content.includes('撤回了一条消息') ||\n content.includes('已收回訊息') ||\n content.includes('送信を取り消しました') ||\n content.startsWith('Auto-reply')\n ) {\n return MessageType.SYSTEM\n }\n return /^https?:\\/\\//.test(content) ? MessageType.LINK : MessageType.TEXT\n}\n","/**\n * 格式模块注册\n * 导出所有支持的格式\n */\n\nimport type { FormatModule } from '../types'\n\n// 导入所有格式模块\nimport chatlab from './chatlab'\nimport chatlabJsonl from './chatlab-jsonl'\nimport shuakamiQqExporter from './shuakami-qq-exporter'\nimport shuakamiQqExporterChunked from './shuakami-qq-exporter-chunked'\nimport weflow from './weflow'\nimport yccccccyEchotrace from './ycccccccy-echotrace'\nimport tyrrrzDiscordExporter from './tyrrrz-discord-exporter'\nimport telegramNative from './telegram-native'\nimport telegramNativeSingle from './telegram-native-single'\nimport whatsappNativeTxt from './whatsapp-native-txt'\nimport qqNativeTxt from './qq-native-txt'\nimport instagramNative from './instagram-native'\nimport googleChatTakeout from './google-chat-takeout'\nimport lineNativeTxt from './line-native-txt'\n\n/**\n * 所有支持的格式模块(按优先级排序)\n * 注意:注册时会自动按 priority 字段排序\n */\nexport const formats: FormatModule[] = [\n shuakamiQqExporterChunked, // 优先级 5 - shuakami/qq-chat-exporter chunked-jsonl\n shuakamiQqExporter, // 优先级 10 - shuakami/qq-chat-exporter\n weflow, // 优先级 15 - WeFlow 微信导出\n yccccccyEchotrace, // 优先级 16 - ycccccccy/echotrace 微信导出\n tyrrrzDiscordExporter, // 优先级 20 - Tyrrrz/DiscordChatExporter\n telegramNative, // 优先级 22 - Telegram 官方全量导出 JSON\n telegramNativeSingle, // 优先级 23 - Telegram 单聊天导出 JSON\n googleChatTakeout, // 优先级 24 - Google Chat Takeout 内部 manifest\n instagramNative, // 优先级 25 - Instagram 官方导出\n whatsappNativeTxt, // 优先级 26 - WhatsApp 官方导出 TXT\n qqNativeTxt, // 优先级 30 - QQ 官方导出 TXT\n lineNativeTxt, // 优先级 35 - LINE 官方导出 TXT\n chatlab, // 优先级 50 - ChatLab JSON\n chatlabJsonl, // 优先级 51 - ChatLab JSONL(流式格式,支持超大文件)\n]\n\n// 按名称导出,方便单独使用\nexport {\n chatlab,\n chatlabJsonl,\n shuakamiQqExporter,\n shuakamiQqExporterChunked,\n weflow,\n yccccccyEchotrace,\n tyrrrzDiscordExporter,\n telegramNative,\n telegramNativeSingle,\n googleChatTakeout,\n instagramNative,\n whatsappNativeTxt,\n qqNativeTxt,\n lineNativeTxt,\n}\n","import { createReadStream } from 'node:fs'\nimport { readFile, stat } from 'node:fs/promises'\nimport { extname } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport {\n CHATLAB_SUPPORTED_FORMAT_VERSIONS,\n MessageType,\n isSupportedChatLabFormatVersion,\n} from '@openchatlab/shared-types'\n\nconst MAX_REPORTED_ISSUES = 100\nconst MAX_UNIX_SECONDS = 100_000_000_000\nconst KNOWN_MESSAGE_TYPES = new Set<number>(\n Object.values(MessageType).filter((value): value is number => typeof value === 'number')\n)\nconst SYSTEM_SENDER_ID = 'SYSTEM'\nconst SYSTEM_SENDER_MESSAGE_TYPES = new Set<number>([MessageType.SYSTEM, MessageType.RECALL])\n\nexport type ChatLabValidationFormat = 'json' | 'jsonl' | 'unknown'\nexport type ChatLabValidationSeverity = 'error' | 'warning'\n\nexport interface ChatLabValidationIssue {\n severity: ChatLabValidationSeverity\n code: string\n message: string\n path?: string\n line?: number\n}\n\nexport interface ChatLabValidationStats {\n headerCount: number\n memberCount: number\n messageCount: number\n uniqueSenderCount: number\n commentLineCount: number\n blankLineCount: number\n}\n\nexport interface ChatLabValidationReport {\n valid: boolean\n format: ChatLabValidationFormat\n version?: string\n fileSize: number\n errorCount: number\n warningCount: number\n issues: ChatLabValidationIssue[]\n truncatedIssueCount: number\n stats: ChatLabValidationStats\n}\n\ninterface ValidationState {\n format: ChatLabValidationFormat\n fileSize: number\n version?: string\n errorCount: number\n warningCount: number\n issues: ChatLabValidationIssue[]\n stats: ChatLabValidationStats\n memberIds: Set<string>\n ownerIds: Set<string>\n senderIds: Set<string>\n messageIds: Set<string>\n replyTargetIds: Set<string>\n lastTimestamp?: number\n}\n\ninterface IssueLocation {\n path?: string\n line?: number\n}\n\nfunction createState(format: ChatLabValidationFormat, fileSize: number): ValidationState {\n return {\n format,\n fileSize,\n errorCount: 0,\n warningCount: 0,\n issues: [],\n stats: {\n headerCount: 0,\n memberCount: 0,\n messageCount: 0,\n uniqueSenderCount: 0,\n commentLineCount: 0,\n blankLineCount: 0,\n },\n memberIds: new Set(),\n ownerIds: new Set(),\n senderIds: new Set(),\n messageIds: new Set(),\n replyTargetIds: new Set(),\n }\n}\n\nfunction addIssue(\n state: ValidationState,\n severity: ChatLabValidationSeverity,\n code: string,\n message: string,\n location: IssueLocation = {}\n): void {\n if (severity === 'error') state.errorCount++\n else state.warningCount++\n\n if (state.issues.length < MAX_REPORTED_ISSUES) {\n state.issues.push({ severity, code, message, ...location })\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction hasOwn(value: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key)\n}\n\nfunction childLocation(location: IssueLocation, path: string): IssueLocation {\n return { ...location, path }\n}\n\nfunction validateRequiredString(\n state: ValidationState,\n value: unknown,\n path: string,\n location: IssueLocation\n): value is string {\n if (typeof value === 'string' && value.trim().length > 0) return true\n addIssue(state, 'error', 'INVALID_REQUIRED_STRING', 'Expected a non-empty string.', childLocation(location, path))\n return false\n}\n\nfunction validateOptionalString(\n state: ValidationState,\n object: Record<string, unknown>,\n key: string,\n path: string,\n location: IssueLocation\n): void {\n if (!hasOwn(object, key)) return\n if (typeof object[key] !== 'string') {\n addIssue(state, 'error', 'INVALID_OPTIONAL_STRING', 'Expected a string.', childLocation(location, path))\n }\n}\n\nfunction validateUnixSeconds(\n state: ValidationState,\n value: unknown,\n path: string,\n location: IssueLocation\n): value is number {\n if (typeof value === 'number' && Number.isInteger(value) && value >= 0 && value < MAX_UNIX_SECONDS) {\n return true\n }\n addIssue(\n state,\n 'error',\n 'INVALID_UNIX_SECONDS',\n 'Expected a non-negative integer Unix timestamp in seconds, not milliseconds.',\n childLocation(location, path)\n )\n return false\n}\n\nfunction validateHeader(\n state: ValidationState,\n chatlab: unknown,\n meta: unknown,\n location: IssueLocation,\n basePath = ''\n): void {\n state.stats.headerCount++\n\n if (!isRecord(chatlab)) {\n addIssue(state, 'error', 'INVALID_CHATLAB_HEADER', 'Expected a chatlab object.', {\n ...location,\n path: `${basePath}chatlab`,\n })\n } else {\n const versionPath = `${basePath}chatlab.version`\n if (typeof chatlab.version !== 'string') {\n addIssue(state, 'error', 'INVALID_VERSION', 'Expected a string format version.', {\n ...location,\n path: versionPath,\n })\n } else {\n state.version = chatlab.version\n if (!isSupportedChatLabFormatVersion(chatlab.version)) {\n addIssue(\n state,\n 'error',\n 'UNSUPPORTED_VERSION',\n `Supported ChatLab format versions: ${CHATLAB_SUPPORTED_FORMAT_VERSIONS.join(', ')}.`,\n {\n ...location,\n path: versionPath,\n }\n )\n }\n }\n validateUnixSeconds(state, chatlab.exportedAt, `${basePath}chatlab.exportedAt`, location)\n validateOptionalString(state, chatlab, 'generator', `${basePath}chatlab.generator`, location)\n validateOptionalString(state, chatlab, 'description', `${basePath}chatlab.description`, location)\n }\n\n if (!isRecord(meta)) {\n addIssue(state, 'error', 'INVALID_META', 'Expected a meta object.', {\n ...location,\n path: `${basePath}meta`,\n })\n return\n }\n\n validateRequiredString(state, meta.name, `${basePath}meta.name`, location)\n validateRequiredString(state, meta.platform, `${basePath}meta.platform`, location)\n if (meta.type !== 'group' && meta.type !== 'private') {\n addIssue(state, 'error', 'INVALID_CHAT_TYPE', 'Expected chat type \"group\" or \"private\".', {\n ...location,\n path: `${basePath}meta.type`,\n })\n }\n validateOptionalString(state, meta, 'groupId', `${basePath}meta.groupId`, location)\n validateOptionalString(state, meta, 'groupAvatar', `${basePath}meta.groupAvatar`, location)\n if (hasOwn(meta, 'ownerId')) {\n if (validateRequiredString(state, meta.ownerId, `${basePath}meta.ownerId`, location)) {\n state.ownerIds.add(meta.ownerId)\n }\n }\n validateOptionalString(state, meta, 'sourceSessionId', `${basePath}meta.sourceSessionId`, location)\n}\n\nfunction validateMember(state: ValidationState, value: unknown, path: string, location: IssueLocation): void {\n state.stats.memberCount++\n if (!isRecord(value)) {\n addIssue(state, 'error', 'INVALID_MEMBER', 'Expected a member object.', childLocation(location, path))\n return\n }\n\n if (validateRequiredString(state, value.platformId, `${path}.platformId`, location)) {\n if (state.memberIds.has(value.platformId)) {\n addIssue(state, 'error', 'DUPLICATE_MEMBER_ID', 'Member platformId must be unique.', {\n ...location,\n path: `${path}.platformId`,\n })\n } else {\n state.memberIds.add(value.platformId)\n }\n }\n validateRequiredString(state, value.accountName, `${path}.accountName`, location)\n validateOptionalString(state, value, 'groupNickname', `${path}.groupNickname`, location)\n validateOptionalString(state, value, 'avatar', `${path}.avatar`, location)\n\n if (hasOwn(value, 'aliases')) {\n if (!Array.isArray(value.aliases) || value.aliases.some((alias) => typeof alias !== 'string')) {\n addIssue(state, 'error', 'INVALID_ALIASES', 'Expected aliases to be an array of strings.', {\n ...location,\n path: `${path}.aliases`,\n })\n }\n }\n\n if (hasOwn(value, 'roles')) {\n if (!Array.isArray(value.roles)) {\n addIssue(state, 'error', 'INVALID_ROLES', 'Expected roles to be an array.', {\n ...location,\n path: `${path}.roles`,\n })\n } else {\n value.roles.forEach((role, index) => {\n const rolePath = `${path}.roles[${index}]`\n if (!isRecord(role)) {\n addIssue(state, 'error', 'INVALID_ROLE', 'Expected a role object.', {\n ...location,\n path: rolePath,\n })\n return\n }\n validateRequiredString(state, role.id, `${rolePath}.id`, location)\n validateOptionalString(state, role, 'name', `${rolePath}.name`, location)\n })\n }\n }\n}\n\nfunction validateMessage(state: ValidationState, value: unknown, path: string, location: IssueLocation): void {\n state.stats.messageCount++\n if (!isRecord(value)) {\n addIssue(state, 'error', 'INVALID_MESSAGE', 'Expected a message object.', childLocation(location, path))\n return\n }\n\n const sender = validateRequiredString(state, value.sender, `${path}.sender`, location) ? value.sender : undefined\n if (sender !== undefined) {\n state.senderIds.add(sender)\n }\n validateRequiredString(state, value.accountName, `${path}.accountName`, location)\n\n if (validateUnixSeconds(state, value.timestamp, `${path}.timestamp`, location)) {\n if (state.lastTimestamp !== undefined && value.timestamp < state.lastTimestamp) {\n addIssue(\n state,\n 'warning',\n 'OUT_OF_ORDER_TIMESTAMP',\n 'Messages should be ordered by timestamp ascending.',\n childLocation(location, `${path}.timestamp`)\n )\n }\n state.lastTimestamp = value.timestamp\n }\n\n const messageType =\n typeof value.type === 'number' && Number.isInteger(value.type) && KNOWN_MESSAGE_TYPES.has(value.type)\n ? value.type\n : undefined\n if (messageType === undefined) {\n addIssue(state, 'error', 'INVALID_MESSAGE_TYPE', 'Expected a supported numeric ChatLab message type.', {\n ...location,\n path: `${path}.type`,\n })\n } else if (sender === SYSTEM_SENDER_ID && !SYSTEM_SENDER_MESSAGE_TYPES.has(messageType)) {\n addIssue(\n state,\n 'error',\n 'INVALID_SYSTEM_SENDER_TYPE',\n 'The reserved SYSTEM sender is only valid for system and recall messages.',\n childLocation(location, `${path}.sender`)\n )\n }\n\n if (!hasOwn(value, 'content') || (typeof value.content !== 'string' && value.content !== null)) {\n addIssue(state, 'error', 'INVALID_MESSAGE_CONTENT', 'Expected content to be a string or null.', {\n ...location,\n path: `${path}.content`,\n })\n }\n validateOptionalString(state, value, 'groupNickname', `${path}.groupNickname`, location)\n\n if (hasOwn(value, 'platformMessageId')) {\n if (validateRequiredString(state, value.platformMessageId, `${path}.platformMessageId`, location)) {\n if (state.messageIds.has(value.platformMessageId)) {\n addIssue(state, 'error', 'DUPLICATE_MESSAGE_ID', 'platformMessageId must be unique.', {\n ...location,\n path: `${path}.platformMessageId`,\n })\n } else {\n state.messageIds.add(value.platformMessageId)\n }\n }\n }\n\n if (hasOwn(value, 'replyToMessageId')) {\n if (validateRequiredString(state, value.replyToMessageId, `${path}.replyToMessageId`, location)) {\n state.replyTargetIds.add(value.replyToMessageId)\n }\n }\n}\n\nfunction finalizeState(\n state: ValidationState,\n requireExplicitMembers: boolean,\n enforceRequiredSections = true\n): ChatLabValidationReport {\n if (enforceRequiredSections) {\n if (state.stats.headerCount === 0) {\n addIssue(state, 'error', 'MISSING_HEADER', 'A ChatLab header is required.')\n }\n if (state.stats.messageCount === 0) {\n addIssue(state, 'error', 'MISSING_MESSAGES', 'At least one message is required.')\n }\n if (requireExplicitMembers && state.stats.memberCount === 0) {\n addIssue(state, 'error', 'MISSING_MEMBERS', 'The JSON format requires a members array with sender identities.')\n }\n }\n\n const ownerMemberIds =\n state.memberIds.size > 0 ? state.memberIds : requireExplicitMembers ? undefined : state.senderIds\n if (ownerMemberIds) {\n for (const ownerId of state.ownerIds) {\n if (!ownerMemberIds.has(ownerId)) {\n addIssue(state, 'error', 'UNKNOWN_OWNER', 'meta.ownerId must reference a member platformId.')\n }\n }\n }\n\n if (state.memberIds.size > 0) {\n for (const senderId of state.senderIds) {\n if (senderId !== SYSTEM_SENDER_ID && !state.memberIds.has(senderId)) {\n addIssue(\n state,\n 'error',\n 'UNKNOWN_MESSAGE_SENDER',\n 'Every message sender must reference an explicit member platformId.'\n )\n }\n }\n }\n\n for (const replyTargetId of state.replyTargetIds) {\n if (!state.messageIds.has(replyTargetId)) {\n addIssue(\n state,\n 'warning',\n 'UNKNOWN_REPLY_TARGET',\n 'A replyToMessageId does not match any platformMessageId in this file.'\n )\n }\n }\n\n state.stats.uniqueSenderCount = state.senderIds.size\n const totalIssueCount = state.errorCount + state.warningCount\n return {\n valid: state.errorCount === 0,\n format: state.format,\n version: state.version,\n fileSize: state.fileSize,\n errorCount: state.errorCount,\n warningCount: state.warningCount,\n issues: state.issues,\n truncatedIssueCount: Math.max(0, totalIssueCount - state.issues.length),\n stats: state.stats,\n }\n}\n\nasync function validateJson(filePath: string, fileSize: number): Promise<ChatLabValidationReport> {\n const state = createState('json', fileSize)\n let root: unknown\n\n try {\n root = JSON.parse(await readFile(filePath, 'utf8')) as unknown\n } catch {\n addIssue(state, 'error', 'INVALID_JSON', 'The file is not valid JSON.')\n return finalizeState(state, true, false)\n }\n\n if (!isRecord(root)) {\n addIssue(state, 'error', 'INVALID_ROOT', 'Expected a top-level JSON object.')\n return finalizeState(state, true, false)\n }\n\n validateHeader(state, root.chatlab, root.meta, {})\n\n if (!Array.isArray(root.members)) {\n addIssue(state, 'error', 'INVALID_MEMBERS', 'Expected members to be an array.', { path: 'members' })\n } else {\n root.members.forEach((member, index) => validateMember(state, member, `members[${index}]`, {}))\n }\n\n if (!Array.isArray(root.messages)) {\n addIssue(state, 'error', 'INVALID_MESSAGES', 'Expected messages to be an array.', { path: 'messages' })\n } else {\n root.messages.forEach((message, index) => validateMessage(state, message, `messages[${index}]`, {}))\n }\n\n return finalizeState(state, true)\n}\n\nasync function validateJsonl(filePath: string, fileSize: number): Promise<ChatLabValidationReport> {\n const state = createState('jsonl', fileSize)\n const input = createReadStream(filePath, { encoding: 'utf8' })\n const lines = createInterface({ input, crlfDelay: Infinity })\n let lineNumber = 0\n let dataLineCount = 0\n let sawMessage = false\n\n for await (const line of lines) {\n lineNumber++\n const trimmed = line.trim()\n if (!trimmed) {\n state.stats.blankLineCount++\n continue\n }\n if (trimmed.startsWith('#')) {\n state.stats.commentLineCount++\n continue\n }\n\n dataLineCount++\n let value: unknown\n try {\n value = JSON.parse(trimmed) as unknown\n } catch {\n addIssue(state, 'error', 'INVALID_JSONL_LINE', 'The line is not valid JSON.', { line: lineNumber })\n continue\n }\n\n if (!isRecord(value)) {\n addIssue(state, 'error', 'INVALID_JSONL_VALUE', 'Expected a JSON object on every data line.', {\n line: lineNumber,\n })\n continue\n }\n\n if (dataLineCount === 1 && value._type !== 'header') {\n addIssue(state, 'error', 'HEADER_NOT_FIRST', 'The first data line must be a header.', {\n line: lineNumber,\n path: '_type',\n })\n }\n\n switch (value._type) {\n case 'header':\n if (state.stats.headerCount > 0) {\n addIssue(state, 'error', 'DUPLICATE_HEADER', 'Only one header line is allowed.', {\n line: lineNumber,\n path: '_type',\n })\n }\n validateHeader(state, value.chatlab, value.meta, { line: lineNumber })\n break\n case 'member':\n if (sawMessage) {\n addIssue(state, 'error', 'MEMBER_AFTER_MESSAGE', 'Member lines must appear before message lines.', {\n line: lineNumber,\n })\n }\n validateMember(state, value, `line[${lineNumber}]`, { line: lineNumber })\n break\n case 'message':\n sawMessage = true\n validateMessage(state, value, `line[${lineNumber}]`, { line: lineNumber })\n break\n default:\n addIssue(state, 'error', 'UNKNOWN_LINE_TYPE', 'Expected _type to be header, member, or message.', {\n line: lineNumber,\n path: '_type',\n })\n }\n }\n\n return finalizeState(state, false)\n}\n\nexport async function validateChatLabFile(filePath: string): Promise<ChatLabValidationReport> {\n const fileStat = await stat(filePath)\n const extension = extname(filePath).toLowerCase()\n\n if (extension === '.json') return validateJson(filePath, fileStat.size)\n if (extension === '.jsonl') return validateJsonl(filePath, fileStat.size)\n\n const state = createState('unknown', fileStat.size)\n addIssue(state, 'error', 'UNSUPPORTED_EXTENSION', 'Expected a .json or .jsonl file.')\n return finalizeState(state, false, false)\n}\n","const SECONDS_PER_MINUTE = 60\nconst DEFAULT_DEMO_TIME_ZONE_OFFSET_MINUTES = 8 * 60\nconst MAX_TIME_ZONE_OFFSET_MINUTES = 14 * 60\n\ninterface ChatLabDemoTimeline {\n timeZoneOffsetMinutes?: unknown\n}\n\ninterface ChatLabDemoHeader extends Record<string, unknown> {\n exportedAt: number\n demoTimeline?: ChatLabDemoTimeline\n}\n\ninterface ChatLabDemoMessage extends Record<string, unknown> {\n timestamp: number\n}\n\ninterface ChatLabDemoDocument extends Record<string, unknown> {\n chatlab: ChatLabDemoHeader\n messages: ChatLabDemoMessage[]\n}\n\nexport interface RebasedChatLabDemoDocuments {\n documents: string[]\n offsetSeconds: number\n latestTimestamp: number\n}\n\n/**\n * Rebase official Demo documents as one timeline so their latest message falls\n * on yesterday in the importing user's local calendar.\n *\n * The same offset is applied to every document. This preserves message\n * intervals and keeps the group/private story lines aligned.\n */\nexport function rebaseChatLabDemoDocuments(\n jsonDocuments: readonly string[],\n now: Date = new Date(),\n targetTimeZone?: string\n): RebasedChatLabDemoDocuments {\n if (jsonDocuments.length === 0) throw new Error('Demo timeline requires at least one document')\n if (!Number.isFinite(now.getTime())) throw new Error('Demo timeline requires a valid import time')\n\n const documents = jsonDocuments.map((json, index) => parseDemoDocument(json, index))\n const timeZoneOffsetMinutes = resolveTimeZoneOffsetMinutes(documents)\n const sourceLatestTimestamp = Math.max(\n ...documents.flatMap((document) => document.messages.map((message) => message.timestamp))\n )\n\n // Restore the source wall-clock time before mapping it to yesterday in the user's local calendar.\n const sourceLatestWallTime = new Date((sourceLatestTimestamp + timeZoneOffsetMinutes * SECONDS_PER_MINUTE) * 1000)\n const latestTimestamp = resolveTargetLatestTimestamp(now, sourceLatestWallTime, targetTimeZone)\n const offsetSeconds = latestTimestamp - sourceLatestTimestamp\n const exportedAt = Math.floor(now.getTime() / 1000)\n\n for (const document of documents) {\n document.chatlab.exportedAt = exportedAt\n for (const message of document.messages) {\n message.timestamp += offsetSeconds\n }\n }\n\n return {\n documents: documents.map((document) => JSON.stringify(document)),\n offsetSeconds,\n latestTimestamp,\n }\n}\n\nfunction resolveTargetLatestTimestamp(now: Date, sourceWallTime: Date, targetTimeZone?: string): number {\n if (!targetTimeZone) {\n const targetLatestTime = new Date(now)\n targetLatestTime.setDate(targetLatestTime.getDate() - 1)\n targetLatestTime.setHours(\n sourceWallTime.getUTCHours(),\n sourceWallTime.getUTCMinutes(),\n sourceWallTime.getUTCSeconds(),\n 0\n )\n return Math.floor(targetLatestTime.getTime() / 1000)\n }\n\n let formatter: Intl.DateTimeFormat\n try {\n formatter = new Intl.DateTimeFormat('en-US-u-ca-gregory-nu-latn', {\n timeZone: targetTimeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hourCycle: 'h23',\n })\n } catch {\n throw new Error('Demo timeline has an invalid target time zone')\n }\n\n const currentDate = getZonedDateTimeParts(now, formatter)\n const yesterday = new Date(Date.UTC(currentDate.year, currentDate.month - 1, currentDate.day - 1))\n const targetParts: ZonedDateTimeParts = {\n year: yesterday.getUTCFullYear(),\n month: yesterday.getUTCMonth() + 1,\n day: yesterday.getUTCDate(),\n hour: sourceWallTime.getUTCHours(),\n minute: sourceWallTime.getUTCMinutes(),\n second: sourceWallTime.getUTCSeconds(),\n }\n\n return Math.floor(zonedWallTimeToUnixMillis(targetParts, formatter) / 1000)\n}\n\ninterface ZonedDateTimeParts {\n year: number\n month: number\n day: number\n hour: number\n minute: number\n second: number\n}\n\nfunction getZonedDateTimeParts(date: Date, formatter: Intl.DateTimeFormat): ZonedDateTimeParts {\n const values = new Map(formatter.formatToParts(date).map((part) => [part.type, part.value]))\n const read = (type: keyof ZonedDateTimeParts) => Number(values.get(type))\n const parts: ZonedDateTimeParts = {\n year: read('year'),\n month: read('month'),\n day: read('day'),\n hour: read('hour'),\n minute: read('minute'),\n second: read('second'),\n }\n if (Object.values(parts).some((value) => !Number.isInteger(value))) {\n throw new Error('Demo timeline could not resolve the target time zone')\n }\n return parts\n}\n\nfunction zonedWallTimeToUnixMillis(target: ZonedDateTimeParts, formatter: Intl.DateTimeFormat): number {\n const targetAsUtc = dateTimePartsToUtcMillis(target)\n let candidate = targetAsUtc\n\n // Intl maps timestamps to zoned wall-clock time; iterative corrections resolve the inverse mapping.\n for (let attempt = 0; attempt < 3; attempt++) {\n const actual = getZonedDateTimeParts(new Date(candidate), formatter)\n const correction = targetAsUtc - dateTimePartsToUtcMillis(actual)\n candidate += correction\n if (correction === 0) return candidate\n }\n\n const actual = getZonedDateTimeParts(new Date(candidate), formatter)\n if (dateTimePartsToUtcMillis(actual) !== targetAsUtc) {\n throw new Error('Demo timeline target wall time does not exist in the selected time zone')\n }\n return candidate\n}\n\nfunction dateTimePartsToUtcMillis(parts: ZonedDateTimeParts): number {\n return Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second)\n}\n\nfunction parseDemoDocument(json: string, index: number): ChatLabDemoDocument {\n let value: unknown\n try {\n value = JSON.parse(json)\n } catch {\n throw new Error(`Demo document ${index + 1} is not valid JSON`)\n }\n\n if (!isRecord(value) || !isRecord(value.chatlab) || !Array.isArray(value.messages)) {\n throw new Error(`Demo document ${index + 1} is not a ChatLab JSON document`)\n }\n if (value.messages.length === 0) {\n throw new Error(`Demo document ${index + 1} has no messages`)\n }\n\n const messages = value.messages.map((message, messageIndex) => {\n if (!isRecord(message) || !isUnixSeconds(message.timestamp)) {\n throw new Error(`Demo document ${index + 1} has an invalid timestamp at message ${messageIndex + 1}`)\n }\n return message as ChatLabDemoMessage\n })\n\n return {\n ...value,\n chatlab: value.chatlab as ChatLabDemoHeader,\n messages,\n }\n}\n\nfunction resolveTimeZoneOffsetMinutes(documents: readonly ChatLabDemoDocument[]): number {\n const offsets = new Set<number>()\n\n for (const document of documents) {\n const configured = document.chatlab.demoTimeline?.timeZoneOffsetMinutes\n const offset = configured ?? DEFAULT_DEMO_TIME_ZONE_OFFSET_MINUTES\n if (typeof offset !== 'number' || !Number.isInteger(offset) || Math.abs(offset) > MAX_TIME_ZONE_OFFSET_MINUTES) {\n throw new Error('Demo timeline has an invalid time zone offset')\n }\n offsets.add(offset)\n }\n\n if (offsets.size !== 1) {\n throw new Error('Demo documents must use the same time zone offset')\n }\n return offsets.values().next().value as number\n}\n\nfunction isUnixSeconds(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","/**\n * Startup observability for the optional Rust parsing kernels.\n *\n * Desktop main process and CLI entry points call this once at startup so\n * `logs/app.log` always records whether imports will use the native kernels\n * or the pure-TS fallback (and why). No user data is ever logged here.\n */\n\nimport { getNativeParserStatus } from '@openchatlab/parser'\nimport { appLogger } from '../logging/app-logger'\n\nlet logged = false\n\n/** Log once per process whether the Rust native parser kernels can load. */\nexport function logNativeParserStatus(): void {\n if (logged) return\n logged = true\n\n const status = getNativeParserStatus()\n if (status.available) {\n appLogger.info('import', 'Native parser kernels available (Rust module loaded)')\n } else if (status.disabled) {\n appLogger.info('import', 'Native parser kernels disabled via CHATLAB_DISABLE_NATIVE_PERF=1; using TS parsers')\n } else {\n appLogger.warn('import', 'Native parser kernels unavailable; imports fall back to TS parsers', {\n reason: status.error ?? 'unknown',\n })\n }\n}\n","/**\n * Import performance logger.\n *\n * Each import must own an independent logger instance because batch imports can\n * run concurrently in the same process.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\n\nexport enum LogLevel {\n ERROR = 'ERROR',\n INFO = 'INFO',\n}\n\nexport interface ImportPerfLogger {\n info(message: string): void\n error(message: string, error?: Error): void\n perf(event: string, messagesProcessed: number, batchSize?: number): void\n perfDetail(detail: string): void\n summary(totalMessages: number, totalMembers: number): void\n reset(): void\n init(sessionId: string): void\n getCurrentLogFile(): string | null\n getErrorCount(): number\n}\n\nexport function createImportPerfLogger(logDir: string): ImportPerfLogger {\n let lastLogTime = Date.now()\n let lastMessageCount = 0\n let currentLogFile: string | null = null\n let errorCount = 0\n\n const append = (line: string) => {\n if (!currentLogFile) return\n try {\n fs.appendFileSync(currentLogFile, line, 'utf-8')\n } catch {\n // Ignore write failure.\n }\n }\n\n const writeLogLine = (level: LogLevel, message: string) => {\n append(`[${new Date().toISOString()}] [${level}] ${message}\\n`)\n }\n\n return {\n init(sessionId) {\n try {\n if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true })\n currentLogFile = path.join(logDir, `import_${sessionId}_${Date.now()}.log`)\n fs.writeFileSync(currentLogFile, `=== Import Log ===\\nStart time: ${new Date().toISOString()}\\n\\n`, 'utf-8')\n } catch {\n currentLogFile = null\n }\n },\n perf(event, messagesProcessed, batchSize) {\n const now = Date.now()\n const duration = now - lastLogTime\n const messagesDelta = messagesProcessed - lastMessageCount\n const speed = duration > 0 ? Math.round((messagesDelta / duration) * 1000) : 0\n let memory = 0\n try {\n memory = Math.round(process.memoryUsage().heapUsed / 1024 / 1024)\n } catch {\n // Ignore memory sampling failure.\n }\n append(\n `[${new Date().toISOString()}] ${event} | ` +\n `messages: ${messagesProcessed.toLocaleString()} | ` +\n `elapsed: ${duration}ms | ` +\n `speed: ${speed.toLocaleString()}/s | ` +\n `memory: ${memory}MB` +\n (batchSize ? ` | batch: ${batchSize}` : '') +\n '\\n'\n )\n lastLogTime = now\n lastMessageCount = messagesProcessed\n },\n perfDetail(detail) {\n append(` ${detail}\\n`)\n },\n reset() {\n lastLogTime = Date.now()\n lastMessageCount = 0\n currentLogFile = null\n errorCount = 0\n },\n getCurrentLogFile() {\n return currentLogFile\n },\n error(message, error) {\n errorCount++\n writeLogLine(LogLevel.ERROR, `${message}${error ? `: ${error.message}` : ''}`)\n },\n info(message) {\n writeLogLine(LogLevel.INFO, message)\n },\n getErrorCount() {\n return errorCount\n },\n summary(totalMessages, totalMembers) {\n append(`\n=== Import Summary ===\nEnd time: ${new Date().toISOString()}\nTotal messages: ${totalMessages.toLocaleString()}\nTotal members: ${totalMembers.toLocaleString()}\nErrors: ${errorCount}\n`)\n },\n }\n}\n\nlet defaultLogger = createImportPerfLogger('')\n\nexport function initPerfLog(sessionId: string, logDir: string): void {\n defaultLogger = createImportPerfLogger(logDir)\n defaultLogger.init(sessionId)\n}\n\nexport function logPerf(event: string, messagesProcessed: number, batchSize?: number): void {\n defaultLogger.perf(event, messagesProcessed, batchSize)\n}\n\nexport function logPerfDetail(detail: string): void {\n defaultLogger.perfDetail(detail)\n}\n\nexport function resetPerfLog(): void {\n defaultLogger.reset()\n}\n\nexport function getCurrentLogFile(): string | null {\n return defaultLogger.getCurrentLogFile()\n}\n\nexport function logError(message: string, error?: Error): void {\n defaultLogger.error(message, error)\n}\n\nexport function logInfo(message: string): void {\n defaultLogger.info(message)\n}\n\nexport function getErrorCount(): number {\n return defaultLogger.getErrorCount()\n}\n\nexport function logSummary(totalMessages: number, totalMembers: number): void {\n defaultLogger.summary(totalMessages, totalMembers)\n}\n","import type { DatabaseAdapter, PreparedStatement } from '@openchatlab/core'\n\nconst MESSAGE_COLUMN_COUNT = 8\nconst SQLITE_LEGACY_VARIABLE_LIMIT = 999\n\n/**\n * Keep each statement below SQLite's historical 999-variable limit.\n * Modern bundled SQLite builds allow more, but this conservative bound keeps\n * the shared DatabaseAdapter path compatible with older/runtime-specific builds.\n */\nexport const MESSAGE_INSERT_MAX_ROWS = Math.floor(SQLITE_LEGACY_VARIABLE_LIMIT / MESSAGE_COLUMN_COUNT)\n\nexport interface MessageInsertRow {\n senderId: number\n senderAccountName: string | null\n senderGroupNickname: string | null\n timestamp: number\n type: number\n content: string | null\n replyToMessageId: string | null\n platformMessageId: string | null\n}\n\nexport class MessageBatchInserter {\n private readonly statementCache = new Map<number, PreparedStatement>()\n\n constructor(private readonly db: DatabaseAdapter) {}\n\n insert(rows: readonly MessageInsertRow[]): number {\n let statementCount = 0\n for (let offset = 0; offset < rows.length; offset += MESSAGE_INSERT_MAX_ROWS) {\n const batch = rows.slice(offset, offset + MESSAGE_INSERT_MAX_ROWS)\n const statement = this.getStatement(batch.length)\n statement.run(...batch.flatMap(toParams))\n statementCount++\n }\n return statementCount\n }\n\n private getStatement(rowCount: number): PreparedStatement {\n const cached = this.statementCache.get(rowCount)\n if (cached) return cached\n\n const values = Array.from({ length: rowCount }, () => '(?, ?, ?, ?, ?, ?, ?, ?)').join(', ')\n const statement = this.db.prepare(\n `INSERT INTO message (\n sender_id,\n sender_account_name,\n sender_group_nickname,\n ts,\n type,\n content,\n reply_to_message_id,\n platform_message_id\n ) VALUES ${values}`\n )\n this.statementCache.set(rowCount, statement)\n return statement\n }\n}\n\nfunction toParams(row: MessageInsertRow): unknown[] {\n return [\n row.senderId,\n row.senderAccountName,\n row.senderGroupNickname,\n row.timestamp,\n row.type,\n row.content,\n row.replyToMessageId,\n row.platformMessageId,\n ]\n}\n","/**\n * Platform-agnostic streaming importer.\n *\n * Extracted from electron/main/worker/import/streamImport.ts.\n * Streams parsed data directly into SQLite with batched transactions,\n * deferred index creation and nickname history tracking.\n *\n * Both Electron and Server/CLI use this module via dependency injection:\n * the caller provides a DatabaseAdapter, progress callback, and optional hooks.\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport {\n CHAT_DB_INDEXES,\n generateSessionIndex as generateCoreSessionIndex,\n normalizeSessionGapThreshold,\n} from '@openchatlab/core'\nimport type { ParsedMember, ParsedMessage } from '@openchatlab/shared-types'\nimport {\n streamParseFile,\n detectFormat,\n detectAllFormats,\n getFormatFeatureById,\n getPreprocessor,\n needsPreprocess,\n isNativeFormatAvailable,\n type ParsedMeta,\n type FormatFeature,\n type ParseProgress,\n} from '@openchatlab/parser'\nimport * as fs from 'fs'\nimport { performance } from 'node:perf_hooks'\nimport { MessageBatchInserter, type MessageInsertRow } from './message-batch-inserter'\nimport { createMessageDedupState, registerMessageAndCheckDuplicate, type DedupMessage } from './message-deduplicator'\n\n// ==================== Public interfaces ====================\n\nexport interface SkipReasons {\n noSenderId: number\n noAccountName: number\n invalidTimestamp: number\n noType: number\n}\n\nexport interface ImportDiagnostics {\n logFile: string | null\n detectedFormat: string | null\n messagesReceived: number\n messagesWritten: number\n duplicateCount: number\n messagesSkipped: number\n skipReasons: SkipReasons\n performance: ImportPerformanceDiagnostics\n}\n\nexport interface ImportStageTimings {\n detectionMs: number\n preprocessingMs: number\n databaseSetupMs: number\n parserMs: number\n metaWriteMs: number\n memberWriteMs: number\n messageWriteMs: number\n nicknameHistoryMs: number\n indexCreationMs: number\n checkpointMs: number\n sessionIndexMs: number\n postImportHookMs: number\n totalMs: number\n}\n\nexport interface ImportPerformanceDiagnostics {\n timings: ImportStageTimings\n messageBatchCount: number\n messageTransactionCount: number\n messageInsertStatementCount: number\n rssStartMb: number\n /** Highest RSS observed at import stage and parser batch boundaries; not a continuous process peak. */\n rssSampledPeakMb: number\n rssSampledDeltaMb: number\n}\n\nexport interface StreamImportResult {\n success: boolean\n sessionId?: string\n platform?: string\n error?: string\n diagnostics?: ImportDiagnostics\n}\n\nexport type ImportProgressCallback = (progress: ParseProgress) => void\n\nexport interface ImportLogger {\n info(message: string): void\n error(message: string, err?: Error): void\n perf(label: string, messageCount: number, batchSize?: number): void\n perfDetail(detail: string): void\n summary(messageCount: number, memberCount: number): void\n reset(): void\n init(sessionId: string): void\n getCurrentLogFile(): string | null\n}\n\nexport interface StreamImportDeps {\n /** Open a new database for writing (tables only, no indexes). */\n openDatabase(sessionId: string): DatabaseAdapter\n /** Delete a database file (and WAL/SHM) on failure. */\n deleteDatabase(sessionId: string): void\n /** Progress callback (IPC postMessage, SSE event, etc.) */\n onProgress: ImportProgressCallback\n /** Optional perf/diagnostic logger */\n logger?: ImportLogger\n /** Optional hook after import completes (e.g. write overview cache) */\n postImportHook?: (db: DatabaseAdapter, sessionId: string) => void | Promise<void>\n /** Generate a session ID. Defaults to timestamp + random. */\n generateSessionId?: () => string\n /** Session gap threshold used by the single post-import session-index build. */\n sessionGapThreshold?: number\n /** Optional test/platform override for the session-index builder. */\n generateSessionIndex?: typeof generateCoreSessionIndex\n /** Optional preprocessing adapters for runtimes that need to override parser capabilities. */\n preprocessing?: {\n getPreprocessor: typeof getPreprocessor\n needsPreprocess: typeof needsPreprocess\n isNativeFormatAvailable: typeof isNativeFormatAvailable\n }\n /** Monotonic clock used by import performance diagnostics. */\n now?: () => number\n}\n\n// ==================== Core streaming import ====================\n\nconst BATCH_COMMIT_SIZE = 50000\nconst CHECKPOINT_INTERVAL = 200000\nconst SYSTEM_SENDER_ID = 'SYSTEM'\nexport const SYSTEM_MEMBER_NAME = '系统消息'\nconst RESERVED_SYSTEM_SENDER_FORMATS = new Set(['chatlab', 'chatlab-jsonl'])\n\ninterface ImportTimingContext {\n totalStartedAt: number\n detectionMs: number\n}\n\nconst defaultNow = () => performance.now()\n\nfunction elapsedMs(startedAt: number, now: () => number): number {\n return now() - startedAt\n}\n\n/**\n * Let the event loop process pending I/O before a long synchronous step.\n * Without this, progress events written to an SSE socket stay buffered\n * until the blocking index work finishes.\n */\nconst yieldToEventLoop = () => new Promise<void>((resolve) => setImmediate(resolve))\n\nfunction defaultGenerateSessionId(): string {\n const ts = Date.now()\n const rand = Math.random().toString(36).substring(2, 8)\n return `chat_${ts}_${rand}`\n}\n\ntype CreateMessageSkipCounter =\n | 'skippedNoSenderId'\n | 'skippedNoAccountName'\n | 'skippedInvalidTimestamp'\n | 'skippedNoType'\n\ntype PreparedCreateMessage = { message: DedupMessage } | { skipCounter: CreateMessageSkipCounter }\n\nexport function normalizeSystemMemberName(\n formatId: string,\n platformId: string,\n name: string | undefined\n): string | undefined {\n return RESERVED_SYSTEM_SENDER_FORMATS.has(formatId) && platformId === SYSTEM_SENDER_ID ? SYSTEM_MEMBER_NAME : name\n}\n\nfunction prepareMessageForCreate(\n message: ParsedMessage,\n senderAccountName = message.senderAccountName\n): PreparedCreateMessage {\n if (!message.senderPlatformId) return { skipCounter: 'skippedNoSenderId' }\n if (!senderAccountName) return { skipCounter: 'skippedNoAccountName' }\n if (message.timestamp === undefined || message.timestamp === null || isNaN(message.timestamp)) {\n return { skipCounter: 'skippedInvalidTimestamp' }\n }\n if (message.type === undefined || message.type === null) return { skipCounter: 'skippedNoType' }\n\n let content: string | null = null\n if (message.content != null) {\n content = typeof message.content === 'string' ? message.content : JSON.stringify(message.content)\n }\n\n return {\n message: {\n platformMessageId: message.platformMessageId,\n timestamp: message.timestamp,\n senderPlatformId: message.senderPlatformId,\n type: message.type,\n content,\n replyToMessageId: message.replyToMessageId,\n },\n }\n}\n\n/**\n * High-performance streaming import: parse a file and write to DB\n * with batched transactions. Supports format auto-detection with fallback.\n */\nexport async function streamingImport(\n filePath: string,\n deps: StreamImportDeps,\n formatOptions?: Record<string, unknown>,\n externalSessionId?: string\n): Promise<StreamImportResult> {\n const now = deps.now ?? defaultNow\n const totalStartedAt = now()\n const detectionStartedAt = now()\n\n if (formatOptions?.formatId) {\n const formatId = formatOptions.formatId as string\n const feature = getFormatFeatureById(formatId)\n if (!feature) {\n return { success: false, error: 'error.unknown_format_id' }\n }\n return streamImportSingle(filePath, deps, feature, formatOptions, externalSessionId, {\n totalStartedAt,\n detectionMs: elapsedMs(detectionStartedAt, now),\n })\n }\n\n const candidates = detectAllFormats(filePath)\n if (candidates.length === 0) {\n return { success: false, error: 'error.unrecognized_format' }\n }\n\n const timingContext = {\n totalStartedAt,\n detectionMs: elapsedMs(detectionStartedAt, now),\n }\n\n if (candidates.length > 1) {\n return streamImportWithFallback(filePath, deps, candidates, formatOptions, externalSessionId, timingContext)\n }\n\n return streamImportSingle(filePath, deps, candidates[0], formatOptions, externalSessionId, timingContext)\n}\n\nasync function streamImportWithFallback(\n filePath: string,\n deps: StreamImportDeps,\n candidates: FormatFeature[],\n formatOptions?: Record<string, unknown>,\n externalSessionId?: string,\n timingContext?: ImportTimingContext\n): Promise<StreamImportResult> {\n for (let i = 0; i < candidates.length; i++) {\n const candidate = candidates[i]\n deps.logger?.info(`[StreamImport] Trying format ${i + 1}/${candidates.length}: ${candidate.name} (${candidate.id})`)\n\n const result = await streamImportSingle(filePath, deps, candidate, formatOptions, externalSessionId, timingContext)\n\n if (result.success) {\n if (i > 0) {\n deps.logger?.info(\n `[StreamImport] Fallback succeeded: ${candidate.name} (after ${i} failed attempt${i > 1 ? 's' : ''})`\n )\n }\n return result\n }\n\n if (i === candidates.length - 1) return result\n\n deps.logger?.info(`[StreamImport] Format ${candidate.name} produced 0 messages, falling back to next candidate...`)\n }\n\n return { success: false, error: 'error.no_messages' }\n}\n\nasync function streamImportSingle(\n filePath: string,\n deps: StreamImportDeps,\n formatFeature: FormatFeature,\n formatOptions?: Record<string, unknown>,\n externalSessionId?: string,\n timingContext?: ImportTimingContext\n): Promise<StreamImportResult> {\n const { onProgress, logger } = deps\n const genId = deps.generateSessionId ?? defaultGenerateSessionId\n const now = deps.now ?? defaultNow\n const totalStartedAt = timingContext?.totalStartedAt ?? now()\n const timings: ImportStageTimings = {\n detectionMs: timingContext?.detectionMs ?? 0,\n preprocessingMs: 0,\n databaseSetupMs: 0,\n parserMs: 0,\n metaWriteMs: 0,\n memberWriteMs: 0,\n messageWriteMs: 0,\n nicknameHistoryMs: 0,\n indexCreationMs: 0,\n checkpointMs: 0,\n sessionIndexMs: 0,\n postImportHookMs: 0,\n totalMs: 0,\n }\n const rssStartBytes = process.memoryUsage().rss\n let rssPeakBytes = rssStartBytes\n const sampleRss = () => {\n rssPeakBytes = Math.max(rssPeakBytes, process.memoryUsage().rss)\n }\n\n logger?.reset()\n const sessionId = externalSessionId || genId()\n logger?.init(sessionId)\n\n logger?.info(`File path: ${filePath}`)\n logger?.info(`Detected format: ${formatFeature.name} (${formatFeature.id})`)\n logger?.info(`Platform: ${formatFeature.platform}`)\n logger?.perf('Import started', 0)\n\n // Preprocess large files if needed\n let actualFilePath = filePath\n let tempFilePath: string | null = null\n const preprocessing = deps.preprocessing ?? { getPreprocessor, needsPreprocess, isNativeFormatAvailable }\n const getImportPreprocessor = preprocessing.getPreprocessor\n const shouldPreprocess = preprocessing.needsPreprocess\n const nativeFormatAvailable = preprocessing.isNativeFormatAvailable\n const preprocessor = getImportPreprocessor(filePath)\n\n const needsLargeFilePreprocess = preprocessor && shouldPreprocess(filePath)\n const nativeCanParseOriginal = needsLargeFilePreprocess && nativeFormatAvailable(formatFeature.id)\n if (nativeCanParseOriginal) {\n logger?.info(\n `[NativeParser] Kernel ${formatFeature.id} is available; skipping large-file preprocessing and parsing the original export`\n )\n } else if (needsLargeFilePreprocess) {\n logger?.info('File needs preprocessing, simplifying large file...')\n onProgress({\n stage: 'parsing',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: 0,\n percentage: 0,\n message: '',\n })\n\n const preprocessingStartedAt = now()\n try {\n tempFilePath = await preprocessor.preprocess(filePath, (progress: ParseProgress) => {\n onProgress({ ...progress, message: '' })\n })\n actualFilePath = tempFilePath\n logger?.info(`Preprocessing done, temp file: ${tempFilePath}`)\n } catch (err) {\n const errorMsg = `Preprocessing failed: ${err instanceof Error ? err.message : String(err)}`\n logger?.error(errorMsg, err instanceof Error ? err : undefined)\n return { success: false, error: errorMsg }\n } finally {\n timings.preprocessingMs = elapsedMs(preprocessingStartedAt, now)\n sampleRss()\n }\n }\n\n const databaseSetupStartedAt = now()\n const databaseSetup = (() => {\n let db: DatabaseAdapter | undefined\n try {\n db = deps.openDatabase(sessionId)\n return {\n ok: true as const,\n db,\n insertMeta: db.prepare(\n `INSERT INTO meta (name, platform, type, imported_at, group_id, group_avatar, owner_id) VALUES (?, ?, ?, ?, ?, ?, ?)`\n ),\n insertMember: db.prepare(\n `INSERT INTO member (platform_id, account_name, group_nickname, aliases, avatar, roles)\n VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(platform_id) DO UPDATE SET\n account_name = COALESCE(NULLIF(excluded.account_name, ''), account_name),\n group_nickname = COALESCE(NULLIF(excluded.group_nickname, ''), group_nickname),\n aliases = CASE WHEN excluded.aliases != '[]' THEN excluded.aliases ELSE aliases END,\n avatar = COALESCE(NULLIF(excluded.avatar, ''), avatar),\n roles = CASE WHEN excluded.roles != '[]' THEN excluded.roles ELSE roles END`\n ),\n getMemberId: db.prepare(`SELECT id FROM member WHERE platform_id = ?`),\n insertNameHistory: db.prepare(\n `INSERT INTO member_name_history (member_id, name_type, name, start_ts, end_ts) VALUES (?, ?, ?, ?, ?)`\n ),\n updateMemberAccountName: db.prepare(`UPDATE member SET account_name = ? WHERE platform_id = ?`),\n updateMemberGroupNickname: db.prepare(`UPDATE member SET group_nickname = ? WHERE platform_id = ?`),\n }\n } catch (error) {\n try {\n db?.close()\n } catch {\n /* ignore cleanup failure while preserving the setup error */\n }\n return { ok: false as const, error, databaseOpened: db !== undefined }\n }\n })()\n timings.databaseSetupMs = elapsedMs(databaseSetupStartedAt, now)\n sampleRss()\n\n if (!databaseSetup.ok) {\n if (tempFilePath && preprocessor) preprocessor.cleanup(tempFilePath)\n if (databaseSetup.databaseOpened) deps.deleteDatabase(sessionId)\n const error = databaseSetup.error\n logger?.error('Import failed during database setup', error instanceof Error ? error : undefined)\n throw error\n }\n\n const {\n db,\n insertMeta,\n insertMember,\n getMemberId,\n insertNameHistory,\n updateMemberAccountName,\n updateMemberGroupNickname,\n } = databaseSetup\n\n const memberIdMap = new Map<string, number>()\n const accountNameTracker = new Map<\n string,\n { currentName: string; lastSeenTs: number; history: Array<{ name: string; startTs: number }> }\n >()\n const groupNicknameTracker = new Map<\n string,\n { currentName: string; lastSeenTs: number; history: Array<{ name: string; startTs: number }> }\n >()\n\n let metaInserted = false\n let importedPlatform = formatFeature.platform\n let messageCountInBatch = 0\n let totalMessageCount = 0\n let duplicateCount = 0\n let lastCheckpointCount = 0\n let inTransaction = false\n let messageTransactionCount = 0\n let messageTransactionMs = 0\n let messageInsertStatementCount = 0\n const messageBatchInserter = new MessageBatchInserter(db)\n\n const beginTransaction = () => {\n if (!inTransaction) {\n const startedAt = now()\n try {\n db.exec('BEGIN TRANSACTION')\n inTransaction = true\n messageTransactionCount++\n } finally {\n messageTransactionMs += elapsedMs(startedAt, now)\n }\n }\n }\n\n const commitTransaction = () => {\n if (inTransaction) {\n const startedAt = now()\n try {\n db.exec('COMMIT')\n inTransaction = false\n } finally {\n messageTransactionMs += elapsedMs(startedAt, now)\n }\n }\n }\n\n const doCheckpoint = () => {\n try {\n db.pragma('wal_checkpoint(TRUNCATE)')\n } catch {\n /* ignore */\n }\n }\n\n const measureCheckpoint = () => {\n const startedAt = now()\n try {\n doCheckpoint()\n } finally {\n timings.checkpointMs += elapsedMs(startedAt, now)\n }\n }\n\n const commitAndBeginNew = () => {\n if (inTransaction) {\n commitTransaction()\n logger?.perf('Commit transaction', totalMessageCount, BATCH_COMMIT_SIZE)\n\n if (totalMessageCount - lastCheckpointCount >= CHECKPOINT_INTERVAL) {\n measureCheckpoint()\n logger?.perf('WAL checkpoint', totalMessageCount)\n lastCheckpointCount = totalMessageCount\n }\n\n onProgress({\n stage: 'saving',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n }\n beginTransaction()\n }\n\n let shouldDeleteDb = false\n let importError: string | null = null\n\n const callbackStats = {\n onProgressCalls: 0,\n onLogCalls: 0,\n onMetaCalls: 0,\n onMembersCalls: 0,\n onMessageBatchCalls: 0,\n totalMembersReceived: 0,\n totalMessagesReceived: 0,\n skippedNoSenderId: 0,\n skippedNoAccountName: 0,\n skippedInvalidTimestamp: 0,\n skippedNoType: 0,\n }\n const dedupState = createMessageDedupState()\n\n logger?.info('Starting streamParseFile...')\n const parsePipelineStartedAt = now()\n let parserTimingFinished = false\n const finishParserTiming = () => {\n if (parserTimingFinished) return\n const parsePipelineMs = elapsedMs(parsePipelineStartedAt, now)\n const writeCallbackMs =\n timings.metaWriteMs + timings.memberWriteMs + timings.messageWriteMs + messageTransactionMs + timings.checkpointMs\n timings.parserMs = Math.max(0, parsePipelineMs - writeCallbackMs)\n parserTimingFinished = true\n sampleRss()\n }\n\n try {\n beginTransaction()\n await streamParseFile(\n actualFilePath,\n {\n batchSize: 5000,\n formatOptions,\n\n onProgress: (progress: ParseProgress) => {\n callbackStats.onProgressCalls++\n onProgress(progress)\n },\n\n onLog: (level: string, message: string) => {\n callbackStats.onLogCalls++\n if (level === 'error') {\n logger?.error(message)\n } else {\n logger?.info(message)\n }\n },\n\n onMeta: (meta: ParsedMeta) => {\n const startedAt = now()\n try {\n callbackStats.onMetaCalls++\n importedPlatform = meta.platform || importedPlatform\n if (!metaInserted) {\n logger?.info(`Writing meta: name=${meta.name}, type=${meta.type}, platform=${meta.platform}`)\n insertMeta.run(\n meta.name,\n meta.platform,\n meta.type,\n Math.floor(Date.now() / 1000),\n meta.groupId || null,\n meta.groupAvatar || null,\n meta.ownerId || null\n )\n metaInserted = true\n }\n } finally {\n timings.metaWriteMs += elapsedMs(startedAt, now)\n sampleRss()\n }\n },\n\n onMembers: (members: ParsedMember[]) => {\n const startedAt = now()\n try {\n callbackStats.onMembersCalls++\n callbackStats.totalMembersReceived += members.length\n logger?.info(`Received member batch: ${members.length} members`)\n for (const member of members) {\n const accountName = normalizeSystemMemberName(formatFeature.id, member.platformId, member.accountName)\n const groupNickname = normalizeSystemMemberName(formatFeature.id, member.platformId, member.groupNickname)\n insertMember.run(\n member.platformId,\n accountName || null,\n groupNickname || null,\n member.aliases ? JSON.stringify(member.aliases) : '[]',\n member.avatar || null,\n member.roles ? JSON.stringify(member.roles) : '[]'\n )\n const row = getMemberId.get(member.platformId) as { id: number } | undefined\n if (row) memberIdMap.set(member.platformId, row.id)\n }\n } finally {\n timings.memberWriteMs += elapsedMs(startedAt, now)\n sampleRss()\n }\n },\n\n onMessageBatch: (messages: ParsedMessage[]) => {\n const startedAt = now()\n const transactionMsBefore = messageTransactionMs\n const checkpointMsBefore = timings.checkpointMs\n try {\n const pendingRows: MessageInsertRow[] = []\n const flushPendingRows = () => {\n if (pendingRows.length === 0) return\n messageInsertStatementCount += messageBatchInserter.insert(pendingRows)\n pendingRows.length = 0\n }\n\n callbackStats.onMessageBatchCalls++\n callbackStats.totalMessagesReceived += messages.length\n if (callbackStats.onMessageBatchCalls <= 3 || callbackStats.onMessageBatchCalls % 10 === 0) {\n logger?.info(`Received message batch #${callbackStats.onMessageBatchCalls}: ${messages.length} messages`)\n }\n\n for (const msg of messages) {\n const senderAccountName = normalizeSystemMemberName(\n formatFeature.id,\n msg.senderPlatformId,\n msg.senderAccountName\n )\n const senderGroupNickname = normalizeSystemMemberName(\n formatFeature.id,\n msg.senderPlatformId,\n msg.senderGroupNickname\n )\n const prepared = prepareMessageForCreate(msg, senderAccountName)\n if ('skipCounter' in prepared) {\n callbackStats[prepared.skipCounter]++\n continue\n }\n\n const dedupMessage = prepared.message\n\n if (registerMessageAndCheckDuplicate(dedupMessage, dedupState)) {\n duplicateCount++\n continue\n }\n\n if (!memberIdMap.has(msg.senderPlatformId)) {\n insertMember.run(\n msg.senderPlatformId,\n senderAccountName || null,\n senderGroupNickname || null,\n '[]',\n null,\n '[]'\n )\n const row = getMemberId.get(msg.senderPlatformId) as { id: number } | undefined\n if (row) memberIdMap.set(msg.senderPlatformId, row.id)\n }\n\n const senderId = memberIdMap.get(msg.senderPlatformId)\n if (senderId === undefined) continue\n\n pendingRows.push({\n senderId,\n senderAccountName: senderAccountName || null,\n senderGroupNickname: senderGroupNickname || null,\n timestamp: dedupMessage.timestamp,\n type: dedupMessage.type,\n content: dedupMessage.content,\n replyToMessageId: msg.replyToMessageId || null,\n platformMessageId: msg.platformMessageId || null,\n })\n messageCountInBatch++\n totalMessageCount++\n\n trackNickname(accountNameTracker, msg.senderPlatformId, senderAccountName, msg.timestamp)\n trackNickname(groupNicknameTracker, msg.senderPlatformId, senderGroupNickname, msg.timestamp)\n\n if (messageCountInBatch >= BATCH_COMMIT_SIZE) {\n flushPendingRows()\n commitAndBeginNew()\n messageCountInBatch = 0\n }\n }\n flushPendingRows()\n } finally {\n const nestedStageMs =\n messageTransactionMs - transactionMsBefore + (timings.checkpointMs - checkpointMsBefore)\n timings.messageWriteMs += Math.max(0, elapsedMs(startedAt, now) - nestedStageMs)\n sampleRss()\n }\n },\n },\n formatFeature.id\n )\n finishParserTiming()\n\n if (inTransaction) {\n commitTransaction()\n }\n timings.messageWriteMs += messageTransactionMs\n\n // Flush nickname history in batch\n onProgress({\n stage: 'saving',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n await yieldToEventLoop()\n logger?.perf('Writing nickname history', totalMessageCount)\n\n const nicknameHistoryStartedAt = now()\n db.exec('BEGIN TRANSACTION')\n let historyCount = 0\n\n flushNicknameHistory(accountNameTracker, 'account_name', memberIdMap, insertNameHistory, updateMemberAccountName)\n flushNicknameHistory(\n groupNicknameTracker,\n 'group_nickname',\n memberIdMap,\n insertNameHistory,\n updateMemberGroupNickname\n )\n historyCount = countHistory(accountNameTracker) + countHistory(groupNicknameTracker)\n\n db.exec('COMMIT')\n timings.nicknameHistoryMs = elapsedMs(nicknameHistoryStartedAt, now)\n sampleRss()\n logger?.perf(`Nickname history written (${historyCount} entries)`, totalMessageCount)\n\n // Create indexes (deferred for performance)\n onProgress({\n stage: 'indexing',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n await yieldToEventLoop()\n logger?.perf('Creating indexes', totalMessageCount)\n const indexCreationStartedAt = now()\n db.exec(CHAT_DB_INDEXES)\n timings.indexCreationMs = elapsedMs(indexCreationStartedAt, now)\n sampleRss()\n logger?.perf('Indexes created', totalMessageCount)\n\n // Final WAL checkpoint + session index + post-import hook\n onProgress({\n stage: 'indexing',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n await yieldToEventLoop()\n measureCheckpoint()\n sampleRss()\n logger?.perf('WAL checkpoint done', totalMessageCount)\n\n // Build session index (segment / message_context tables)\n const sessionIndexStartedAt = now()\n const sessionGapThreshold = normalizeSessionGapThreshold(deps.sessionGapThreshold)\n try {\n const generateSessionIndex = deps.generateSessionIndex ?? generateCoreSessionIndex\n generateSessionIndex(db, sessionGapThreshold)\n logger?.perfDetail(`[SessionIndex] Built with gap threshold ${sessionGapThreshold}s`)\n logger?.perf('Session index built', totalMessageCount)\n } catch (error) {\n logger?.error('Session index build failed (non-fatal)', error instanceof Error ? error : undefined)\n } finally {\n timings.sessionIndexMs = elapsedMs(sessionIndexStartedAt, now)\n sampleRss()\n }\n\n // Post-import hook (e.g. overview cache)\n if (deps.postImportHook) {\n const postImportHookStartedAt = now()\n try {\n await deps.postImportHook(db, sessionId)\n logger?.perf('Post-import hook done', totalMessageCount)\n } catch {\n /* non-fatal */\n } finally {\n timings.postImportHookMs = elapsedMs(postImportHookStartedAt, now)\n sampleRss()\n }\n }\n\n logger?.perfDetail(\n `[Stages] parser=${timings.parserMs.toFixed(1)}ms | message-write=${timings.messageWriteMs.toFixed(1)}ms | ` +\n `indexes=${timings.indexCreationMs.toFixed(1)}ms | ` +\n `session-index=${timings.sessionIndexMs.toFixed(1)}ms | hook=${timings.postImportHookMs.toFixed(1)}ms`\n )\n logger?.perf('Import completed', totalMessageCount)\n\n // Diagnostic logging\n logger?.info(`=== Parser Callback Stats ===`)\n logger?.info(`onProgress calls: ${callbackStats.onProgressCalls}`)\n logger?.info(`onLog calls: ${callbackStats.onLogCalls}`)\n logger?.info(`onMeta calls: ${callbackStats.onMetaCalls}`)\n logger?.info(\n `onMembers calls: ${callbackStats.onMembersCalls}, total members: ${callbackStats.totalMembersReceived}`\n )\n logger?.info(\n `onMessageBatch calls: ${callbackStats.onMessageBatchCalls}, total messages: ${callbackStats.totalMessagesReceived}`\n )\n if (\n callbackStats.skippedNoSenderId > 0 ||\n callbackStats.skippedNoAccountName > 0 ||\n callbackStats.skippedInvalidTimestamp > 0 ||\n callbackStats.skippedNoType > 0\n ) {\n logger?.info(`=== Skipped Messages Stats ===`)\n if (callbackStats.skippedNoSenderId > 0)\n logger?.info(` missing senderPlatformId: ${callbackStats.skippedNoSenderId}`)\n if (callbackStats.skippedNoAccountName > 0)\n logger?.info(` missing senderAccountName: ${callbackStats.skippedNoAccountName}`)\n if (callbackStats.skippedInvalidTimestamp > 0)\n logger?.info(` invalid timestamp: ${callbackStats.skippedInvalidTimestamp}`)\n if (callbackStats.skippedNoType > 0) logger?.info(` missing type: ${callbackStats.skippedNoType}`)\n }\n if (duplicateCount > 0) logger?.info(`Duplicate messages skipped: ${duplicateCount}`)\n\n logger?.summary(totalMessageCount, memberIdMap.size)\n\n if (totalMessageCount === 0) {\n logger?.error(\n `Import failed: no messages parsed (received ${callbackStats.totalMessagesReceived} messages, all skipped or none received)`\n )\n shouldDeleteDb = true\n importError = 'error.no_messages'\n }\n } catch (error) {\n finishParserTiming()\n logger?.error('Import failed', error instanceof Error ? error : undefined)\n if (inTransaction) {\n try {\n db.exec('ROLLBACK')\n } catch {\n /* ignore */\n }\n }\n shouldDeleteDb = true\n importError = error instanceof Error ? error.message : String(error)\n } finally {\n db.close()\n\n if (tempFilePath && preprocessor) {\n preprocessor.cleanup(tempFilePath)\n }\n\n if (shouldDeleteDb) {\n deps.deleteDatabase(sessionId)\n }\n sampleRss()\n }\n\n timings.totalMs = elapsedMs(totalStartedAt, now)\n const bytesPerMegabyte = 1024 * 1024\n const diagnostics: ImportDiagnostics = {\n logFile: logger?.getCurrentLogFile() ?? null,\n detectedFormat: formatFeature ? `${formatFeature.name} (${formatFeature.id})` : null,\n messagesReceived: callbackStats.totalMessagesReceived,\n messagesWritten: totalMessageCount,\n duplicateCount,\n messagesSkipped:\n callbackStats.skippedNoSenderId +\n callbackStats.skippedNoAccountName +\n callbackStats.skippedInvalidTimestamp +\n callbackStats.skippedNoType,\n skipReasons: {\n noSenderId: callbackStats.skippedNoSenderId,\n noAccountName: callbackStats.skippedNoAccountName,\n invalidTimestamp: callbackStats.skippedInvalidTimestamp,\n noType: callbackStats.skippedNoType,\n },\n performance: {\n timings,\n messageBatchCount: callbackStats.onMessageBatchCalls,\n messageTransactionCount,\n messageInsertStatementCount,\n rssStartMb: rssStartBytes / bytesPerMegabyte,\n rssSampledPeakMb: rssPeakBytes / bytesPerMegabyte,\n rssSampledDeltaMb: (rssPeakBytes - rssStartBytes) / bytesPerMegabyte,\n },\n }\n\n if (importError) {\n return { success: false, platform: importedPlatform, error: importError, diagnostics }\n }\n return { success: true, sessionId, platform: importedPlatform, diagnostics }\n}\n\n// ==================== Dry-run analysis ====================\n\nexport interface AnalyzeNewImportResult {\n totalMessages: number\n newMessageCount: number\n duplicateCount: number\n totalMembers: number\n meta: { name: string; platform: string; type: string } | null\n error?: string\n}\n\nexport interface AnalyzeNewImportOptions {\n formatId?: string\n chatIndex?: number\n}\n\nexport async function analyzeNewImport(\n filePath: string,\n onProgress: ImportProgressCallback,\n options?: AnalyzeNewImportOptions\n): Promise<AnalyzeNewImportResult> {\n const formatFeature = options?.formatId ? getFormatFeatureById(options.formatId) : detectFormat(filePath)\n if (!formatFeature) {\n return {\n totalMessages: 0,\n newMessageCount: 0,\n duplicateCount: 0,\n totalMembers: 0,\n meta: null,\n error: 'error.unrecognized_format',\n }\n }\n\n let meta: { name: string; platform: string; type: string } | null = null\n const memberSet = new Set<string>()\n const dedupState = createMessageDedupState()\n let totalMessages = 0\n let newMessageCount = 0\n let duplicateCount = 0\n\n await streamParseFile(\n filePath,\n {\n formatOptions: options?.chatIndex === undefined ? undefined : { chatIndex: options.chatIndex },\n onMeta: (parsedMeta: ParsedMeta) => {\n meta = { name: parsedMeta.name, platform: parsedMeta.platform, type: parsedMeta.type }\n },\n onMembers: (members: ParsedMember[]) => {\n for (const m of members) memberSet.add(m.platformId)\n },\n onProgress: (progress: ParseProgress) => {\n onProgress(progress)\n },\n onMessageBatch: (batch: ParsedMessage[]) => {\n for (const msg of batch) {\n totalMessages++\n if (!memberSet.has(msg.senderPlatformId)) memberSet.add(msg.senderPlatformId)\n\n const prepared = prepareMessageForCreate(msg)\n if ('skipCounter' in prepared) continue\n if (registerMessageAndCheckDuplicate(prepared.message, dedupState)) {\n duplicateCount++\n } else {\n newMessageCount++\n }\n }\n },\n },\n options?.formatId\n )\n\n return { totalMessages, newMessageCount, duplicateCount, totalMembers: memberSet.size, meta }\n}\n\n// ==================== Temp DB for merge preview ====================\n\nexport interface StreamParseFileInfoResult {\n name: string\n format: string\n platform: string\n messageCount: number\n memberCount: number\n fileSize: number\n tempDbPath: string\n}\n\nexport interface StreamParseFileInfoDeps {\n createTempDatabase(filePath: string): { db: DatabaseAdapter; tempDbPath: string }\n onProgress: ImportProgressCallback\n}\n\nexport async function streamParseFileInfo(\n filePath: string,\n deps: StreamParseFileInfoDeps\n): Promise<StreamParseFileInfoResult> {\n const formatFeature = detectFormat(filePath)\n if (!formatFeature) {\n throw new Error('Unrecognized file format')\n }\n\n const fileSize = fs.statSync(filePath).size\n\n deps.onProgress({\n stage: 'parsing',\n bytesRead: 0,\n totalBytes: fileSize,\n messagesProcessed: 0,\n percentage: 0,\n message: '',\n })\n\n const { db, tempDbPath } = deps.createTempDatabase(filePath)\n\n const insertMeta = db.prepare(\n 'INSERT INTO meta (name, platform, type, group_id, group_avatar, owner_id) VALUES (?, ?, ?, ?, ?, ?)'\n )\n const insertMember = db.prepare(\n 'INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar) VALUES (?, ?, ?, ?)'\n )\n const insertMessage = db.prepare(\n `INSERT INTO message (\n platform_message_id, sender_platform_id, sender_account_name, sender_group_nickname,\n timestamp, type, content, reply_to_message_id\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n\n let meta: ParsedMeta = { name: 'Unknown', platform: formatFeature.platform, type: 0 as any }\n const memberSet = new Set<string>()\n let messageCount = 0\n let metaInserted = false\n\n db.exec('BEGIN TRANSACTION')\n\n try {\n await streamParseFile(filePath, {\n batchSize: fileSize > 100 * 1024 * 1024 ? 2000 : 5000,\n\n onProgress: (progress: ParseProgress) => {\n deps.onProgress(progress)\n },\n\n onMeta: (parsedMeta: ParsedMeta) => {\n meta = parsedMeta\n if (!metaInserted) {\n insertMeta.run(\n parsedMeta.name,\n parsedMeta.platform,\n parsedMeta.type,\n parsedMeta.groupId || null,\n parsedMeta.groupAvatar || null,\n parsedMeta.ownerId || null\n )\n metaInserted = true\n }\n },\n\n onMembers: (parsedMembers: ParsedMember[]) => {\n for (const m of parsedMembers) {\n if (!memberSet.has(m.platformId)) {\n memberSet.add(m.platformId)\n insertMember.run(m.platformId, m.accountName || null, m.groupNickname || null, m.avatar || null)\n }\n }\n },\n\n onMessageBatch: (batch: ParsedMessage[]) => {\n for (const msg of batch) {\n if (!memberSet.has(msg.senderPlatformId)) {\n memberSet.add(msg.senderPlatformId)\n insertMember.run(msg.senderPlatformId, msg.senderAccountName || null, msg.senderGroupNickname || null, null)\n }\n\n insertMessage.run(\n msg.platformMessageId || null,\n msg.senderPlatformId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\n msg.timestamp,\n msg.type,\n msg.content || null,\n msg.replyToMessageId || null\n )\n messageCount++\n }\n },\n })\n\n db.exec('COMMIT')\n db.close()\n\n return {\n name: meta.name,\n format: formatFeature.name,\n platform: meta.platform,\n messageCount,\n memberCount: memberSet.size,\n fileSize,\n tempDbPath,\n }\n } catch (error) {\n try {\n db.exec('ROLLBACK')\n } catch {\n /* ignore */\n }\n db.close()\n\n try {\n if (fs.existsSync(tempDbPath)) fs.unlinkSync(tempDbPath)\n } catch {\n /* ignore */\n }\n\n throw error\n }\n}\n\n// ==================== Internal helpers ====================\n\ntype NicknameTracker = Map<\n string,\n { currentName: string; lastSeenTs: number; history: Array<{ name: string; startTs: number }> }\n>\n\nfunction trackNickname(\n tracker: NicknameTracker,\n platformId: string,\n name: string | undefined | null,\n timestamp: number\n): void {\n if (!name) return\n // For account_name tracking, skip if name equals platformId\n const existing = tracker.get(platformId)\n if (!existing) {\n tracker.set(platformId, {\n currentName: name,\n lastSeenTs: timestamp,\n history: [{ name, startTs: timestamp }],\n })\n } else if (existing.currentName !== name) {\n existing.history.push({ name, startTs: timestamp })\n existing.currentName = name\n existing.lastSeenTs = timestamp\n } else {\n existing.lastSeenTs = timestamp\n }\n}\n\ninterface PreparedStatement {\n run(...args: unknown[]): unknown\n}\n\nfunction flushNicknameHistory(\n tracker: NicknameTracker,\n nameType: string,\n memberIdMap: Map<string, number>,\n insertNameHistory: PreparedStatement,\n updateMemberName: PreparedStatement\n): void {\n for (const [platformId, data] of tracker.entries()) {\n if (!platformId || platformId === '0' || platformId === 'undefined') continue\n\n const senderId = memberIdMap.get(platformId)\n if (!senderId) continue\n\n const uniqueNames = new Map<string, { startTs: number; lastTs: number }>()\n for (const h of data.history) {\n const existing = uniqueNames.get(h.name)\n if (!existing) {\n uniqueNames.set(h.name, { startTs: h.startTs, lastTs: h.startTs })\n } else {\n existing.lastTs = h.startTs\n }\n }\n\n // For account_name, skip the platformId itself\n if (nameType === 'account_name') {\n uniqueNames.delete(platformId)\n }\n\n if (uniqueNames.size <= 1) {\n updateMemberName.run(data.currentName, platformId)\n continue\n }\n\n const sortedHistory = Array.from(uniqueNames.entries()).sort((a, b) => a[1].startTs - b[1].startTs)\n for (let i = 0; i < sortedHistory.length; i++) {\n const [name, { startTs }] = sortedHistory[i]\n const endTs = i < sortedHistory.length - 1 ? sortedHistory[i + 1][1].startTs : null\n insertNameHistory.run(senderId, nameType, name, startTs, endTs)\n }\n\n updateMemberName.run(data.currentName, platformId)\n }\n}\n\nfunction countHistory(tracker: NicknameTracker): number {\n let count = 0\n for (const [, data] of tracker.entries()) {\n if (data.history.length > 1) count += data.history.length\n }\n return count\n}\n","import { generateMessageKey } from '@openchatlab/core'\n\nconst SCOPED_PLATFORM_MESSAGE_ID_PATTERN = /^(__chatlab_message_scope__\\d+__)(.*)$/\n\nexport interface MessageDedupState {\n platformMessageIds: Set<string>\n /** Fallback keys for every accepted message, regardless of platform ID. */\n fallbackKeys: Set<string>\n /** Unmatched fallback keys whose accepted message did not have a stable platform ID. */\n fallbackOnlyKeys: Set<string>\n}\n\nexport interface DedupMessage {\n platformMessageId?: string\n timestamp: number\n senderPlatformId: string\n type: number\n content: string | null\n replyToMessageId?: string\n}\n\nexport interface ParsedPlatformMessageId {\n rawId: string\n scope?: string\n}\n\nexport function parsePlatformMessageId(id: string): ParsedPlatformMessageId {\n const scoped = SCOPED_PLATFORM_MESSAGE_ID_PATTERN.exec(id)\n if (!scoped) return { rawId: id }\n\n try {\n return { rawId: decodeURIComponent(scoped[2]), scope: scoped[1] }\n } catch {\n return { rawId: id }\n }\n}\n\nexport function applyPlatformMessageIdScope(id: string | undefined, scope: string | undefined): string | undefined {\n if (!id || !scope) return id\n return `${scope}${encodeURIComponent(parsePlatformMessageId(id).rawId)}`\n}\n\nexport function generateFallbackMessageKey(message: Omit<DedupMessage, 'platformMessageId'>): string {\n const contentKey = generateMessageKey(message.timestamp, message.senderPlatformId, message.content)\n return JSON.stringify([contentKey, message.type, message.replyToMessageId || null])\n}\n\nexport function createMessageDedupState(\n platformMessageIds: Iterable<string> = [],\n fallbackKeys: Iterable<string> = [],\n fallbackOnlyKeys: Iterable<string> = []\n): MessageDedupState {\n return {\n platformMessageIds: new Set(platformMessageIds),\n fallbackKeys: new Set(fallbackKeys),\n fallbackOnlyKeys: new Set(fallbackOnlyKeys),\n }\n}\n\n/**\n * Canonical import dedup rule shared by file imports and JSON Push: prefer stable platform\n * message IDs and fall back to timestamp + sender platform ID + type + normalized content + reply target.\n */\nexport function registerMessageAndCheckDuplicate(message: DedupMessage, state: MessageDedupState): boolean {\n const key = generateFallbackMessageKey(message)\n\n if (message.platformMessageId) {\n if (state.platformMessageIds.has(message.platformMessageId)) return true\n state.platformMessageIds.add(message.platformMessageId)\n\n // Only bridge ID-bearing messages to fallback-only copies. Comparing this\n // key against other ID-bearing messages would incorrectly merge distinct\n // platform messages that happen to have identical content and timestamps.\n if (state.fallbackOnlyKeys.delete(key)) return true\n state.fallbackKeys.add(key)\n return false\n }\n\n if (state.fallbackKeys.has(key)) return true\n state.fallbackKeys.add(key)\n state.fallbackOnlyKeys.add(key)\n return false\n}\n","/**\n * Platform-agnostic incremental importer.\n *\n * Extracted from electron/main/worker/import/incrementalImport.ts.\n * Appends new messages to an existing session, using dual-path dedup:\n * platformMessageId (preferred) + content hash (fallback).\n *\n * Callers provide DatabaseAdapter + progress callback via dependency injection.\n */\n\nimport type { DatabaseAdapter, PreparedStatement } from '@openchatlab/core'\nimport { generateSessionIndex, generateIncrementalSessionIndex, getSessionIndexStats } from '@openchatlab/core'\nimport {\n streamParseFile,\n detectFormat,\n getFormatFeatureById,\n type ParseProgress,\n type StreamParseCallbacks,\n} from '@openchatlab/parser'\nimport {\n applyPlatformMessageIdScope,\n createMessageDedupState,\n generateFallbackMessageKey,\n registerMessageAndCheckDuplicate,\n type DedupMessage,\n type MessageDedupState,\n} from './message-deduplicator'\nimport { normalizeSystemMemberName, SYSTEM_MEMBER_NAME, type ImportProgressCallback } from './streaming-importer'\n\n// ==================== Public interfaces ====================\n\nexport interface SenderPlatformIdMapping {\n sourceId: string\n targetId: string\n}\n\nexport interface ImportOptions {\n metaUpdateMode?: 'patch' | 'none'\n memberUpdateMode?: 'upsert' | 'none'\n formatId?: string\n chatIndex?: number\n /** Internal scope selected by automatic matching for merger-namespaced message IDs. */\n platformMessageIdScope?: string\n /** Source-to-target sender IDs proven by automatic platform-message matching. */\n senderPlatformIdMappings?: SenderPlatformIdMapping[]\n}\n\nexport interface IncrementalAnalyzeResult {\n newMessageCount: number\n duplicateCount: number\n totalInFile: number\n platform?: string\n error?: string\n}\n\ninterface ErrorSample {\n index: number\n reason: string\n detail: string\n}\n\nexport interface IncrementalImportResult {\n success: boolean\n newMessageCount: number\n error?: string\n batch?: {\n receivedCount: number\n writtenCount: number\n duplicateCount: number\n errorCount: number\n errorReasonCounts: Record<string, number>\n errorSample: ErrorSample[]\n }\n session?: {\n totalCount: number\n memberCount: number\n firstTimestamp: number\n lastTimestamp: number\n }\n updates?: {\n metaUpdated: boolean\n membersAdded: number\n membersUpdated: number\n }\n}\n\nexport interface IncrementalImportDeps {\n /** Open existing session DB for read-only access (analyze) or read-write (import). */\n openDatabase(sessionId: string, readonly?: boolean): DatabaseAdapter\n onProgress: ImportProgressCallback\n /** Optional parser diagnostics hook (e.g. native parser fallback monitoring). */\n onParserLog?: NonNullable<StreamParseCallbacks['onLog']>\n /** Optional hook after incremental import (e.g. update overview cache). */\n postImportHook?: (db: DatabaseAdapter, sessionId: string) => void | Promise<void>\n}\n\n// ==================== Internal helpers ====================\n\nconst PLATFORM_ID_QUERY_CHUNK_SIZE = 500\n\ninterface ExistingMessageCandidate {\n type: number\n content: string | null\n reply_to_message_id: string | null\n platform_message_id: string | null\n}\n\ninterface ExistingMessageLookup {\n db: DatabaseAdapter\n maxMessageId: number\n hasFallbackOnlyMessages: boolean\n consumedFallbackOnlyKeys: Set<string>\n bridgedPlatformMessageIds: Set<string>\n candidateStatement: PreparedStatement\n platformIdStatements: Map<number, PreparedStatement>\n}\n\ntype CandidateCache = Map<string, ExistingMessageCandidate[]>\n\nfunction applyMessageIdScope<T extends { platformMessageId?: string; replyToMessageId?: string }>(\n message: T,\n scope: string | undefined\n): T {\n if (!scope) return message\n return {\n ...message,\n platformMessageId: applyPlatformMessageIdScope(message.platformMessageId, scope),\n replyToMessageId: applyPlatformMessageIdScope(message.replyToMessageId, scope),\n }\n}\n\nfunction createPlatformIdMapper(mappings: SenderPlatformIdMapping[] | undefined): (platformId: string) => string {\n const mapping = new Map(mappings?.map(({ sourceId, targetId }) => [sourceId, targetId]))\n return (platformId) => mapping.get(platformId) ?? platformId\n}\n\nfunction applySenderPlatformIdMapping<T extends { senderPlatformId: string }>(\n message: T,\n mapPlatformId: (platformId: string) => string\n): T {\n const senderPlatformId = mapPlatformId(message.senderPlatformId)\n return senderPlatformId === message.senderPlatformId ? message : { ...message, senderPlatformId }\n}\n\nfunction createExistingMessageLookup(db: DatabaseAdapter): ExistingMessageLookup {\n const maxMessageId =\n (db.prepare('SELECT MAX(id) AS max_id FROM message').get() as { max_id: number | null } | undefined)?.max_id ?? 0\n const hasFallbackOnlyMessages = Boolean(\n db\n .prepare(\n `SELECT 1 FROM message\n WHERE id <= ? AND (platform_message_id IS NULL OR platform_message_id = '')\n LIMIT 1`\n )\n .get(maxMessageId)\n )\n\n return {\n db,\n maxMessageId,\n hasFallbackOnlyMessages,\n consumedFallbackOnlyKeys: new Set<string>(),\n bridgedPlatformMessageIds: new Set<string>(),\n candidateStatement: db.prepare(\n `SELECT msg.type, msg.content, msg.reply_to_message_id, msg.platform_message_id\n FROM member m\n JOIN message msg ON msg.sender_id = m.id\n WHERE m.platform_id = ? AND msg.ts = ? AND msg.id <= ?`\n ),\n platformIdStatements: new Map<number, PreparedStatement>(),\n }\n}\n\nfunction findExistingPlatformMessageIds(\n lookup: ExistingMessageLookup,\n platformMessageIds: Iterable<string | undefined>\n): Set<string> {\n const uniqueIds = [...new Set([...platformMessageIds].filter((id): id is string => Boolean(id)))]\n const existingIds = new Set<string>()\n\n for (let start = 0; start < uniqueIds.length; start += PLATFORM_ID_QUERY_CHUNK_SIZE) {\n const chunk = uniqueIds.slice(start, start + PLATFORM_ID_QUERY_CHUNK_SIZE)\n let statement = lookup.platformIdStatements.get(chunk.length)\n if (!statement) {\n const placeholders = chunk.map(() => '?').join(', ')\n statement = lookup.db.prepare(\n `SELECT platform_message_id\n FROM message\n WHERE id <= ? AND platform_message_id IN (${placeholders})`\n )\n lookup.platformIdStatements.set(chunk.length, statement)\n }\n const rows = statement.all(lookup.maxMessageId, ...chunk) as Array<{ platform_message_id: string }>\n for (const row of rows) existingIds.add(row.platform_message_id)\n }\n\n return existingIds\n}\n\nfunction getExistingCandidates(\n lookup: ExistingMessageLookup,\n message: DedupMessage,\n cache: CandidateCache\n): ExistingMessageCandidate[] {\n const cacheKey = JSON.stringify([message.senderPlatformId, message.timestamp])\n const cached = cache.get(cacheKey)\n if (cached) return cached\n\n const rows = lookup.candidateStatement.all(\n message.senderPlatformId,\n message.timestamp,\n lookup.maxMessageId\n ) as unknown as ExistingMessageCandidate[]\n cache.set(cacheKey, rows)\n return rows\n}\n\nfunction candidateFallbackKey(message: DedupMessage, candidate: ExistingMessageCandidate): string {\n return generateFallbackMessageKey({\n timestamp: message.timestamp,\n senderPlatformId: message.senderPlatformId,\n type: candidate.type,\n content: candidate.content,\n replyToMessageId: candidate.reply_to_message_id ?? undefined,\n })\n}\n\nfunction isExistingMessageDuplicate(\n lookup: ExistingMessageLookup,\n message: DedupMessage,\n existingBatchPlatformIds: Set<string>,\n candidateCache: CandidateCache\n): boolean {\n if (\n message.platformMessageId &&\n (existingBatchPlatformIds.has(message.platformMessageId) ||\n lookup.bridgedPlatformMessageIds.has(message.platformMessageId))\n ) {\n return true\n }\n\n const fallbackKey = generateFallbackMessageKey(message)\n if (message.platformMessageId) {\n if (!lookup.hasFallbackOnlyMessages || lookup.consumedFallbackOnlyKeys.has(fallbackKey)) return false\n const matchesFallbackOnly = getExistingCandidates(lookup, message, candidateCache).some(\n (candidate) => !candidate.platform_message_id && candidateFallbackKey(message, candidate) === fallbackKey\n )\n if (matchesFallbackOnly) {\n lookup.consumedFallbackOnlyKeys.add(fallbackKey)\n lookup.bridgedPlatformMessageIds.add(message.platformMessageId)\n }\n return matchesFallbackOnly\n }\n\n return getExistingCandidates(lookup, message, candidateCache).some(\n (candidate) => candidateFallbackKey(message, candidate) === fallbackKey\n )\n}\n\nfunction isDuplicate(\n lookup: ExistingMessageLookup,\n incomingState: MessageDedupState,\n message: DedupMessage,\n existingBatchPlatformIds: Set<string>,\n candidateCache: CandidateCache\n): boolean {\n if (isExistingMessageDuplicate(lookup, message, existingBatchPlatformIds, candidateCache)) return true\n return registerMessageAndCheckDuplicate(message, incomingState)\n}\n\nexport function normalizeImportTimestamp(timestamp: unknown): number | null {\n const value = typeof timestamp === 'string' && timestamp.trim() !== '' ? Number(timestamp) : timestamp\n return typeof value === 'number' && value > 0 && Number.isFinite(value) ? value : null\n}\n\n// ==================== Analyze (dry-run) ====================\n\nexport async function analyzeIncrementalImport(\n sessionId: string,\n filePath: string,\n deps: IncrementalImportDeps,\n options?: ImportOptions\n): Promise<IncrementalAnalyzeResult> {\n const formatFeature = options?.formatId ? getFormatFeatureById(options.formatId) : detectFormat(filePath)\n if (!formatFeature) {\n return { error: 'error.unrecognized_format', newMessageCount: 0, duplicateCount: 0, totalInFile: 0 }\n }\n\n let db: DatabaseAdapter\n try {\n db = deps.openDatabase(sessionId, true)\n } catch {\n return { error: 'error.session_not_found', newMessageCount: 0, duplicateCount: 0, totalInFile: 0 }\n }\n\n let totalInFile = 0\n let newMessageCount = 0\n let duplicateCount = 0\n let platform = formatFeature.platform\n const mapPlatformId = createPlatformIdMapper(options?.senderPlatformIdMappings)\n\n try {\n const existingLookup = createExistingMessageLookup(db)\n const incomingState = createMessageDedupState()\n\n await streamParseFile(\n filePath,\n {\n formatOptions: options?.chatIndex === undefined ? undefined : { chatIndex: options.chatIndex },\n onMeta: (meta) => {\n platform = meta.platform\n },\n onMembers: () => {},\n onProgress: (progress: ParseProgress) => {\n deps.onProgress(progress)\n },\n onLog: deps.onParserLog,\n onMessageBatch: (batch) => {\n const scopedBatch = batch.map((message) =>\n applyMessageIdScope(applySenderPlatformIdMapping(message, mapPlatformId), options?.platformMessageIdScope)\n )\n const existingBatchPlatformIds = findExistingPlatformMessageIds(\n existingLookup,\n scopedBatch.map((message) => message.platformMessageId)\n )\n const candidateCache: CandidateCache = new Map()\n\n for (const msg of scopedBatch) {\n totalInFile++\n const timestamp = normalizeImportTimestamp(msg.timestamp)\n if (timestamp === null) continue\n\n if (\n isDuplicate(\n existingLookup,\n incomingState,\n { ...msg, timestamp },\n existingBatchPlatformIds,\n candidateCache\n )\n ) {\n duplicateCount++\n } else {\n newMessageCount++\n }\n }\n },\n },\n options?.formatId\n )\n } finally {\n db.close()\n }\n\n return { newMessageCount, duplicateCount, totalInFile, platform }\n}\n\n// ==================== Execute incremental import ====================\n\nexport async function incrementalImport(\n sessionId: string,\n filePath: string,\n deps: IncrementalImportDeps,\n options?: ImportOptions\n): Promise<IncrementalImportResult> {\n const formatFeature = options?.formatId ? getFormatFeatureById(options.formatId) : detectFormat(filePath)\n if (!formatFeature) {\n return { success: false, newMessageCount: 0, error: 'error.unrecognized_format' }\n }\n\n let db: DatabaseAdapter\n try {\n db = deps.openDatabase(sessionId, false)\n } catch {\n return { success: false, newMessageCount: 0, error: 'error.session_not_found' }\n }\n\n const metaUpdateMode = options?.metaUpdateMode ?? 'patch'\n const memberUpdateMode = options?.memberUpdateMode ?? 'upsert'\n const mapPlatformId = createPlatformIdMapper(options?.senderPlatformIdMappings)\n\n try {\n const existingLookup = createExistingMessageLookup(db)\n const incomingState = createMessageDedupState()\n\n const memberIdMap = new Map<string, number>()\n const existingMembers = db.prepare('SELECT id, platform_id FROM member').all() as Array<{\n id: number\n platform_id: string\n }>\n for (const m of existingMembers) {\n memberIdMap.set(m.platform_id, m.id)\n }\n\n const upsertMember = db.prepare(`\n INSERT INTO member (platform_id, account_name, group_nickname, aliases, avatar, roles)\n VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(platform_id) DO UPDATE SET\n account_name = COALESCE(NULLIF(excluded.account_name, ''), account_name),\n group_nickname = COALESCE(NULLIF(excluded.group_nickname, ''), group_nickname),\n aliases = CASE WHEN excluded.aliases != '[]' THEN excluded.aliases ELSE aliases END,\n avatar = COALESCE(NULLIF(excluded.avatar, ''), avatar),\n roles = CASE WHEN excluded.roles != '[]' THEN excluded.roles ELSE roles END\n `)\n\n const insertMemberMinimal = db.prepare(`\n INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar)\n VALUES (?, ?, ?, ?)\n `)\n\n const getMemberId = db.prepare('SELECT id FROM member WHERE platform_id = ?')\n\n const insertMessage = db.prepare(`\n INSERT INTO message (sender_id, sender_account_name, sender_group_nickname, ts, type, content, reply_to_message_id, platform_message_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `)\n\n const updateMeta = db.prepare(`\n UPDATE meta SET\n name = COALESCE(NULLIF(?, ''), name),\n group_id = COALESCE(NULLIF(?, ''), group_id),\n group_avatar = COALESCE(NULLIF(?, ''), group_avatar),\n owner_id = COALESCE(NULLIF(?, ''), owner_id),\n imported_at = ?\n `)\n\n const preWriteMaxTs =\n (db.prepare('SELECT MAX(ts) as max_ts FROM message').get() as { max_ts: number | null })?.max_ts ?? 0\n\n db.exec('BEGIN TRANSACTION')\n\n let newMessageCount = 0\n let duplicateCount = 0\n let processedCount = 0\n let minWrittenTs = Infinity\n let metaUpdated = false\n let membersAdded = 0\n let membersUpdated = 0\n let errorCount = 0\n const errorReasonCounts: Record<string, number> = {}\n const errorSamples: ErrorSample[] = []\n const MAX_ERROR_SAMPLES = 5\n const BATCH_SIZE = 5000\n\n function trackError(index: number, reason: string, detail: string) {\n errorCount++\n errorReasonCounts[reason] = (errorReasonCounts[reason] || 0) + 1\n if (errorSamples.length < MAX_ERROR_SAMPLES) {\n errorSamples.push({ index, reason, detail })\n }\n }\n\n await streamParseFile(\n filePath,\n {\n formatOptions: options?.chatIndex === undefined ? undefined : { chatIndex: options.chatIndex },\n onMeta: (meta) => {\n if (metaUpdateMode === 'none') return\n updateMeta.run(\n meta.name || '',\n meta.groupId || '',\n meta.groupAvatar || '',\n meta.ownerId ? mapPlatformId(meta.ownerId) : '',\n Math.floor(Date.now() / 1000)\n )\n metaUpdated = true\n },\n onMembers: (members) => {\n if (memberUpdateMode === 'none') return\n for (const m of members) {\n const platformId = mapPlatformId(m.platformId)\n const existed = memberIdMap.has(platformId)\n const accountName = normalizeSystemMemberName(formatFeature.id, platformId, m.accountName)\n const groupNickname = normalizeSystemMemberName(formatFeature.id, platformId, m.groupNickname)\n upsertMember.run(\n platformId,\n accountName || null,\n groupNickname || null,\n m.aliases ? JSON.stringify(m.aliases) : '[]',\n m.avatar || null,\n m.roles ? JSON.stringify(m.roles) : '[]'\n )\n if (!existed) {\n const row = getMemberId.get(platformId) as { id: number } | undefined\n if (row) memberIdMap.set(platformId, row.id)\n membersAdded++\n } else {\n membersUpdated++\n }\n }\n },\n onProgress: (progress: ParseProgress) => {\n deps.onProgress(progress)\n },\n onLog: deps.onParserLog,\n onMessageBatch: (batch) => {\n const scopedBatch = batch.map((message) =>\n applyMessageIdScope(applySenderPlatformIdMapping(message, mapPlatformId), options?.platformMessageIdScope)\n )\n const existingBatchPlatformIds = findExistingPlatformMessageIds(\n existingLookup,\n scopedBatch.map((message) => message.platformMessageId)\n )\n const candidateCache: CandidateCache = new Map()\n\n for (const msg of scopedBatch) {\n processedCount++\n\n if (!msg.senderPlatformId) {\n trackError(processedCount, 'MISSING_SENDER', 'sender field is empty')\n continue\n }\n if (msg.timestamp === undefined || msg.timestamp === null) {\n trackError(processedCount, 'MISSING_TIMESTAMP', 'timestamp field is missing')\n continue\n }\n const timestamp = normalizeImportTimestamp(msg.timestamp)\n if (timestamp === null) {\n trackError(processedCount, 'INVALID_TIMESTAMP', `timestamp value: ${msg.timestamp}`)\n continue\n }\n\n if (\n isDuplicate(\n existingLookup,\n incomingState,\n { ...msg, timestamp },\n existingBatchPlatformIds,\n candidateCache\n )\n ) {\n duplicateCount++\n continue\n }\n\n const senderAccountName = normalizeSystemMemberName(\n formatFeature.id,\n msg.senderPlatformId,\n msg.senderAccountName\n )\n const senderGroupNickname = normalizeSystemMemberName(\n formatFeature.id,\n msg.senderPlatformId,\n msg.senderGroupNickname\n )\n let memberId = memberIdMap.get(msg.senderPlatformId)\n if (!memberId) {\n insertMemberMinimal.run(\n msg.senderPlatformId,\n senderAccountName || null,\n senderGroupNickname || null,\n null\n )\n const row = getMemberId.get(msg.senderPlatformId) as { id: number } | undefined\n if (row) {\n memberId = row.id\n memberIdMap.set(msg.senderPlatformId, memberId)\n membersAdded++\n }\n }\n if (!memberId) continue\n\n insertMessage.run(\n memberId,\n senderAccountName || null,\n senderGroupNickname || null,\n timestamp,\n msg.type,\n msg.content || null,\n msg.replyToMessageId || null,\n msg.platformMessageId || null\n )\n\n if (timestamp < minWrittenTs) minWrittenTs = timestamp\n newMessageCount++\n }\n\n if (processedCount % BATCH_SIZE === 0) {\n deps.onProgress({\n stage: 'saving',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: processedCount,\n percentage: 50,\n message: `Processed ${processedCount}, added ${newMessageCount}`,\n })\n }\n },\n },\n options?.formatId\n )\n\n db.exec('COMMIT')\n\n if (!metaUpdated) {\n db.prepare('UPDATE meta SET imported_at = ?').run(Math.floor(Date.now() / 1000))\n }\n\n // Incremental session index (segment / message_context tables).\n // Use full rebuild for backfill batches: generateIncrementalSessionIndex\n // compares the first new message with the latest existing segment, so an\n // older backfilled message would be incorrectly attached to the newest segment.\n if (newMessageCount > 0) {\n try {\n const sessionGapThreshold = getSessionIndexStats(db).gapThreshold\n if (minWrittenTs < preWriteMaxTs) {\n generateSessionIndex(db, sessionGapThreshold)\n } else {\n generateIncrementalSessionIndex(db, sessionGapThreshold)\n }\n } catch {\n /* non-fatal */\n }\n }\n\n const sessionStats = db\n .prepare(\n `SELECT\n COUNT(*) as totalCount,\n MIN(ts) as firstTimestamp,\n MAX(ts) as lastTimestamp\n FROM message`\n )\n .get() as { totalCount: number; firstTimestamp: number; lastTimestamp: number }\n const memberCountRow = db\n .prepare(`SELECT COUNT(*) as count FROM member WHERE COALESCE(account_name, '') != ?`)\n .get(SYSTEM_MEMBER_NAME) as { count: number }\n\n // Post-import hook (e.g. overview cache)\n try {\n await deps.postImportHook?.(db, sessionId)\n } catch {\n /* non-fatal */\n }\n\n db.close()\n\n deps.onProgress({\n stage: 'done',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: processedCount,\n percentage: 100,\n message: `Import complete, added ${newMessageCount} messages`,\n })\n\n return {\n success: true,\n newMessageCount,\n batch: {\n receivedCount: processedCount,\n writtenCount: newMessageCount,\n duplicateCount,\n errorCount,\n errorReasonCounts,\n errorSample: errorSamples,\n },\n session: {\n totalCount: sessionStats.totalCount,\n memberCount: memberCountRow.count,\n firstTimestamp: sessionStats.firstTimestamp,\n lastTimestamp: sessionStats.lastTimestamp,\n },\n updates: {\n metaUpdated,\n membersAdded,\n membersUpdated,\n },\n }\n } catch (error) {\n try {\n db.exec('ROLLBACK')\n } catch {\n /* ignore */\n }\n db.close()\n\n console.error('[IncrementalImport] Error:', error)\n return { success: false, newMessageCount: 0, error: String(error) }\n }\n}\n","const SAFE_IMPORT_SESSION_ID_RE = /^[A-Za-z0-9_@-][A-Za-z0-9._@-]{0,127}$/\n\nexport function isValidImportSessionId(sessionId: string): boolean {\n return SAFE_IMPORT_SESSION_ID_RE.test(sessionId) && !sessionId.includes('..')\n}\n","import { createHash } from 'node:crypto'\nimport { generateMessageKey, getSessionMeta, isChatSessionDb, type DatabaseAdapter } from '@openchatlab/core'\nimport { streamParseFile, type ParsedMeta } from '@openchatlab/parser'\nimport { MessageType } from '@openchatlab/shared-types'\nimport { normalizeImportTimestamp, type SenderPlatformIdMapping } from './incremental-importer'\nimport { parsePlatformMessageId } from './message-deduplicator'\nimport type { ImportProgressCallback } from './streaming-importer'\n\nconst MATCH_WINDOW_SIZE = 5\n\nexport type AutoImportMatchMethod = 'source-session-id' | 'stable-id' | 'trailing-messages'\nexport type AutoImportCreateReason = 'no-match' | 'ambiguous'\n\nexport type AutoImportDecision =\n | {\n action: 'incremental'\n sessionId: string\n matchedBy: AutoImportMatchMethod\n platformMessageIdScope?: string\n senderPlatformIdMappings?: SenderPlatformIdMapping[]\n }\n | { action: 'create'; reason: AutoImportCreateReason }\n\nexport interface AutoImportTargetPlan {\n decision: AutoImportDecision\n concurrencyKey: string\n exclusive: boolean\n coalesceCreate: boolean\n}\n\nexport interface AutoImportMatcherDeps {\n listSessionIds(): string[]\n openReadonly(sessionId: string): DatabaseAdapter\n onProgress?: ImportProgressCallback\n}\n\nfunction buildPrivateIdentity(\n platform: string,\n ownerId: string | null | undefined,\n memberIds: Iterable<string>\n): string | null {\n const sortedIds = [...new Set(memberIds)].filter((id) => id && id.toLowerCase() !== 'system').sort()\n const validOwnerId = ownerId && ownerId.toLowerCase() !== 'system' ? ownerId : null\n if (validOwnerId) {\n return sortedIds.length > 0 ? `owner\\0${validOwnerId}\\0members\\0${sortedIds.join('\\0')}` : null\n }\n return platform === 'qq' && sortedIds.length >= 2 ? `members\\0${sortedIds.join('\\0')}` : null\n}\n\nfunction isBusinessMessage(type: number, content: string | null): boolean {\n return type !== MessageType.SYSTEM && type !== MessageType.RECALL && Boolean(content?.trim())\n}\n\nfunction addMessageWindow(\n recentKeys: string[],\n windows: Set<string>,\n recentPlatformIdKeys: string[],\n platformIdWindows: Set<string>,\n platformIdSenders: Map<string, string | null>,\n recentExactPlatformIdKeys: string[],\n exactPlatformIdWindows: Set<string>,\n exactPlatformIdSenders: Map<string, string | null>,\n message: {\n platformMessageId?: string\n timestamp: unknown\n senderPlatformId: string\n type: number\n content: string | null\n }\n): void {\n if (!message.senderPlatformId || !isBusinessMessage(message.type, message.content)) return\n const timestamp = normalizeImportTimestamp(message.timestamp)\n if (timestamp === null) return\n\n recentKeys.push(generateMessageKey(timestamp, message.senderPlatformId, message.content))\n if (recentKeys.length > MATCH_WINDOW_SIZE) recentKeys.shift()\n if (recentKeys.length === MATCH_WINDOW_SIZE) windows.add(recentKeys.join(''))\n\n const platformIdKey = generatePlatformMessageKey(message, timestamp)\n const exactPlatformIdKey = generatePlatformMessageKey(message, timestamp, false)\n if (!platformIdKey) {\n recentPlatformIdKeys.length = 0\n recentExactPlatformIdKeys.length = 0\n return\n }\n recordPlatformMessageSender(platformIdSenders, platformIdKey, message.senderPlatformId)\n recordPlatformMessageSender(exactPlatformIdSenders, exactPlatformIdKey ?? platformIdKey, message.senderPlatformId)\n recentPlatformIdKeys.push(platformIdKey)\n if (recentPlatformIdKeys.length > MATCH_WINDOW_SIZE) recentPlatformIdKeys.shift()\n if (recentPlatformIdKeys.length === MATCH_WINDOW_SIZE) platformIdWindows.add(recentPlatformIdKeys.join(''))\n\n recentExactPlatformIdKeys.push(exactPlatformIdKey ?? platformIdKey)\n if (recentExactPlatformIdKeys.length > MATCH_WINDOW_SIZE) recentExactPlatformIdKeys.shift()\n if (recentExactPlatformIdKeys.length === MATCH_WINDOW_SIZE) {\n exactPlatformIdWindows.add(recentExactPlatformIdKeys.join(''))\n }\n}\n\nfunction recordPlatformMessageSender(\n senders: Map<string, string | null>,\n messageKey: string,\n senderPlatformId: string\n): void {\n if (!senders.has(messageKey)) {\n senders.set(messageKey, senderPlatformId)\n } else if (senders.get(messageKey) !== senderPlatformId) {\n senders.set(messageKey, null)\n }\n}\n\nfunction isUsablePlatformId(id: string | null | undefined): id is string {\n return Boolean(id && id.toLowerCase() !== 'system')\n}\n\nfunction deriveSenderPlatformIdMappings(\n sourceMemberIds: Iterable<string>,\n targetMemberIds: Iterable<string>,\n sourceSenders: string[],\n targetSenders: string[],\n sourceOwnerId?: string | null,\n targetOwnerId?: string | null,\n allowSingleRemainingPair = false\n): SenderPlatformIdMapping[] | null {\n const sourceIds = new Set([...sourceMemberIds].filter(isUsablePlatformId))\n const targetIds = new Set([...targetMemberIds].filter(isUsablePlatformId))\n if (isUsablePlatformId(sourceOwnerId)) sourceIds.add(sourceOwnerId)\n if (isUsablePlatformId(targetOwnerId)) targetIds.add(targetOwnerId)\n\n const forward = new Map<string, string>()\n const reverse = new Map<string, string>()\n let hasDrift = false\n const addPair = (sourceId: string, targetId: string): boolean => {\n if (!sourceIds.has(sourceId) || !targetIds.has(targetId)) return false\n const existingTarget = forward.get(sourceId)\n const existingSource = reverse.get(targetId)\n if ((existingTarget && existingTarget !== targetId) || (existingSource && existingSource !== sourceId)) {\n return false\n }\n forward.set(sourceId, targetId)\n reverse.set(targetId, sourceId)\n hasDrift ||= sourceId !== targetId\n return true\n }\n\n if (\n isUsablePlatformId(sourceOwnerId) &&\n isUsablePlatformId(targetOwnerId) &&\n !addPair(sourceOwnerId, targetOwnerId)\n ) {\n return null\n }\n for (let index = 0; index < sourceSenders.length; index++) {\n if (!addPair(sourceSenders[index], targetSenders[index])) return null\n }\n\n if (!hasDrift) return []\n\n for (const sourceId of sourceIds) {\n if (!forward.has(sourceId) && targetIds.has(sourceId) && !reverse.has(sourceId)) {\n addPair(sourceId, sourceId)\n }\n }\n\n const remainingSourceIds = [...sourceIds].filter((id) => !forward.has(id))\n const remainingTargetIds = [...targetIds].filter((id) => !reverse.has(id))\n // In a two-party private chat, one proven participant leaves only one possible peer.\n // Group membership can change, so cardinality alone must not pair joined and departed members.\n if (allowSingleRemainingPair && remainingSourceIds.length === 1 && remainingTargetIds.length === 1) {\n addPair(remainingSourceIds[0], remainingTargetIds[0])\n }\n if ([...sourceIds].some((id) => !forward.has(id))) return null\n\n return [...forward]\n .filter(([sourceId, targetId]) => sourceId !== targetId)\n .map(([sourceId, targetId]) => ({ sourceId, targetId }))\n}\n\nfunction getCommonPlatformMessageIdScope(ids: Array<string | null>): string | undefined {\n let commonScope: string | undefined\n for (const id of ids) {\n if (!id) return undefined\n const { scope } = parsePlatformMessageId(id)\n if (!scope || (commonScope && scope !== commonScope)) return undefined\n commonScope = scope\n }\n return commonScope\n}\n\nfunction generatePlatformMessageKey(\n message: { platformMessageId?: string; type: number; content: string | null },\n timestamp: number,\n normalizeScope = true\n): string | null {\n if (!message.platformMessageId) return null\n const parsedId = parsePlatformMessageId(message.platformMessageId)\n return generateMessageKey(\n timestamp,\n normalizeScope ? parsedId.rawId : message.platformMessageId,\n JSON.stringify([message.type, message.content || null])\n )\n}\n\nexport async function resolveAutoImportTarget(\n filePath: string,\n deps: AutoImportMatcherDeps,\n formatOptions?: Record<string, unknown>\n): Promise<AutoImportDecision> {\n return (await resolveAutoImportTargetPlan(filePath, deps, formatOptions)).decision\n}\n\nexport async function resolveAutoImportTargetPlan(\n filePath: string,\n deps: AutoImportMatcherDeps,\n formatOptions?: Record<string, unknown>\n): Promise<AutoImportTargetPlan> {\n let sourceMeta: ParsedMeta | null = null\n const sourceMemberIds = new Set<string>()\n const sourceWindows = new Set<string>()\n const sourcePlatformIdWindows = new Set<string>()\n const sourcePlatformIdSenders = new Map<string, string | null>()\n const sourceExactPlatformIdWindows = new Set<string>()\n const sourceExactPlatformIdSenders = new Map<string, string | null>()\n const recentSourceKeys: string[] = []\n const recentSourcePlatformIdKeys: string[] = []\n const recentSourceExactPlatformIdKeys: string[] = []\n let sourceHasScopedPlatformMessageIds = false\n const { formatId, ...parserOptions } = formatOptions ?? {}\n\n await streamParseFile(\n filePath,\n {\n formatOptions: parserOptions,\n onProgress: deps.onProgress ?? (() => {}),\n onMeta: (meta) => {\n sourceMeta = meta\n },\n onMembers: (members) => {\n for (const member of members) sourceMemberIds.add(member.platformId)\n },\n onMessageBatch: (messages) => {\n for (const message of messages) {\n sourceHasScopedPlatformMessageIds ||= Boolean(\n (message.platformMessageId && parsePlatformMessageId(message.platformMessageId).scope) ||\n (message.replyToMessageId && parsePlatformMessageId(message.replyToMessageId).scope)\n )\n sourceMemberIds.add(message.senderPlatformId)\n addMessageWindow(\n recentSourceKeys,\n sourceWindows,\n recentSourcePlatformIdKeys,\n sourcePlatformIdWindows,\n sourcePlatformIdSenders,\n recentSourceExactPlatformIdKeys,\n sourceExactPlatformIdWindows,\n sourceExactPlatformIdSenders,\n message\n )\n }\n },\n },\n typeof formatId === 'string' ? formatId : undefined\n )\n\n if (!sourceMeta) throw new Error('Import source did not provide metadata')\n\n const meta = sourceMeta as ParsedMeta\n const sourceHasValidOwner = Boolean(meta.ownerId && meta.ownerId.toLowerCase() !== 'system')\n // QCE sources intentionally omit owner metadata because their UID/UIN namespaces\n // cannot be mapped reliably. Match those sources by participants even when an\n // owner profile has since populated the existing session's owner_id.\n const matchQqPrivateByMembers = meta.platform === 'qq' && meta.type === 'private' && !sourceHasValidOwner\n const privateIdentity =\n meta.type === 'private' ? buildPrivateIdentity(meta.platform, meta.ownerId, sourceMemberIds) : null\n const hasStableIdentity = Boolean(meta.groupId || privateIdentity)\n const stableIdentity = meta.groupId\n ? `group\\0${meta.groupId}`\n : privateIdentity\n ? `private\\0${privateIdentity}`\n : null\n const stableConcurrencyKey = stableIdentity\n ? `source:${createHash('sha256').update(`${meta.platform}\\0${meta.type}\\0${stableIdentity}`).digest('hex')}`\n : null\n\n const stableMatches: string[] = []\n const trailingMatches: Array<{\n sessionId: string\n platformMessageIdScope?: string\n senderPlatformIdMappings?: SenderPlatformIdMapping[]\n }> = []\n for (const sessionId of deps.listSessionIds()) {\n let db: DatabaseAdapter | null = null\n try {\n db = deps.openReadonly(sessionId)\n // Desktop 的数据库目录还可能包含配置库等非聊天 DB,匹配前统一排除。\n if (!isChatSessionDb(db)) continue\n\n const candidate = getSessionMeta(db)\n if (candidate?.platform !== meta.platform || candidate.type !== meta.type) continue\n\n const groupMatches = Boolean(meta.groupId && candidate.groupId === meta.groupId)\n const memberRows =\n privateIdentity || sourcePlatformIdWindows.size > 0\n ? (db.prepare(\"SELECT platform_id FROM member WHERE LOWER(platform_id) != 'system'\").all() as Array<{\n platform_id: string\n }>)\n : []\n const candidatePrivateIdentity = privateIdentity\n ? buildPrivateIdentity(\n candidate.platform,\n matchQqPrivateByMembers ? null : candidate.ownerId,\n memberRows.map((row) => row.platform_id)\n )\n : null\n\n if (groupMatches || (privateIdentity && candidatePrivateIdentity === privateIdentity)) {\n stableMatches.push(sessionId)\n }\n\n if (sourceWindows.size > 0 || sourcePlatformIdWindows.size > 0) {\n const rows = db\n .prepare(\n `SELECT msg.ts, member.platform_id, msg.type, msg.content, msg.platform_message_id\n FROM message msg\n JOIN member ON member.id = msg.sender_id\n WHERE msg.type NOT IN (?, ?)\n AND NULLIF(TRIM(msg.content), '') IS NOT NULL\n ORDER BY msg.ts DESC, msg.id DESC\n LIMIT ?`\n )\n .all(MessageType.SYSTEM, MessageType.RECALL, MATCH_WINDOW_SIZE) as Array<{\n ts: number\n platform_id: string\n type: number\n content: string\n platform_message_id: string | null\n }>\n\n if (rows.length === MATCH_WINDOW_SIZE) {\n const orderedRows = rows.reverse()\n const signature = orderedRows.map((row) => generateMessageKey(row.ts, row.platform_id, row.content)).join('')\n const platformIdKeys = orderedRows.map((row) =>\n generatePlatformMessageKey(\n {\n platformMessageId: row.platform_message_id || undefined,\n type: row.type,\n content: row.content,\n },\n row.ts\n )\n )\n const platformIdSignature = platformIdKeys.every((key): key is string => key !== null)\n ? platformIdKeys.join('')\n : null\n const exactPlatformIdKeys = orderedRows.map((row) =>\n generatePlatformMessageKey(\n {\n platformMessageId: row.platform_message_id || undefined,\n type: row.type,\n content: row.content,\n },\n row.ts,\n false\n )\n )\n const exactPlatformIdSignature = exactPlatformIdKeys.every((key): key is string => key !== null)\n ? exactPlatformIdKeys.join('')\n : null\n const platformIdsMatch = sourceHasScopedPlatformMessageIds\n ? exactPlatformIdSignature !== null && sourceExactPlatformIdWindows.has(exactPlatformIdSignature)\n : platformIdSignature !== null && sourcePlatformIdWindows.has(platformIdSignature)\n const contentMatches = !sourceHasScopedPlatformMessageIds && sourceWindows.has(signature)\n const matchedPlatformIdKeys = sourceHasScopedPlatformMessageIds ? exactPlatformIdKeys : platformIdKeys\n const sourceSenderLookup = sourceHasScopedPlatformMessageIds\n ? sourceExactPlatformIdSenders\n : sourcePlatformIdSenders\n const sourceSenders = platformIdsMatch\n ? matchedPlatformIdKeys.map((key) => (key ? sourceSenderLookup.get(key) : undefined))\n : []\n const senderPlatformIdMappings =\n platformIdsMatch && sourceSenders.every((sender): sender is string => Boolean(sender))\n ? deriveSenderPlatformIdMappings(\n sourceMemberIds,\n memberRows.map((row) => row.platform_id),\n sourceSenders,\n orderedRows.map((row) => row.platform_id),\n meta.ownerId,\n candidate.ownerId,\n meta.type === 'private'\n )\n : null\n const safePlatformIdsMatch = platformIdsMatch && senderPlatformIdMappings !== null\n if (contentMatches || safePlatformIdsMatch) {\n const platformMessageIdScope = sourceHasScopedPlatformMessageIds\n ? undefined\n : getCommonPlatformMessageIdScope(orderedRows.map((row) => row.platform_message_id))\n trailingMatches.push({\n sessionId,\n ...(platformMessageIdScope ? { platformMessageIdScope } : {}),\n ...(safePlatformIdsMatch && senderPlatformIdMappings.length > 0 ? { senderPlatformIdMappings } : {}),\n })\n }\n }\n }\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error)\n throw new Error(`Failed to inspect import candidate ${JSON.stringify(sessionId)}: ${detail}`, {\n cause: error,\n })\n } finally {\n db?.close()\n }\n }\n\n if (hasStableIdentity) {\n if (meta.sourceSessionId && stableMatches.includes(meta.sourceSessionId)) {\n const trailingMatch = trailingMatches.find((match) => match.sessionId === meta.sourceSessionId)\n return {\n decision: {\n action: 'incremental',\n sessionId: meta.sourceSessionId,\n matchedBy: 'source-session-id',\n ...(trailingMatch?.platformMessageIdScope\n ? { platformMessageIdScope: trailingMatch.platformMessageIdScope }\n : {}),\n ...(trailingMatch?.senderPlatformIdMappings\n ? { senderPlatformIdMappings: trailingMatch.senderPlatformIdMappings }\n : {}),\n },\n concurrencyKey: `session:${meta.sourceSessionId}`,\n exclusive: false,\n coalesceCreate: false,\n }\n }\n if (stableMatches.length === 1) {\n const trailingMatch = trailingMatches.find((match) => match.sessionId === stableMatches[0])\n return {\n decision: {\n action: 'incremental',\n sessionId: stableMatches[0],\n matchedBy: 'stable-id',\n ...(trailingMatch?.platformMessageIdScope\n ? { platformMessageIdScope: trailingMatch.platformMessageIdScope }\n : {}),\n ...(trailingMatch?.senderPlatformIdMappings\n ? { senderPlatformIdMappings: trailingMatch.senderPlatformIdMappings }\n : {}),\n },\n concurrencyKey: `session:${stableMatches[0]}`,\n exclusive: false,\n coalesceCreate: false,\n }\n }\n }\n\n // 稳定身份缺失、漂移或产生多个候选时,仍只接受唯一的 5 条连续消息重叠。\n const sourceSessionTrailingMatch = meta.sourceSessionId\n ? trailingMatches.find((match) => match.sessionId === meta.sourceSessionId)\n : undefined\n if (meta.sourceSessionId && sourceSessionTrailingMatch) {\n return {\n decision: {\n action: 'incremental',\n sessionId: meta.sourceSessionId,\n matchedBy: 'source-session-id',\n ...(sourceSessionTrailingMatch.platformMessageIdScope\n ? { platformMessageIdScope: sourceSessionTrailingMatch.platformMessageIdScope }\n : {}),\n ...(sourceSessionTrailingMatch.senderPlatformIdMappings\n ? { senderPlatformIdMappings: sourceSessionTrailingMatch.senderPlatformIdMappings }\n : {}),\n },\n concurrencyKey: `session:${meta.sourceSessionId}`,\n exclusive: false,\n coalesceCreate: false,\n }\n }\n if (trailingMatches.length === 1) {\n const [trailingMatch] = trailingMatches\n return {\n decision: {\n action: 'incremental',\n sessionId: trailingMatch.sessionId,\n matchedBy: 'trailing-messages',\n ...(trailingMatch.platformMessageIdScope\n ? { platformMessageIdScope: trailingMatch.platformMessageIdScope }\n : {}),\n ...(trailingMatch.senderPlatformIdMappings\n ? { senderPlatformIdMappings: trailingMatch.senderPlatformIdMappings }\n : {}),\n },\n concurrencyKey: `session:${trailingMatch.sessionId}`,\n exclusive: false,\n coalesceCreate: false,\n }\n }\n const ambiguous = stableMatches.length > 1 || trailingMatches.length > 1\n const decision: AutoImportDecision = { action: 'create', reason: ambiguous ? 'ambiguous' : 'no-match' }\n if (!ambiguous && stableConcurrencyKey) {\n return {\n decision,\n concurrencyKey: stableConcurrencyKey,\n exclusive: false,\n coalesceCreate: true,\n }\n }\n return {\n decision,\n concurrencyKey: 'unresolved',\n exclusive: true,\n coalesceCreate: false,\n }\n}\n","import { appLogger } from '../logging/app-logger'\nimport {\n resolveAutoImportTarget,\n type AutoImportDecision,\n type AutoImportCreateReason,\n type AutoImportMatcherDeps,\n type AutoImportMatchMethod,\n} from './auto-import-matcher'\nimport type { IncrementalAnalyzeResult, IncrementalImportResult, SenderPlatformIdMapping } from './incremental-importer'\nimport type {\n AnalyzeNewImportResult,\n ImportDiagnostics,\n ImportProgressCallback,\n StreamImportResult,\n} from './streaming-importer'\nimport { isValidImportSessionId } from './session-id'\n\nexport interface AutoImportOptions {\n explicitSessionId?: string\n formatOptions?: Record<string, unknown>\n /** Pre-resolved by the batch coordinator while the data directory is read-only. */\n resolvedDecision?: AutoImportDecision\n}\n\nexport interface AutoImportAppendContext {\n platformMessageIdScope?: string\n senderPlatformIdMappings?: SenderPlatformIdMapping[]\n}\n\nexport interface AutoImportDeps extends AutoImportMatcherDeps {\n sessionExists(sessionId: string): boolean\n createSession(\n filePath: string,\n formatOptions?: Record<string, unknown>,\n sessionId?: string,\n onProgress?: ImportProgressCallback\n ): Promise<StreamImportResult>\n appendSession(\n sessionId: string,\n filePath: string,\n formatOptions?: Record<string, unknown>,\n onProgress?: ImportProgressCallback,\n context?: AutoImportAppendContext\n ): Promise<IncrementalImportResult>\n resolveTarget?: typeof resolveAutoImportTarget\n}\n\nexport interface AutoImportAnalysisDeps extends AutoImportMatcherDeps {\n sessionExists(sessionId: string): boolean\n analyzeCreateSession(filePath: string, formatOptions?: Record<string, unknown>): Promise<AnalyzeNewImportResult>\n analyzeAppendSession(\n sessionId: string,\n filePath: string,\n formatOptions?: Record<string, unknown>,\n context?: AutoImportAppendContext\n ): Promise<IncrementalAnalyzeResult>\n resolveTarget?: typeof resolveAutoImportTarget\n}\n\nexport interface AutoImportResult {\n success: boolean\n sessionId?: string\n platform?: string\n importMode?: 'created' | 'incremental'\n matchedBy?: AutoImportMatchMethod\n createReason?: AutoImportCreateReason\n newMessageCount?: number\n duplicateCount?: number\n batch?:\n | NonNullable<IncrementalImportResult['batch']>\n | { receivedCount: number; writtenCount: number; duplicateCount: number }\n session?: IncrementalImportResult['session']\n updates?: IncrementalImportResult['updates']\n diagnostics?: ImportDiagnostics\n error?: string\n}\n\nexport interface AutoImportAnalysisResult {\n success: boolean\n importMode?: 'created' | 'incremental'\n sessionId?: string\n matchedBy?: AutoImportMatchMethod\n createReason?: AutoImportCreateReason\n totalMessageCount?: number\n newMessageCount?: number\n duplicateCount?: number\n totalMemberCount?: number\n meta?: AnalyzeNewImportResult['meta']\n error?: string\n}\n\ntype AutoImportPlan =\n | {\n action: 'incremental'\n sessionId: string\n matchedBy?: AutoImportMatchMethod\n platformMessageIdScope?: string\n senderPlatformIdMappings?: SenderPlatformIdMapping[]\n }\n | { action: 'create'; sessionId?: string; reason?: AutoImportCreateReason }\n\nasync function planAutoImport(\n filePath: string,\n deps: Pick<AutoImportDeps, 'listSessionIds' | 'openReadonly' | 'onProgress' | 'sessionExists' | 'resolveTarget'>,\n options: AutoImportOptions\n): Promise<AutoImportPlan> {\n if (options.resolvedDecision) {\n const decision = options.resolvedDecision\n return decision.action === 'incremental'\n ? {\n action: 'incremental',\n sessionId: decision.sessionId,\n matchedBy: decision.matchedBy,\n ...(decision.platformMessageIdScope ? { platformMessageIdScope: decision.platformMessageIdScope } : {}),\n ...(decision.senderPlatformIdMappings ? { senderPlatformIdMappings: decision.senderPlatformIdMappings } : {}),\n }\n : { action: 'create', reason: decision.reason }\n }\n\n if (options.explicitSessionId) {\n if (!isValidImportSessionId(options.explicitSessionId)) {\n throw new Error('sessionId contains invalid characters')\n }\n return deps.sessionExists(options.explicitSessionId)\n ? { action: 'incremental', sessionId: options.explicitSessionId }\n : { action: 'create', sessionId: options.explicitSessionId }\n }\n\n const decision: AutoImportDecision = await (deps.resolveTarget ?? resolveAutoImportTarget)(\n filePath,\n deps,\n options.formatOptions\n )\n return decision.action === 'incremental'\n ? {\n action: 'incremental',\n sessionId: decision.sessionId,\n matchedBy: decision.matchedBy,\n ...(decision.platformMessageIdScope ? { platformMessageIdScope: decision.platformMessageIdScope } : {}),\n ...(decision.senderPlatformIdMappings ? { senderPlatformIdMappings: decision.senderPlatformIdMappings } : {}),\n }\n : { action: 'create', reason: decision.reason }\n}\n\nfunction mapCreateResult(result: StreamImportResult, createReason?: AutoImportCreateReason): AutoImportResult {\n if (!result.success || !result.sessionId) {\n return {\n success: false,\n ...(result.platform !== undefined ? { platform: result.platform } : {}),\n error: result.error,\n diagnostics: result.diagnostics,\n }\n }\n return {\n success: true,\n sessionId: result.sessionId,\n platform: result.platform,\n importMode: 'created',\n ...(createReason ? { createReason } : {}),\n newMessageCount: result.diagnostics?.messagesWritten ?? 0,\n duplicateCount: result.diagnostics?.duplicateCount ?? 0,\n batch: result.diagnostics\n ? {\n receivedCount: result.diagnostics.messagesReceived,\n writtenCount: result.diagnostics.messagesWritten,\n duplicateCount: result.diagnostics.duplicateCount,\n }\n : undefined,\n diagnostics: result.diagnostics,\n }\n}\n\nfunction mapIncrementalResult(\n sessionId: string,\n result: IncrementalImportResult,\n matchedBy?: AutoImportMatchMethod\n): AutoImportResult {\n const incrementalResult = {\n sessionId,\n importMode: 'incremental' as const,\n ...(matchedBy ? { matchedBy } : {}),\n }\n if (!result.success) return { success: false, ...incrementalResult, error: result.error }\n return {\n success: true,\n ...incrementalResult,\n newMessageCount: result.newMessageCount,\n duplicateCount: result.batch?.duplicateCount ?? 0,\n batch: result.batch,\n session: result.session,\n updates: result.updates,\n }\n}\n\nexport async function autoImportFile(\n filePath: string,\n deps: AutoImportDeps,\n options: AutoImportOptions = {}\n): Promise<AutoImportResult> {\n let plan: AutoImportPlan | undefined\n try {\n if (!options.explicitSessionId) {\n appLogger.info('import', 'Automatic session matching started', {\n candidateCount: deps.listSessionIds().length,\n })\n }\n plan = await planAutoImport(filePath, deps, options)\n\n if (plan.action === 'incremental') {\n const result = mapIncrementalResult(\n plan.sessionId,\n await deps.appendSession(plan.sessionId, filePath, options.formatOptions, deps.onProgress, {\n platformMessageIdScope: plan.platformMessageIdScope,\n senderPlatformIdMappings: plan.senderPlatformIdMappings,\n }),\n plan.matchedBy\n )\n appLogger.info('import', 'Incremental import completed', {\n sessionId: plan.sessionId,\n matchedBy: plan.matchedBy,\n success: result.success,\n newMessageCount: result.newMessageCount,\n duplicateCount: result.duplicateCount,\n })\n return result\n }\n\n const result = mapCreateResult(\n await deps.createSession(filePath, options.formatOptions, plan.sessionId, deps.onProgress),\n plan.reason\n )\n appLogger.info('import', 'Import created a new session', {\n reason: plan.reason,\n sessionId: result.sessionId,\n success: result.success,\n })\n return result\n } catch (error) {\n appLogger.error('import', 'Automatic import failed', error)\n return {\n success: false,\n ...(plan?.action === 'incremental'\n ? {\n sessionId: plan.sessionId,\n importMode: 'incremental' as const,\n ...(plan.matchedBy ? { matchedBy: plan.matchedBy } : {}),\n }\n : {}),\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\nexport async function analyzeAutoImportFile(\n filePath: string,\n deps: AutoImportAnalysisDeps,\n options: AutoImportOptions = {}\n): Promise<AutoImportAnalysisResult> {\n try {\n const plan = await planAutoImport(filePath, deps, options)\n\n if (plan.action === 'incremental') {\n const analysis = await deps.analyzeAppendSession(plan.sessionId, filePath, options.formatOptions, {\n platformMessageIdScope: plan.platformMessageIdScope,\n senderPlatformIdMappings: plan.senderPlatformIdMappings,\n })\n if (analysis.error) return { success: false, error: analysis.error }\n const result: AutoImportAnalysisResult = {\n success: true,\n importMode: 'incremental',\n sessionId: plan.sessionId,\n ...(plan.matchedBy ? { matchedBy: plan.matchedBy } : {}),\n totalMessageCount: analysis.totalInFile,\n newMessageCount: analysis.newMessageCount,\n duplicateCount: analysis.duplicateCount,\n }\n appLogger.info('import', 'Incremental import analysis completed', {\n sessionId: plan.sessionId,\n matchedBy: plan.matchedBy,\n newMessageCount: analysis.newMessageCount,\n duplicateCount: analysis.duplicateCount,\n })\n return result\n }\n\n const analysis = await deps.analyzeCreateSession(filePath, options.formatOptions)\n if (analysis.error) return { success: false, error: analysis.error }\n const result: AutoImportAnalysisResult = {\n success: true,\n importMode: 'created',\n ...(plan.sessionId ? { sessionId: plan.sessionId } : {}),\n ...(plan.reason ? { createReason: plan.reason } : {}),\n totalMessageCount: analysis.totalMessages,\n newMessageCount: analysis.newMessageCount,\n duplicateCount: analysis.duplicateCount,\n totalMemberCount: analysis.totalMembers,\n meta: analysis.meta,\n }\n appLogger.info('import', 'New session import analysis completed', {\n reason: plan.reason,\n messageCount: analysis.totalMessages,\n memberCount: analysis.totalMembers,\n })\n return result\n } catch (error) {\n appLogger.error('import', 'Automatic import analysis failed', error)\n return { success: false, error: error instanceof Error ? error.message : String(error) }\n }\n}\n","export interface KeyedBatchTask<T> {\n value: T\n key: string\n /**\n * Exclusive tasks form a barrier: they start only after all earlier work\n * finishes, and no later work starts until they complete.\n */\n exclusive?: boolean\n}\n\nexport type KeyedBatchTaskResult<T> =\n | { status: 'success'; value: T }\n | { status: 'failed'; error: string }\n | { status: 'cancelled' }\n\nexport interface KeyedBatchOptions<TInput, TOutput> {\n concurrency?: number\n signal?: AbortSignal\n run(task: TInput, index: number): Promise<TOutput>\n onTaskStart?: (task: TInput, index: number) => void\n onTaskComplete?: (task: TInput, index: number, result: KeyedBatchTaskResult<TOutput>) => void\n}\n\nexport function normalizeBatchConcurrency(value: unknown, taskCount: number): number {\n if (taskCount <= 0) return 0\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) return 1\n return Math.min(value, taskCount)\n}\n\nexport function resolveDefaultBatchConcurrency(\n taskCount: number,\n resources: { cpuCount?: number; totalMemoryBytes?: number } = {}\n): number {\n if (taskCount <= 0) return 0\n const cpuCount = resources.cpuCount ?? cpus().length\n const totalMemoryBytes = resources.totalMemoryBytes ?? totalmem()\n const lowResourceRuntime = cpuCount < 4 || totalMemoryBytes < 4 * 1024 * 1024 * 1024\n return Math.min(taskCount, lowResourceRuntime ? 1 : 2)\n}\n\n/**\n * Run keyed work with bounded concurrency while serializing identical keys.\n * Exclusive tasks are ordering barriers for work whose target cannot be\n * resolved safely before writes begin.\n */\nexport async function runKeyedBatch<TInput, TOutput>(\n tasks: Array<KeyedBatchTask<TInput>>,\n options: KeyedBatchOptions<TInput, TOutput>\n): Promise<Array<KeyedBatchTaskResult<TOutput>>> {\n if (tasks.length === 0) return []\n\n const concurrency = normalizeBatchConcurrency(options.concurrency, tasks.length)\n const results: Array<KeyedBatchTaskResult<TOutput> | undefined> = Array(tasks.length)\n const pending = new Set(tasks.map((_, index) => index))\n const activeKeys = new Set<string>()\n let activeCount = 0\n let exclusiveActive = false\n\n return new Promise((resolve) => {\n const finishIfDone = () => {\n if (activeCount !== 0 || pending.size !== 0) return false\n options.signal?.removeEventListener('abort', schedule)\n resolve(results as Array<KeyedBatchTaskResult<TOutput>>)\n return true\n }\n\n const cancelPending = () => {\n if (!options.signal?.aborted) return\n for (const index of pending) {\n pending.delete(index)\n const result = { status: 'cancelled' as const }\n results[index] = result\n options.onTaskComplete?.(tasks[index].value, index, result)\n }\n }\n\n const findRunnableIndex = (): number | undefined => {\n if (exclusiveActive) return undefined\n for (const index of pending) {\n const task = tasks[index]\n if (task.exclusive) return activeCount === 0 ? index : undefined\n if (!activeKeys.has(task.key)) return index\n }\n return undefined\n }\n\n function schedule(): void {\n cancelPending()\n if (finishIfDone()) return\n\n while (activeCount < concurrency && !options.signal?.aborted) {\n const index = findRunnableIndex()\n if (index === undefined) break\n\n const task = tasks[index]\n pending.delete(index)\n activeCount++\n activeKeys.add(task.key)\n if (task.exclusive) exclusiveActive = true\n options.onTaskStart?.(task.value, index)\n\n void Promise.resolve()\n .then(() => options.run(task.value, index))\n .then(\n (value) => ({ status: 'success' as const, value }),\n (error) => ({\n status: 'failed' as const,\n error: error instanceof Error ? error.message : String(error),\n })\n )\n .then((result) => {\n results[index] = result\n options.onTaskComplete?.(task.value, index, result)\n })\n .finally(() => {\n activeCount--\n activeKeys.delete(task.key)\n if (task.exclusive) exclusiveActive = false\n schedule()\n })\n }\n }\n\n options.signal?.addEventListener('abort', schedule, { once: true })\n schedule()\n })\n}\nimport { cpus, totalmem } from 'node:os'\n","import { resolveAutoImportTargetPlan, type AutoImportDecision, type AutoImportTargetPlan } from './auto-import-matcher'\nimport { autoImportFile, type AutoImportDeps, type AutoImportOptions, type AutoImportResult } from './auto-importer'\nimport { normalizeBatchConcurrency, runKeyedBatch, type KeyedBatchTaskResult } from './batch-coordinator'\nimport type { ImportProgressCallback } from './streaming-importer'\nimport { appLogger } from '../logging/app-logger'\n\nexport interface AutoImportBatchItem {\n id: string\n filePath: string\n options?: AutoImportOptions\n}\n\nexport type AutoImportBatchItemResult =\n | { id: string; status: 'success'; result: AutoImportResult }\n | { id: string; status: 'failed'; error: string; result?: AutoImportResult }\n | { id: string; status: 'cancelled' }\n\nexport interface AutoImportBatchOptions {\n concurrency?: number\n signal?: AbortSignal\n onItemStart?: (item: AutoImportBatchItem, index: number) => void\n onItemProgress?: (item: AutoImportBatchItem, index: number, progress: Parameters<ImportProgressCallback>[0]) => void\n onItemComplete?: (item: AutoImportBatchItem, index: number, result: AutoImportBatchItemResult) => void\n resolveTargetPlan?: typeof resolveAutoImportTargetPlan\n}\n\ninterface PlannedItem {\n item: AutoImportBatchItem\n itemIndex: number\n plan: AutoImportTargetPlan\n}\n\nfunction mapTaskResult(\n item: AutoImportBatchItem,\n result: KeyedBatchTaskResult<AutoImportResult>\n): AutoImportBatchItemResult {\n if (result.status === 'cancelled') return { id: item.id, status: 'cancelled' }\n if (result.status === 'failed') return { id: item.id, status: 'failed', error: result.error }\n if (!result.value.success) {\n return {\n id: item.id,\n status: 'failed',\n error: result.value.error ?? 'Import failed',\n result: result.value,\n }\n }\n return { id: item.id, status: 'success', result: result.value }\n}\n\nfunction preserveSerialTargetMatching(planned: PlannedItem[]): void {\n let priorPlannedWrite = false\n\n for (const plannedItem of planned) {\n let plan = plannedItem.plan\n if (priorPlannedWrite && !plannedItem.item.options?.explicitSessionId && !plan.exclusive) {\n plan = {\n ...plan,\n concurrencyKey: `unresolved:${plannedItem.itemIndex}`,\n exclusive: true,\n coalesceCreate: false,\n }\n }\n\n plannedItem.plan = plan\n priorPlannedWrite = true\n }\n}\n\nexport async function autoImportBatch(\n items: AutoImportBatchItem[],\n deps: AutoImportDeps,\n options: AutoImportBatchOptions = {}\n): Promise<AutoImportBatchItemResult[]> {\n if (items.length === 0) return []\n\n const resolvePlan = options.resolveTargetPlan ?? resolveAutoImportTargetPlan\n const concurrency = normalizeBatchConcurrency(options.concurrency, items.length)\n appLogger.info('import-batch', 'Batch import started', {\n itemCount: items.length,\n concurrency,\n })\n const preflight = await runKeyedBatch(\n items.map((item, index) => ({ value: { item, itemIndex: index }, key: `preflight:${index}` })),\n {\n concurrency,\n signal: options.signal,\n onTaskStart: ({ item, itemIndex }) => options.onItemStart?.(item, itemIndex),\n run: async ({ item, itemIndex }) => {\n const itemOptions = item.options ?? {}\n if (itemOptions.explicitSessionId) {\n const sessionId = itemOptions.explicitSessionId\n const decision: AutoImportDecision = deps.sessionExists(sessionId)\n ? { action: 'incremental', sessionId, matchedBy: 'source-session-id' }\n : { action: 'create', reason: 'no-match' }\n return {\n item,\n itemIndex,\n plan: {\n decision,\n concurrencyKey: `session:${sessionId}`,\n exclusive: false,\n coalesceCreate: decision.action === 'create',\n },\n } satisfies PlannedItem\n }\n return {\n item,\n itemIndex,\n plan: await resolvePlan(\n item.filePath,\n {\n ...deps,\n onProgress: (progress) => options.onItemProgress?.(item, itemIndex, progress),\n },\n itemOptions.formatOptions\n ),\n } satisfies PlannedItem\n },\n }\n )\n\n const finalResults: Array<AutoImportBatchItemResult | undefined> = Array(items.length)\n const planned: PlannedItem[] = []\n for (let index = 0; index < preflight.length; index++) {\n const result = preflight[index]\n if (result.status === 'success') {\n planned.push(result.value)\n continue\n }\n const mapped: AutoImportBatchItemResult =\n result.status === 'cancelled'\n ? { id: items[index].id, status: 'cancelled' }\n : { id: items[index].id, status: 'failed', error: result.error }\n finalResults[index] = mapped\n options.onItemComplete?.(items[index], index, mapped)\n }\n\n preserveSerialTargetMatching(planned)\n const targetSessionByKey = new Map<string, string>()\n await runKeyedBatch<PlannedItem, AutoImportResult>(\n planned.map((plannedItem) => ({\n value: plannedItem,\n key: plannedItem.plan.concurrencyKey,\n exclusive: plannedItem.plan.exclusive,\n })),\n {\n concurrency,\n signal: options.signal,\n onTaskComplete: ({ item, itemIndex }, _plannedIndex, taskResult) => {\n const mapped = mapTaskResult(item, taskResult)\n finalResults[itemIndex] = mapped\n options.onItemComplete?.(item, itemIndex, mapped)\n },\n run: async ({ item, itemIndex, plan }) => {\n const targetSessionId = plan.coalesceCreate ? targetSessionByKey.get(plan.concurrencyKey) : undefined\n const decision: AutoImportDecision = targetSessionId\n ? { action: 'incremental', sessionId: targetSessionId, matchedBy: 'trailing-messages' }\n : plan.decision\n const result = await autoImportFile(\n item.filePath,\n {\n ...deps,\n onProgress: (progress) => options.onItemProgress?.(item, itemIndex, progress),\n },\n {\n ...item.options,\n resolvedDecision:\n plan.exclusive || (item.options?.explicitSessionId && !targetSessionId) ? undefined : decision,\n }\n )\n if (!plan.exclusive && result.success && result.sessionId) {\n targetSessionByKey.set(plan.concurrencyKey, result.sessionId)\n }\n return result\n },\n }\n )\n\n const completedResults = finalResults as AutoImportBatchItemResult[]\n appLogger.info('import-batch', 'Batch import completed', {\n itemCount: items.length,\n successCount: completedResults.filter((result) => result.status === 'success').length,\n failedCount: completedResults.filter((result) => result.status === 'failed').length,\n cancelledCount: completedResults.filter((result) => result.status === 'cancelled').length,\n })\n return completedResults\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { randomUUID } from 'node:crypto'\nimport { appLogger } from '../logging/app-logger'\n\nexport const IMPORT_LOCK_FILENAME = '.chatlab-import.lock'\nexport const IMPORT_IN_PROGRESS_ERROR_KEY = 'error.import_in_progress'\n\ninterface ImportLockOwner {\n pid: number\n token: string\n startedAt: number\n}\n\nexport class ImportInProgressError extends Error {\n readonly code = 'IMPORT_IN_PROGRESS'\n readonly errorKey = IMPORT_IN_PROGRESS_ERROR_KEY\n\n constructor() {\n super('Another import is already in progress')\n this.name = 'ImportInProgressError'\n }\n}\n\nfunction parseOwner(lockPath: string): ImportLockOwner | null {\n try {\n const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf-8')) as Partial<ImportLockOwner>\n if (\n typeof parsed.pid !== 'number' ||\n !Number.isInteger(parsed.pid) ||\n parsed.pid <= 0 ||\n typeof parsed.token !== 'string' ||\n parsed.token.length === 0 ||\n typeof parsed.startedAt !== 'number'\n ) {\n return null\n }\n return parsed as ImportLockOwner\n } catch {\n return null\n }\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code !== 'ESRCH'\n }\n}\n\nfunction removeStaleLock(lockPath: string): boolean {\n const owner = parseOwner(lockPath)\n if (owner) {\n if (isProcessRunning(owner.pid)) return false\n } else {\n try {\n // A competing process may have created the file but not written its owner yet.\n // Only treat malformed lock files as stale after that short creation window.\n if (Date.now() - fs.statSync(lockPath).mtimeMs < 5_000) return false\n } catch {\n return true\n }\n }\n\n try {\n fs.unlinkSync(lockPath)\n appLogger.warn('import-lock', 'Removed stale import lock', { ownerPid: owner?.pid })\n return true\n } catch {\n return false\n }\n}\n\nfunction releaseLock(lockPath: string, fd: number, owner: ImportLockOwner): void {\n try {\n fs.closeSync(fd)\n } catch (error) {\n appLogger.error('import-lock', 'Failed to close import lock', error)\n }\n\n try {\n if (parseOwner(lockPath)?.token !== owner.token) {\n appLogger.warn('import-lock', 'Import lock ownership changed before release')\n return\n }\n fs.unlinkSync(lockPath)\n appLogger.info('import-lock', 'Import lock released')\n } catch (error) {\n appLogger.error('import-lock', 'Failed to release import lock', error)\n }\n}\n\nexport async function withDataDirImportLock<T>(userDataDir: string, task: () => Promise<T>): Promise<T> {\n fs.mkdirSync(userDataDir, { recursive: true })\n const lockPath = path.join(userDataDir, IMPORT_LOCK_FILENAME)\n\n let fd: number | null = null\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n fd = fs.openSync(lockPath, 'wx')\n break\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code\n if (code === 'EEXIST' && attempt === 0 && removeStaleLock(lockPath)) continue\n if (code === 'EEXIST') {\n appLogger.warn('import-lock', 'Rejected concurrent import')\n throw new ImportInProgressError()\n }\n throw error\n }\n }\n\n if (fd === null) throw new ImportInProgressError()\n\n const owner: ImportLockOwner = {\n pid: process.pid,\n token: randomUUID(),\n startedAt: Date.now(),\n }\n\n try {\n fs.writeFileSync(fd, JSON.stringify(owner), 'utf-8')\n fs.fsyncSync(fd)\n appLogger.info('import-lock', 'Import lock acquired')\n return await task()\n } finally {\n releaseLock(lockPath, fd, owner)\n }\n}\n","export type ArchiveImportErrorCode =\n | 'error.archive_unsupported'\n | 'error.archive_encrypted'\n | 'error.archive_corrupt'\n | 'error.archive_unsafe_path'\n | 'error.archive_limit_exceeded'\n | 'error.google_chat_structure_not_found'\n | 'error.google_chat_conversation_incomplete'\n | 'error.import_source_not_found'\n | 'error.import_source_expired'\n\nexport class ArchiveImportError extends Error {\n constructor(\n readonly code: ArchiveImportErrorCode,\n message: string,\n options?: ErrorOptions\n ) {\n super(message, options)\n this.name = 'ArchiveImportError'\n }\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { pipeline } from 'node:stream/promises'\nimport type { Entry, ZipFile } from 'yauzl'\nimport { openPromise } from 'yauzl'\nimport { ArchiveImportError } from './errors'\nimport type {\n ArchiveEntryStreamOpener,\n ArchiveEntrySummary,\n ArchiveEntryVisitor,\n ZipArchiveReaderOptions,\n} from './types'\n\nconst DEFAULT_MAX_ENTRIES = 100_000\nconst DEFAULT_MAX_ENTRY_BYTES = 20 * 1024 * 1024 * 1024\nconst DEFAULT_MAX_COMPRESSION_RATIO = 10_000\n\nexport function validateArchiveEntryName(fileName: string): void {\n if (\n fileName.startsWith('/') ||\n /^[A-Za-z]:\\//.test(fileName) ||\n fileName.includes('\\\\') ||\n fileName.split('/').includes('..')\n ) {\n throw new ArchiveImportError('error.archive_unsafe_path', `Unsafe archive entry path: ${fileName}`)\n }\n}\n\nfunction mapArchiveError(error: unknown): ArchiveImportError {\n if (error instanceof ArchiveImportError) return error\n const message = error instanceof Error ? error.message : String(error)\n return new ArchiveImportError('error.archive_corrupt', `Invalid ZIP archive: ${message}`, {\n cause: error,\n })\n}\n\nfunction toSummary(entry: Entry): ArchiveEntrySummary {\n return {\n name: entry.fileName,\n compressedSize: entry.compressedSize,\n uncompressedSize: entry.uncompressedSize,\n compressionMethod: entry.compressionMethod,\n isDirectory: entry.fileName.endsWith('/'),\n }\n}\n\n/**\n * 对 ZIP 中央目录和条目元数据做统一校验。所有调用方必须经过这里,\n * 防止 resolver 在看到恶意路径或异常压缩比后继续处理。\n */\nfunction validateEntry(entry: Entry, options: Required<ZipArchiveReaderOptions>): void {\n validateArchiveEntryName(entry.fileName)\n\n if (entry.isEncrypted()) {\n throw new ArchiveImportError('error.archive_encrypted', `Encrypted ZIP entry is not supported: ${entry.fileName}`)\n }\n if (!entry.canDecodeFileData() || (entry.compressionMethod !== 0 && entry.compressionMethod !== 8)) {\n throw new ArchiveImportError(\n 'error.archive_unsupported',\n `Unsupported ZIP compression method for entry: ${entry.fileName}`\n )\n }\n if (entry.uncompressedSize > options.maxEntryBytes) {\n throw new ArchiveImportError('error.archive_limit_exceeded', `ZIP entry exceeds the size limit: ${entry.fileName}`)\n }\n\n if (entry.uncompressedSize > 0) {\n const ratio = entry.compressedSize === 0 ? Number.POSITIVE_INFINITY : entry.uncompressedSize / entry.compressedSize\n if (ratio > options.maxCompressionRatio) {\n throw new ArchiveImportError(\n 'error.archive_limit_exceeded',\n `ZIP entry exceeds the compression ratio limit: ${entry.fileName}`\n )\n }\n }\n}\n\nexport class ZipArchiveReader {\n private readonly options: Required<ZipArchiveReaderOptions>\n\n constructor(\n private readonly archivePath: string,\n options: ZipArchiveReaderOptions = {}\n ) {\n this.options = {\n maxEntries: options.maxEntries ?? DEFAULT_MAX_ENTRIES,\n maxEntryBytes: options.maxEntryBytes ?? DEFAULT_MAX_ENTRY_BYTES,\n maxCompressionRatio: options.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO,\n }\n }\n\n private async open(): Promise<ZipFile> {\n try {\n return await openPromise(this.archivePath, {\n autoClose: true,\n decodeStrings: true,\n validateEntrySizes: true,\n strictFileNames: true,\n })\n } catch (error) {\n throw mapArchiveError(error)\n }\n }\n\n async listEntries(): Promise<ArchiveEntrySummary[]> {\n const entries: ArchiveEntrySummary[] = []\n await this.visitEntries((entry) => {\n entries.push(entry)\n })\n return entries\n }\n\n /**\n * 每次只推进一个 ZIP 条目;visitor 若打开流,必须等待流消费结束后返回。\n * 这样大 ZIP 不会同时创建大量 inflate stream。\n */\n async visitEntries(visitor: ArchiveEntryVisitor): Promise<void> {\n const zipFile = await this.open()\n let count = 0\n try {\n for await (const entry of zipFile.eachEntry()) {\n count++\n if (count > this.options.maxEntries) {\n throw new ArchiveImportError('error.archive_limit_exceeded', 'ZIP archive contains too many entries')\n }\n validateEntry(entry, this.options)\n\n let opened = false\n const openStream: ArchiveEntryStreamOpener = async () => {\n if (opened) {\n throw new ArchiveImportError('error.archive_corrupt', `ZIP entry stream opened twice: ${entry.fileName}`)\n }\n opened = true\n try {\n return await zipFile.openReadStreamPromise(entry)\n } catch (error) {\n throw mapArchiveError(error)\n }\n }\n await visitor(toSummary(entry), openStream)\n }\n } catch (error) {\n throw mapArchiveError(error)\n } finally {\n if (zipFile.isOpen) zipFile.close()\n }\n }\n\n async pipeEntries(targets: ReadonlyMap<string, string>): Promise<void> {\n const remaining = new Set(targets.keys())\n await this.visitEntries(async (entry, openStream) => {\n const destination = targets.get(entry.name)\n if (!destination || entry.isDirectory) return\n fs.mkdirSync(path.dirname(destination), { recursive: true })\n await pipeline(await openStream(), fs.createWriteStream(destination, { flags: 'wx' }))\n remaining.delete(entry.name)\n })\n\n if (remaining.size > 0) {\n throw new ArchiveImportError(\n 'error.archive_corrupt',\n `ZIP archive is missing expected entries: ${Array.from(remaining).join(', ')}`\n )\n }\n }\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { Readable } from 'node:stream'\nimport streamChain from 'stream-chain'\nimport streamJson from 'stream-json'\nimport pickModule from 'stream-json/filters/Pick.js'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\nimport { PARSER_FORMAT_IDS } from '@openchatlab/parser'\nimport { ArchiveImportError } from './errors'\nimport type { ZipArchiveReader } from './archive-reader'\nimport type { ArchiveEntrySummary, ArchiveResolver, MaterializedImport, PreparedImportChat } from './types'\n\nconst { chain } = streamChain\nconst { parser } = streamJson\nconst { pick } = pickModule\nconst { streamValues } = streamValuesModule\n\nconst GROUP_FILE = /^Takeout\\/Google Chat\\/Groups\\/((DM|Space) [^/]+)\\/(group_info|messages)\\.json$/\nconst USER_INFO_FILE = /^Takeout\\/Google Chat\\/Users\\/[^/]+\\/user_info\\.json$/\nconst TAKEOUT_ROOT = /^Takeout\\/([^/]+)\\//\nconst MAX_METADATA_BYTES = 5 * 1024 * 1024\n\ninterface GoogleChatUser {\n email?: string\n name?: string\n}\n\ninterface GoogleChatUserInfo {\n user?: GoogleChatUser\n}\n\ninterface GoogleChatGroupInfo {\n name?: string\n members?: GoogleChatUser[]\n}\n\ninterface ConversationDraft {\n chatId: string\n kind: 'DM' | 'Space'\n groupInfoEntry?: string\n messagesEntry?: string\n groupInfo?: GoogleChatGroupInfo\n messageCount?: number\n}\n\nasync function readSmallJson<T>(stream: Readable, maxBytes = MAX_METADATA_BYTES): Promise<T> {\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of stream as AsyncIterable<Buffer | string>) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n total += buffer.length\n if (total > maxBytes) {\n throw new ArchiveImportError('error.archive_limit_exceeded', 'Google Chat metadata exceeds the size limit')\n }\n chunks.push(buffer)\n }\n try {\n return JSON.parse(Buffer.concat(chunks).toString('utf8')) as T\n } catch (error) {\n throw new ArchiveImportError('error.archive_corrupt', 'Invalid Google Chat metadata JSON', { cause: error })\n }\n}\n\nasync function countMessages(stream: Readable): Promise<number> {\n const pipeline = chain([stream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n let count = 0\n try {\n for await (const _item of pipeline as AsyncIterable<unknown>) count++\n return count\n } catch (error) {\n throw new ArchiveImportError('error.archive_corrupt', 'Invalid Google Chat messages JSON', { cause: error })\n }\n}\n\nfunction normalizeEmail(user: GoogleChatUser | undefined): string | null {\n return user?.email?.trim().toLowerCase() || null\n}\n\nfunction deriveConversationName(draft: ConversationDraft, ownerEmail: string | null): string {\n const groupInfo = draft.groupInfo\n if (draft.kind === 'Space' && groupInfo?.name?.trim()) return groupInfo.name.trim()\n\n const members = groupInfo?.members ?? []\n const other = members.find((member) => {\n const email = normalizeEmail(member)\n return !ownerEmail || email !== ownerEmail\n })\n return other?.name?.trim() || other?.email?.trim() || members[0]?.name?.trim() || draft.chatId\n}\n\nexport class GoogleChatTakeoutResolver implements ArchiveResolver {\n readonly id = PARSER_FORMAT_IDS.GOOGLE_CHAT_NATIVE\n readonly platform = 'google-chat'\n\n detect(entries: ArchiveEntrySummary[]): boolean {\n const products = new Set<string>()\n const groupFiles = new Map<string, Set<string>>()\n\n for (const entry of entries) {\n const product = entry.name.match(TAKEOUT_ROOT)?.[1]\n if (product) products.add(product)\n\n const match = entry.name.match(GROUP_FILE)\n if (!match) continue\n const files = groupFiles.get(match[1]) ?? new Set<string>()\n files.add(match[3])\n groupFiles.set(match[1], files)\n }\n\n return (\n products.size === 1 &&\n products.has('Google Chat') &&\n Array.from(groupFiles.values()).some((files) => files.has('group_info') && files.has('messages'))\n )\n }\n\n /**\n * 扫描时按 ZIP 顺序消费必要 JSON。会话元数据和消息计数先暂存,\n * 等 owner 信息齐备后统一生成名称,避免依赖归档内部文件顺序。\n */\n async scan(reader: ZipArchiveReader): Promise<PreparedImportChat[]> {\n let ownerEmail: string | null = null\n const drafts = new Map<string, ConversationDraft>()\n\n await reader.visitEntries(async (entry, openStream) => {\n if (USER_INFO_FILE.test(entry.name)) {\n const ownerInfo = await readSmallJson<GoogleChatUserInfo>(await openStream())\n ownerEmail = normalizeEmail(ownerInfo.user)\n return\n }\n\n const match = entry.name.match(GROUP_FILE)\n if (!match) return\n const chatId = `Groups/${match[1]}`\n const draft =\n drafts.get(chatId) ??\n ({\n chatId,\n kind: match[2] as 'DM' | 'Space',\n } satisfies ConversationDraft)\n\n if (match[3] === 'group_info') {\n draft.groupInfoEntry = entry.name\n draft.groupInfo = await readSmallJson<GoogleChatGroupInfo>(await openStream())\n } else {\n draft.messagesEntry = entry.name\n draft.messageCount = await countMessages(await openStream())\n }\n drafts.set(chatId, draft)\n })\n\n if (drafts.size === 0) {\n throw new ArchiveImportError(\n 'error.google_chat_structure_not_found',\n 'Google Chat conversation structure was not found'\n )\n }\n\n return Array.from(drafts.values())\n .map((draft) => {\n if (!draft.groupInfoEntry || !draft.messagesEntry || !draft.groupInfo || draft.messageCount === undefined) {\n throw new ArchiveImportError(\n 'error.google_chat_conversation_incomplete',\n `Google Chat conversation is missing required files: ${draft.chatId}`\n )\n }\n return {\n chatId: draft.chatId,\n name: deriveConversationName(draft, ownerEmail),\n type: draft.kind === 'Space' ? 'group' : 'private',\n messageCount: draft.messageCount,\n memberCount: draft.groupInfo.members?.length ?? 0,\n } satisfies PreparedImportChat\n })\n .sort((a, b) => a.chatId.localeCompare(b.chatId))\n }\n\n async materialize(\n reader: ZipArchiveReader,\n chat: PreparedImportChat,\n targetDir: string\n ): Promise<MaterializedImport> {\n const entries = await reader.listEntries()\n const groupPrefix = `Takeout/Google Chat/${chat.chatId}/`\n const userInfoEntry = entries.find((entry) => USER_INFO_FILE.test(entry.name))?.name\n const groupInfoEntry = `${groupPrefix}group_info.json`\n const messagesEntry = `${groupPrefix}messages.json`\n if (\n !userInfoEntry ||\n !entries.some((entry) => entry.name === groupInfoEntry) ||\n !entries.some((entry) => entry.name === messagesEntry)\n ) {\n throw new ArchiveImportError(\n 'error.google_chat_conversation_incomplete',\n `Google Chat conversation is missing required files: ${chat.chatId}`\n )\n }\n\n fs.mkdirSync(targetDir, { recursive: true })\n await reader.pipeEntries(\n new Map([\n [userInfoEntry, path.join(targetDir, 'user_info.json')],\n [groupInfoEntry, path.join(targetDir, 'group_info.json')],\n [messagesEntry, path.join(targetDir, 'messages.json')],\n ])\n )\n\n const manifestPath = path.join(targetDir, 'google-chat-import.json')\n fs.writeFileSync(\n manifestPath,\n JSON.stringify(\n {\n format: 'chatlab-google-chat-takeout',\n version: 1,\n chatId: chat.chatId,\n chatType: chat.type,\n chatName: chat.name,\n userInfoFile: 'user_info.json',\n groupInfoFile: 'group_info.json',\n messagesFile: 'messages.json',\n },\n null,\n 2\n ),\n { encoding: 'utf8', flag: 'wx' }\n )\n return { manifestPath }\n }\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { randomUUID } from 'node:crypto'\nimport { ZipArchiveReader } from './archive-reader'\nimport { ArchiveImportError } from './errors'\nimport { GoogleChatTakeoutResolver } from './google-chat-resolver'\nimport type { ArchiveResolver, PreparedImportSource } from './types'\nimport { createChatLabTempDir } from '../../temp-workspace'\n\nconst DEFAULT_TTL_MS = 30 * 60 * 1000\n\ninterface SourceRecord {\n descriptor: PreparedImportSource\n archivePath: string\n ownsArchive: boolean\n resolver: ArchiveResolver\n lastAccessAt: number\n}\n\nexport interface ArchiveImportSourceManagerOptions {\n tempRoot?: string\n ttlMs?: number\n now?: () => number\n resolvers?: ArchiveResolver[]\n}\n\nexport class ArchiveImportSourceManager {\n private readonly records = new Map<string, SourceRecord>()\n private readonly expiredSourceIds = new Set<string>()\n private readonly tempRoot: string\n private readonly ttlMs: number\n private readonly now: () => number\n private readonly resolvers: ArchiveResolver[]\n\n constructor(options: ArchiveImportSourceManagerOptions = {}) {\n this.tempRoot = options.tempRoot ?? createChatLabTempDir('imports', 'sources-')\n this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS\n this.now = options.now ?? Date.now\n this.resolvers = options.resolvers ?? [new GoogleChatTakeoutResolver()]\n fs.mkdirSync(this.tempRoot, { recursive: true })\n }\n\n prepareLocalArchive(archivePath: string): Promise<PreparedImportSource> {\n return this.prepareArchive(archivePath, false)\n }\n\n prepareOwnedArchive(archivePath: string): Promise<PreparedImportSource> {\n return this.prepareArchive(archivePath, true)\n }\n\n private async prepareArchive(archivePath: string, ownsArchive: boolean): Promise<PreparedImportSource> {\n await this.cleanupExpired()\n const reader = new ZipArchiveReader(archivePath)\n try {\n const entries = await reader.listEntries()\n const matches = this.resolvers.filter((resolver) => resolver.detect(entries))\n if (matches.length !== 1) {\n throw new ArchiveImportError(\n 'error.archive_unsupported',\n matches.length === 0 ? 'Unsupported archive format' : 'Archive matches multiple import formats'\n )\n }\n\n const resolver = matches[0]\n const chats = await resolver.scan(reader)\n const createdAt = this.now()\n const sourceId = randomUUID()\n const descriptor: PreparedImportSource = {\n sourceId,\n formatId: resolver.id,\n platform: resolver.platform,\n chats,\n expiresAt: createdAt + this.ttlMs,\n }\n this.records.set(sourceId, {\n descriptor,\n archivePath,\n ownsArchive,\n resolver,\n lastAccessAt: createdAt,\n })\n return descriptor\n } catch (error) {\n if (ownsArchive) this.removePath(archivePath)\n throw error\n }\n }\n\n /**\n * selected chat 的 JSON 物化目录只在 handler 生命周期内存在。\n * 无论数据库导入成功、失败还是抛异常,finally 都会删除它。\n */\n async withMaterializedChat<T>(\n sourceId: string,\n chatId: string,\n handler: (manifestPath: string) => Promise<T>\n ): Promise<T> {\n const record = await this.getActiveRecord(sourceId)\n const chat = record.descriptor.chats.find((item) => item.chatId === chatId)\n if (!chat) {\n throw new ArchiveImportError(\n 'error.google_chat_conversation_incomplete',\n `Archive conversation was not found: ${chatId}`\n )\n }\n const materializedDir = fs.mkdtempSync(path.join(this.tempRoot, `materialized-${sourceId}-`))\n try {\n const reader = new ZipArchiveReader(record.archivePath)\n const materialized = await record.resolver.materialize(reader, chat, materializedDir)\n return await handler(materialized.manifestPath)\n } finally {\n this.removePath(materializedDir)\n }\n }\n\n async release(sourceId: string): Promise<void> {\n const record = this.records.get(sourceId)\n if (!record) return\n this.records.delete(sourceId)\n if (record.ownsArchive) this.removePath(record.archivePath)\n }\n\n async cleanupExpired(now = this.now()): Promise<number> {\n let removed = 0\n for (const [sourceId, record] of this.records) {\n if (now - record.lastAccessAt < this.ttlMs) continue\n this.records.delete(sourceId)\n this.expiredSourceIds.add(sourceId)\n if (record.ownsArchive) this.removePath(record.archivePath)\n removed++\n }\n return removed\n }\n\n async close(): Promise<void> {\n for (const sourceId of Array.from(this.records.keys())) {\n await this.release(sourceId)\n }\n this.removePath(this.tempRoot)\n }\n\n private async getActiveRecord(sourceId: string): Promise<SourceRecord> {\n const record = this.records.get(sourceId)\n if (!record) {\n const code = this.expiredSourceIds.has(sourceId) ? 'error.import_source_expired' : 'error.import_source_not_found'\n throw new ArchiveImportError(code, `Archive import source is unavailable: ${sourceId}`)\n }\n\n const now = this.now()\n if (now - record.lastAccessAt >= this.ttlMs) {\n this.records.delete(sourceId)\n this.expiredSourceIds.add(sourceId)\n if (record.ownsArchive) this.removePath(record.archivePath)\n throw new ArchiveImportError('error.import_source_expired', `Archive import source expired: ${sourceId}`)\n }\n\n record.lastAccessAt = now\n record.descriptor.expiresAt = now + this.ttlMs\n return record\n }\n\n private removePath(targetPath: string): void {\n try {\n fs.rmSync(targetPath, { recursive: true, force: true })\n } catch {\n // 清理失败不覆盖原始导入错误,残留文件由下次启动或系统临时目录回收。\n }\n }\n}\n","/**\n * Markdown export engine (platform-agnostic).\n *\n * Extracted from electron/main/worker/query/session/export.ts.\n * Exports filtered chat messages to Markdown format via a writer abstraction.\n * Supports condition-based filtering (keyword/sender/time) and session-based export.\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\n\n// ==================== Public interfaces ====================\n\nexport interface ExportFilterParams {\n sessionId: string\n sessionName: string\n filterMode: 'condition' | 'session'\n keywords?: string[]\n timeFilter?: { startTs: number; endTs: number }\n senderIds?: number[]\n contextSize?: number\n segmentIds?: number[]\n}\n\nexport interface ExportProgress {\n stage: 'preparing' | 'exporting' | 'done' | 'error'\n currentBlock: number\n totalBlocks: number\n percentage: number\n message: string\n}\n\nexport type ExportProgressCallback = (progress: ExportProgress) => void\n\nexport interface ExportWriter {\n write(chunk: string): void\n end(): void\n}\n\nexport interface ExportDeps {\n openDatabase(sessionId: string): DatabaseAdapter | null\n onProgress?: ExportProgressCallback\n}\n\nexport interface ExportResult {\n success: boolean\n error?: string\n totalBlocks: number\n totalMessages: number\n}\n\n// ==================== Core export logic ====================\n\n/**\n * Export filter results to Markdown via the provided writer.\n * Platform-agnostic: no filesystem or IPC dependency.\n */\nexport function exportFilterResultToMarkdown(\n params: ExportFilterParams,\n deps: ExportDeps,\n writer: ExportWriter\n): ExportResult {\n const db = deps.openDatabase(params.sessionId)\n if (!db) {\n return { success: false, error: 'Cannot open database', totalBlocks: 0, totalMessages: 0 }\n }\n\n const progress = deps.onProgress\n\n try {\n writer.write(`# ${params.sessionName} - Chat Filter Results\\n\\n`)\n writer.write(`> Export time: ${new Date().toLocaleString()}\\n\\n`)\n\n writer.write(`## Filter Conditions\\n\\n`)\n if (params.filterMode === 'condition') {\n if (params.keywords && params.keywords.length > 0) {\n writer.write(`- Keywords: ${params.keywords.join(', ')}\\n`)\n }\n if (params.timeFilter) {\n const start = new Date(params.timeFilter.startTs * 1000).toLocaleString()\n const end = new Date(params.timeFilter.endTs * 1000).toLocaleString()\n writer.write(`- Time range: ${start} ~ ${end}\\n`)\n }\n writer.write(`- Context: ±${params.contextSize || 10} messages\\n`)\n } else {\n writer.write(`- Mode: session filter\\n`)\n writer.write(`- Selected sessions: ${params.segmentIds?.length || 0}\\n`)\n }\n writer.write('\\n')\n\n let totalMessages = 0\n let blockIndex = 0\n\n if (params.filterMode === 'condition') {\n const result = exportConditionMode(db, params, writer, progress)\n totalMessages = result.totalMessages\n blockIndex = result.blockIndex\n } else {\n const result = exportSessionMode(db, params, writer, progress)\n totalMessages = result.totalMessages\n blockIndex = result.blockIndex\n }\n\n writer.end()\n\n progress?.({\n stage: 'done',\n currentBlock: blockIndex,\n totalBlocks: blockIndex,\n percentage: 100,\n message: `Export complete, ${blockIndex} blocks`,\n })\n\n return { success: true, totalBlocks: blockIndex, totalMessages }\n } catch (error) {\n progress?.({\n stage: 'error',\n currentBlock: 0,\n totalBlocks: 0,\n percentage: 0,\n message: `Export failed: ${String(error)}`,\n })\n return { success: false, error: String(error), totalBlocks: 0, totalMessages: 0 }\n }\n}\n\n// ==================== Condition mode ====================\n\nfunction exportConditionMode(\n db: DatabaseAdapter,\n params: ExportFilterParams,\n writer: ExportWriter,\n progress?: ExportProgressCallback\n): { totalMessages: number; blockIndex: number } {\n const contextSize = params.contextSize || 10\n\n const lightweightSql = `\n SELECT id, ts, sender_id as senderId, content\n FROM message\n ${params.timeFilter ? 'WHERE ts >= ? AND ts <= ?' : ''}\n ORDER BY ts ASC, id ASC\n `\n const sqlParams: unknown[] = []\n if (params.timeFilter) {\n sqlParams.push(params.timeFilter.startTs, params.timeFilter.endTs)\n }\n\n const hitIndexes: number[] = []\n let msgIndex = 0\n const stmt = db.prepare(lightweightSql)\n const rows = stmt.all(...sqlParams) as Array<{\n id: number\n ts: number\n senderId: number\n content: string | null\n }>\n\n for (const row of rows) {\n let isHit = true\n if (params.keywords && params.keywords.length > 0) {\n const content = (row.content || '').toLowerCase()\n isHit = params.keywords.some((kw) => content.includes(kw.toLowerCase()))\n }\n if (isHit && params.senderIds && params.senderIds.length > 0) {\n isHit = params.senderIds.includes(row.senderId)\n }\n if (isHit) hitIndexes.push(msgIndex)\n msgIndex++\n }\n\n const totalHits = hitIndexes.length\n\n progress?.({\n stage: 'preparing',\n currentBlock: 0,\n totalBlocks: 0,\n percentage: 10,\n message: `Analyzing: found ${totalHits} matching messages...`,\n })\n\n if (hitIndexes.length === 0) {\n writer.write(`## Statistics\\n\\n- No matches\\n`)\n writer.end()\n return { totalMessages: 0, blockIndex: 0 }\n }\n\n const totalMsgCount = msgIndex\n const ranges: Array<{ start: number; end: number; hitIndexes: number[] }> = []\n for (const hitIdx of hitIndexes) {\n const start = Math.max(0, hitIdx - contextSize)\n const end = Math.min(totalMsgCount - 1, hitIdx + contextSize)\n if (ranges.length > 0) {\n const last = ranges[ranges.length - 1]\n if (start <= last.end + 1) {\n last.end = Math.max(last.end, end)\n last.hitIndexes.push(hitIdx)\n continue\n }\n }\n ranges.push({ start, end, hitIndexes: [hitIdx] })\n }\n\n const totalBlocks = ranges.length\n progress?.({\n stage: 'exporting',\n currentBlock: 0,\n totalBlocks,\n percentage: 15,\n message: `Exporting ${totalBlocks} conversation blocks...`,\n })\n\n writer.write(`## Statistics\\n\\n- Blocks: ${totalBlocks}\\n- Hits: ${totalHits}\\n\\n`)\n writer.write(`## Conversation Content\\n\\n`)\n\n let totalMessages = 0\n let blockIndex = 0\n\n for (const range of ranges) {\n blockIndex++\n progress?.({\n stage: 'exporting',\n currentBlock: blockIndex,\n totalBlocks,\n percentage: Math.round(15 + ((blockIndex - 1) / totalBlocks) * 80),\n message: `Exporting block ${blockIndex}/${totalBlocks}...`,\n })\n\n const blockSql = `\n SELECT msg.id, msg.ts,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n msg.content\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${params.timeFilter ? 'WHERE msg.ts >= ? AND msg.ts <= ?' : ''}\n ORDER BY msg.ts ASC, msg.id ASC\n LIMIT ? OFFSET ?\n `\n const blockParams: unknown[] = []\n if (params.timeFilter) {\n blockParams.push(params.timeFilter.startTs, params.timeFilter.endTs)\n }\n blockParams.push(range.end - range.start + 1, range.start)\n\n const messages = db.prepare(blockSql).all(...blockParams) as Array<{\n id: number\n ts: number\n senderName: string\n content: string | null\n }>\n\n if (messages.length === 0) continue\n\n const hitIndexSet = new Set(range.hitIndexes.map((idx) => idx - range.start))\n const startTime = new Date(messages[0].ts * 1000).toLocaleString()\n const endTime = new Date(messages[messages.length - 1].ts * 1000).toLocaleString()\n writer.write(`### Block ${blockIndex} (${startTime} ~ ${endTime})\\n\\n`)\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n const time = new Date(msg.ts * 1000).toLocaleTimeString()\n const hitMark = hitIndexSet.has(i) ? ' ⭐' : ''\n writer.write(`${time} ${msg.senderName}${hitMark}: ${msg.content || '[non-text message]'}\\n`)\n totalMessages++\n }\n writer.write('\\n')\n }\n\n return { totalMessages, blockIndex }\n}\n\n// ==================== Session mode ====================\n\nfunction exportSessionMode(\n db: DatabaseAdapter,\n params: ExportFilterParams,\n writer: ExportWriter,\n progress?: ExportProgressCallback\n): { totalMessages: number; blockIndex: number } {\n if (!params.segmentIds || params.segmentIds.length === 0) {\n writer.write(`## Statistics\\n\\n- No sessions selected\\n`)\n writer.end()\n return { totalMessages: 0, blockIndex: 0 }\n }\n\n progress?.({\n stage: 'preparing',\n currentBlock: 0,\n totalBlocks: params.segmentIds.length,\n percentage: 10,\n message: `Preparing to export ${params.segmentIds.length} sessions...`,\n })\n\n const sessionsSql = `\n SELECT id, start_ts as startTs, end_ts as endTs\n FROM segment\n WHERE id IN (${params.segmentIds.map(() => '?').join(',')})\n ORDER BY start_ts ASC\n `\n const sessions = db.prepare(sessionsSql).all(...params.segmentIds) as Array<{\n id: number\n startTs: number\n endTs: number\n }>\n\n const totalBlocks = sessions.length\n writer.write(`## Statistics\\n\\n- Blocks: ${totalBlocks}\\n\\n`)\n writer.write(`## Conversation Content\\n\\n`)\n\n const messagesSql = `\n SELECT msg.id,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n msg.content,\n msg.ts as timestamp\n FROM message_context mc\n JOIN message msg ON msg.id = mc.message_id\n JOIN member m ON msg.sender_id = m.id\n WHERE mc.segment_id = ?\n ORDER BY msg.ts ASC\n `\n\n let totalMessages = 0\n let blockIndex = 0\n\n for (const session of sessions) {\n blockIndex++\n progress?.({\n stage: 'exporting',\n currentBlock: blockIndex,\n totalBlocks,\n percentage: Math.round(15 + ((blockIndex - 1) / totalBlocks) * 80),\n message: `Exporting session ${blockIndex}/${totalBlocks}...`,\n })\n\n const messages = db.prepare(messagesSql).all(session.id) as Array<{\n id: number\n senderName: string\n content: string | null\n timestamp: number\n }>\n\n if (messages.length === 0) continue\n\n const startTime = new Date(session.startTs * 1000).toLocaleString()\n const endTime = new Date(session.endTs * 1000).toLocaleString()\n writer.write(`### Block ${blockIndex} (${startTime} ~ ${endTime})\\n\\n`)\n\n for (const msg of messages) {\n const time = new Date(msg.timestamp * 1000).toLocaleTimeString()\n writer.write(`${time} ${msg.senderName}: ${msg.content || '[non-text message]'}\\n`)\n totalMessages++\n }\n writer.write('\\n')\n }\n\n return { totalMessages, blockIndex }\n}\n","/**\n * Multi-format export: txt / json / markdown.\n * Simplified for time-range-only filtering (no keyword/context block logic).\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport { CHATLAB_FORMAT_VERSION } from '@openchatlab/shared-types'\nimport { appLogger } from '../logging/app-logger'\n\nexport type ExportFormat = 'txt' | 'json' | 'markdown'\n\nexport interface FormatExportParams {\n sessionId: string\n sessionName: string\n format: ExportFormat\n timeFilter?: { startTs: number; endTs: number }\n}\n\nexport interface FormatExportResult {\n success: boolean\n error?: string\n totalMessages: number\n content: string\n filename: string\n mimeType: string\n}\n\ninterface MessageRow {\n ts: number\n senderName: string\n sender: string\n accountName: string\n groupNickname: string | null\n type: number\n content: string | null\n platformMessageId: string | null\n replyToMessageId: string | null\n}\n\ninterface MetaRow {\n name: string\n platform: string\n type: string\n groupId: string | null\n groupAvatar: string | null\n ownerId: string | null\n}\n\ninterface MemberRow {\n platformId: string\n accountName: string | null\n groupNickname: string | null\n aliases: string | null\n avatar: string | null\n roles: string | null\n}\n\nfunction queryMessages(db: DatabaseAdapter, timeFilter?: { startTs: number; endTs: number }): MessageRow[] {\n const hasFilter = !!timeFilter\n const sql = `\n SELECT msg.ts,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n m.platform_id as sender,\n COALESCE(msg.sender_account_name, m.account_name, m.platform_id) as accountName,\n COALESCE(msg.sender_group_nickname, m.group_nickname) as groupNickname,\n msg.type,\n msg.content,\n msg.platform_message_id as platformMessageId,\n msg.reply_to_message_id as replyToMessageId\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${hasFilter ? 'WHERE msg.ts >= ? AND msg.ts <= ?' : ''}\n ORDER BY msg.ts ASC, msg.id ASC\n `\n const params: unknown[] = []\n if (hasFilter) {\n params.push(timeFilter!.startTs, timeFilter!.endTs)\n }\n return db.prepare(sql).all(...params) as unknown as MessageRow[]\n}\n\nfunction formatTimeShort(ts: number): string {\n return new Date(ts * 1000).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' })\n}\n\nfunction exportAsTxt(messages: MessageRow[], sessionName: string): string {\n const lines: string[] = [`${sessionName}\\n`]\n let lastDate = ''\n for (const msg of messages) {\n const date = new Date(msg.ts * 1000).toLocaleDateString()\n if (date !== lastDate) {\n lines.push(`\\n--- ${date} ---\\n`)\n lastDate = date\n }\n lines.push(`${formatTimeShort(msg.ts)} ${msg.senderName}: ${msg.content || '[non-text]'}`)\n }\n return lines.join('\\n')\n}\n\nfunction parseOptionalArray<T>(value: string | null): T[] | undefined {\n if (!value) return undefined\n try {\n const parsed = JSON.parse(value)\n return Array.isArray(parsed) && parsed.length > 0 ? (parsed as T[]) : undefined\n } catch {\n return undefined\n }\n}\n\nfunction exportAsJson(\n db: DatabaseAdapter,\n messages: MessageRow[],\n sessionName: string,\n sourceSessionId: string\n): string {\n const meta = db\n .prepare(\n `SELECT name,\n platform,\n type,\n group_id as groupId,\n group_avatar as groupAvatar,\n owner_id as ownerId\n FROM meta\n LIMIT 1`\n )\n .get() as unknown as MetaRow | undefined\n if (!meta) throw new Error('Cannot read session metadata')\n\n const members = db\n .prepare(\n `SELECT platform_id as platformId,\n account_name as accountName,\n group_nickname as groupNickname,\n aliases,\n avatar,\n roles\n FROM member\n ORDER BY id ASC`\n )\n .all() as unknown as MemberRow[]\n\n const data = {\n chatlab: {\n version: CHATLAB_FORMAT_VERSION,\n exportedAt: Math.floor(Date.now() / 1000),\n generator: 'ChatLab',\n },\n meta: {\n name: meta.name || sessionName,\n platform: meta.platform,\n type: meta.type,\n groupId: meta.groupId || undefined,\n groupAvatar: meta.groupAvatar || undefined,\n ownerId: meta.ownerId || undefined,\n sourceSessionId,\n },\n members: members.map((member) => ({\n platformId: member.platformId,\n accountName: member.accountName || member.platformId,\n groupNickname: member.groupNickname || undefined,\n aliases: parseOptionalArray<string>(member.aliases),\n avatar: member.avatar || undefined,\n roles: parseOptionalArray<{ id: string; name?: string }>(member.roles),\n })),\n messages: messages.map((msg) => ({\n sender: msg.sender,\n accountName: msg.accountName,\n groupNickname: msg.groupNickname || undefined,\n timestamp: msg.ts,\n type: msg.type,\n content: msg.content,\n platformMessageId: msg.platformMessageId || undefined,\n replyToMessageId: msg.replyToMessageId || undefined,\n })),\n }\n return JSON.stringify(data, null, 2)\n}\n\nfunction exportAsMarkdown(messages: MessageRow[], sessionName: string): string {\n const lines: string[] = [`# ${sessionName}\\n`, `> Export time: ${new Date().toLocaleString()}\\n`]\n let lastDate = ''\n for (const msg of messages) {\n const date = new Date(msg.ts * 1000).toLocaleDateString()\n if (date !== lastDate) {\n lines.push(`\\n## ${date}\\n`)\n lastDate = date\n }\n lines.push(`**${formatTimeShort(msg.ts)} ${msg.senderName}**: ${msg.content || '*[non-text]*'}`)\n }\n return lines.join('\\n')\n}\n\nconst FORMAT_CONFIG: Record<ExportFormat, { ext: string; mime: string }> = {\n txt: { ext: 'txt', mime: 'text/plain; charset=utf-8' },\n json: { ext: 'json', mime: 'application/json; charset=utf-8' },\n markdown: { ext: 'md', mime: 'text/markdown; charset=utf-8' },\n}\n\nexport function exportWithFormat(\n params: FormatExportParams,\n openDatabase: (sessionId: string) => DatabaseAdapter | null\n): FormatExportResult {\n const db = openDatabase(params.sessionId)\n if (!db) {\n return { success: false, error: 'Cannot open database', totalMessages: 0, content: '', filename: '', mimeType: '' }\n }\n\n try {\n const messages = queryMessages(db, params.timeFilter)\n if (messages.length === 0) {\n return {\n success: false,\n error: 'No messages found in the specified range',\n totalMessages: 0,\n content: '',\n filename: '',\n mimeType: '',\n }\n }\n\n const { ext, mime } = FORMAT_CONFIG[params.format]\n let content: string\n switch (params.format) {\n case 'txt':\n content = exportAsTxt(messages, params.sessionName)\n break\n case 'json':\n content = exportAsJson(db, messages, params.sessionName, params.sessionId)\n break\n case 'markdown':\n content = exportAsMarkdown(messages, params.sessionName)\n break\n }\n\n const timestamp = Date.now()\n const filename = `${params.sessionName}_export_${timestamp}.${ext}`\n appLogger.info('export', 'chat export generated', {\n sessionId: params.sessionId,\n format: params.format,\n totalMessages: messages.length,\n filteredByTime: !!params.timeFilter,\n })\n return { success: true, totalMessages: messages.length, content, filename, mimeType: mime }\n } catch (error) {\n appLogger.error('export', 'chat export failed', error)\n return {\n success: false,\n error: String(error),\n totalMessages: 0,\n content: '',\n filename: '',\n mimeType: '',\n }\n }\n}\n","/**\n * Temporary database manager for merge operations.\n *\n * Extracted from electron/main/merger/tempCache.ts.\n * Uses DatabaseAdapter for platform-agnostic temp DB read/write.\n *\n * Callers provide a DatabaseAdapter factory; TempDbWriter/TempDbReader\n * handle schema creation and streaming reads.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport { CHATLAB_FORMAT_VERSION, type ParsedMember, type ParsedMessage } from '@openchatlab/shared-types'\nimport type { MergerDataSource, MergerInputMessage, MergerSourceMeta } from './index'\nimport type { MergerMember } from '@openchatlab/core'\n\n// ==================== Temp DB schema (simplified for merge preview) ====================\n\nexport const TEMP_DB_SCHEMA = `\n CREATE TABLE IF NOT EXISTS meta (\n name TEXT NOT NULL,\n platform TEXT NOT NULL,\n type TEXT NOT NULL,\n group_id TEXT,\n group_avatar TEXT,\n owner_id TEXT\n );\n\n CREATE TABLE IF NOT EXISTS member (\n platform_id TEXT PRIMARY KEY,\n account_name TEXT,\n group_nickname TEXT,\n avatar TEXT\n );\n\n CREATE TABLE IF NOT EXISTS message (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n platform_message_id TEXT,\n sender_platform_id TEXT NOT NULL,\n sender_account_name TEXT,\n sender_group_nickname TEXT,\n timestamp INTEGER NOT NULL,\n type INTEGER NOT NULL,\n content TEXT,\n reply_to_message_id TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_message_ts ON message(timestamp);\n CREATE INDEX IF NOT EXISTS idx_message_sender ON message(sender_platform_id);\n`\n\n// ==================== TempDbWriter ====================\n\nexport class TempDbWriter {\n private db: DatabaseAdapter\n private memberSet = new Set<string>()\n private messageCount = 0\n\n constructor(db: DatabaseAdapter) {\n this.db = db\n db.exec(TEMP_DB_SCHEMA)\n db.exec('BEGIN TRANSACTION')\n }\n\n writeMeta(meta: {\n name: string\n platform: string\n type: string | number\n groupId?: string\n groupAvatar?: string\n ownerId?: string\n }): void {\n this.db\n .prepare('INSERT INTO meta (name, platform, type, group_id, group_avatar, owner_id) VALUES (?, ?, ?, ?, ?, ?)')\n .run(meta.name, meta.platform, meta.type, meta.groupId || null, meta.groupAvatar || null, meta.ownerId || null)\n }\n\n writeMembers(members: ParsedMember[]): void {\n const insert = this.db.prepare(\n 'INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar) VALUES (?, ?, ?, ?)'\n )\n for (const m of members) {\n if (!this.memberSet.has(m.platformId)) {\n this.memberSet.add(m.platformId)\n insert.run(m.platformId, m.accountName || null, m.groupNickname || null, m.avatar || null)\n }\n }\n }\n\n writeMessages(messages: ParsedMessage[]): void {\n const insert = this.db.prepare(\n `INSERT INTO message (\n platform_message_id, sender_platform_id, sender_account_name, sender_group_nickname,\n timestamp, type, content, reply_to_message_id\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n const memberInsert = this.db.prepare(\n 'INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar) VALUES (?, ?, ?, ?)'\n )\n for (const msg of messages) {\n if (!this.memberSet.has(msg.senderPlatformId)) {\n this.memberSet.add(msg.senderPlatformId)\n memberInsert.run(msg.senderPlatformId, msg.senderAccountName || null, msg.senderGroupNickname || null, null)\n }\n insert.run(\n msg.platformMessageId || null,\n msg.senderPlatformId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\n msg.timestamp,\n msg.type,\n msg.content || null,\n msg.replyToMessageId || null\n )\n this.messageCount++\n }\n }\n\n finish(): { messageCount: number; memberCount: number } {\n this.db.exec('COMMIT')\n const result = { messageCount: this.messageCount, memberCount: this.memberSet.size }\n this.db.close()\n return result\n }\n\n abort(): void {\n try {\n this.db.exec('ROLLBACK')\n } catch {\n /* ignore */\n }\n this.db.close()\n }\n}\n\n// ==================== TempDbReader ====================\n\nexport interface TempDbMeta {\n name: string\n platform: string\n type: string\n groupId?: string\n groupAvatar?: string\n ownerId?: string\n}\n\nexport class TempDbReader {\n private db: DatabaseAdapter\n\n constructor(db: DatabaseAdapter) {\n this.db = db\n }\n\n getMeta(): TempDbMeta | null {\n const row = this.db.prepare('SELECT * FROM meta LIMIT 1').get() as\n | {\n name: string\n platform: string\n type: string\n group_id: string | null\n group_avatar: string | null\n owner_id: string | null\n }\n | undefined\n if (!row) return null\n return {\n name: row.name,\n platform: row.platform,\n type: row.type,\n groupId: row.group_id || undefined,\n groupAvatar: row.group_avatar || undefined,\n ownerId: row.owner_id || undefined,\n }\n }\n\n getMembers(): ParsedMember[] {\n const rows = this.db.prepare('SELECT * FROM member').all() as Array<{\n platform_id: string\n account_name: string | null\n group_nickname: string | null\n avatar: string | null\n }>\n return rows.map((r) => ({\n platformId: r.platform_id,\n accountName: r.account_name || r.platform_id,\n groupNickname: r.group_nickname || undefined,\n avatar: r.avatar || undefined,\n }))\n }\n\n getMessageCount(): number {\n const row = this.db.prepare('SELECT COUNT(*) as count FROM message').get() as { count: number }\n return row.count\n }\n\n streamMessages(batchSize: number, callback: (messages: ParsedMessage[]) => void): void {\n const stmt = this.db.prepare(`\n SELECT platform_message_id, sender_platform_id, sender_account_name, sender_group_nickname,\n timestamp, type, content, reply_to_message_id\n FROM message ORDER BY timestamp ASC, id ASC LIMIT ? OFFSET ?\n `)\n\n let offset = 0\n while (true) {\n const rows = stmt.all(batchSize, offset) as Array<{\n platform_message_id: string | null\n sender_platform_id: string\n sender_account_name: string | null\n sender_group_nickname: string | null\n timestamp: number\n type: number\n content: string | null\n reply_to_message_id: string | null\n }>\n\n if (rows.length === 0) break\n\n const messages: ParsedMessage[] = rows.map((r) => ({\n platformMessageId: r.platform_message_id || undefined,\n senderPlatformId: r.sender_platform_id,\n senderAccountName: r.sender_account_name || r.sender_platform_id,\n senderGroupNickname: r.sender_group_nickname || undefined,\n timestamp: r.timestamp,\n type: r.type as ParsedMessage['type'],\n content: r.content,\n replyToMessageId: r.reply_to_message_id || undefined,\n }))\n\n callback(messages)\n offset += batchSize\n }\n }\n\n getAllMessages(): ParsedMessage[] {\n const rows = this.db\n .prepare(\n `SELECT platform_message_id, sender_platform_id, sender_account_name, sender_group_nickname,\n timestamp, type, content, reply_to_message_id\n FROM message ORDER BY timestamp ASC, id ASC`\n )\n .all() as Array<{\n platform_message_id: string | null\n sender_platform_id: string\n sender_account_name: string | null\n sender_group_nickname: string | null\n timestamp: number\n type: number\n content: string | null\n reply_to_message_id: string | null\n }>\n\n return rows.map((r) => ({\n platformMessageId: r.platform_message_id || undefined,\n senderPlatformId: r.sender_platform_id,\n senderAccountName: r.sender_account_name || r.sender_platform_id,\n senderGroupNickname: r.sender_group_nickname || undefined,\n timestamp: r.timestamp,\n type: r.type as ParsedMessage['type'],\n content: r.content,\n replyToMessageId: r.reply_to_message_id || undefined,\n }))\n }\n\n close(): void {\n this.db.close()\n }\n\n /** Wrap this reader as a MergerDataSource for use with merge algorithms. */\n toDataSource(): MergerDataSource {\n return createDataSourceFromReader(this)\n }\n}\n\nfunction createDataSourceFromReader(reader: TempDbReader): MergerDataSource {\n return {\n getMeta(): MergerSourceMeta | null {\n const meta = reader.getMeta()\n if (!meta) return null\n return {\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n groupId: meta.groupId,\n groupAvatar: meta.groupAvatar,\n ownerId: meta.ownerId,\n }\n },\n getMembers(): MergerMember[] {\n return reader.getMembers().map((m) => ({\n platformId: m.platformId,\n accountName: m.accountName,\n groupNickname: m.groupNickname,\n avatar: m.avatar,\n }))\n },\n getMessageCount(): number {\n return reader.getMessageCount()\n },\n streamMessages(batchSize: number, callback: (messages: MergerInputMessage[]) => void): void {\n reader.streamMessages(batchSize, (messages) => {\n callback(\n messages.map((msg) => ({\n senderPlatformId: msg.senderPlatformId,\n platformMessageId: msg.platformMessageId,\n senderAccountName: msg.senderAccountName,\n senderGroupNickname: msg.senderGroupNickname,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content,\n replyToMessageId: msg.replyToMessageId,\n }))\n )\n })\n },\n }\n}\n\n// ==================== Session export ====================\n\nexport interface ExportedSession {\n chatlab: { version: typeof CHATLAB_FORMAT_VERSION; exportedAt: number; generator: string; description: string }\n meta: {\n name: string\n platform: string\n type: string\n groupId?: string\n groupAvatar?: string\n ownerId?: string\n }\n members: Array<{ platformId: string; accountName: string; groupNickname?: string; avatar?: string }>\n messages: Array<{\n platformMessageId?: string\n sender: string\n accountName: string\n groupNickname?: string\n timestamp: number\n type: number\n content: string | null\n replyToMessageId?: string\n }>\n}\n\n/**\n * Export a session DB to a ChatLab JSON structure (in memory).\n * Caller decides how to persist (write to file, send over HTTP, etc.).\n */\nexport function exportSessionToJson(db: DatabaseAdapter): ExportedSession {\n const meta = db.prepare('SELECT * FROM meta').get() as\n | {\n name: string\n platform: string\n type: string\n group_id?: string\n group_avatar?: string\n owner_id?: string\n }\n | undefined\n\n if (!meta) throw new Error('Cannot read session meta')\n\n const members = db.prepare('SELECT platform_id, account_name, group_nickname, avatar FROM member').all() as Array<{\n platform_id: string\n account_name?: string\n group_nickname?: string\n avatar?: string\n }>\n\n const messages = db\n .prepare(\n `SELECT m.platform_id as sender, msg.sender_account_name as accountName,\n msg.sender_group_nickname as groupNickname, msg.ts as timestamp, msg.type, msg.content,\n msg.platform_message_id as platformMessageId, msg.reply_to_message_id as replyToMessageId\n FROM message msg JOIN member m ON msg.sender_id = m.id ORDER BY msg.ts, msg.id`\n )\n .all() as Array<{\n platformMessageId?: string\n sender: string\n accountName?: string\n groupNickname?: string\n timestamp: number\n type: number\n content?: string\n replyToMessageId?: string\n }>\n\n return {\n chatlab: {\n version: CHATLAB_FORMAT_VERSION,\n exportedAt: Math.floor(Date.now() / 1000),\n generator: 'ChatLab Export',\n description: `Exported from session: ${meta.name}`,\n },\n meta: {\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n groupId: meta.group_id,\n groupAvatar: meta.group_avatar,\n ownerId: meta.owner_id,\n },\n members: members.map((m) => ({\n platformId: m.platform_id,\n accountName: m.account_name || m.platform_id,\n groupNickname: m.group_nickname || undefined,\n avatar: m.avatar,\n })),\n messages: messages.map((msg) => ({\n platformMessageId: msg.platformMessageId || undefined,\n sender: msg.sender,\n accountName: msg.accountName || msg.sender,\n groupNickname: msg.groupNickname || undefined,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content ?? null,\n replyToMessageId: msg.replyToMessageId || undefined,\n })),\n }\n}\n\n// ==================== Filesystem cleanup helpers ====================\n\nexport function deleteTempDatabase(dbPath: string): void {\n try {\n if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath)\n const walPath = dbPath + '-wal'\n const shmPath = dbPath + '-shm'\n if (fs.existsSync(walPath)) fs.unlinkSync(walPath)\n if (fs.existsSync(shmPath)) fs.unlinkSync(shmPath)\n } catch {\n /* best-effort cleanup */\n }\n}\n\nexport function cleanupTempDatabases(tempDir: string): void {\n try {\n if (!fs.existsSync(tempDir)) return\n const files = fs.readdirSync(tempDir)\n for (const file of files) {\n if (file.startsWith('merge_') && file.endsWith('.db')) {\n deleteTempDatabase(path.join(tempDir, file))\n }\n }\n } catch {\n /* best-effort */\n }\n}\n","/**\n * Shared session service layer.\n *\n * Provides unified session CRUD used by both CLI Web routes and Electron IPC/API.\n * All business logic (sorting, data shape, validation) lives here —\n * callers only handle protocol-specific concerns (HTTP params, IPC channels).\n */\n\nimport {\n getSessionInfo,\n getSessionOverview,\n getSessionMeta,\n getSummaryCount,\n getPrivateChatMemberAvatar,\n isChatSessionDb,\n buildSessionInfo,\n renameSession as coreRenameSession,\n updateSessionOwnerId as coreUpdateSessionOwnerId,\n} from '@openchatlab/core'\nimport type { CoreSessionInfo, DatabaseAdapter, SessionOverview } from '@openchatlab/core'\nimport type { SessionRuntimeAdapter } from './adapters'\nimport { getValidatedOverviewCache } from '../cache/session-cache'\n\nexport interface AnalysisSessionDTO extends CoreSessionInfo {\n id: string\n dbPath: string\n memberAvatar: string | null\n aiConversationCount: number\n ownerName: string | null\n ownerStatus: 'resolved' | 'missing' | 'unresolved'\n ownerExcluded: boolean\n}\n\n/**\n * Optional hooks for platform-specific behavior.\n * Electron can provide a cached overview resolver and post-list enrichment;\n * CLI Web can omit these to use default (no-cache) behavior.\n */\nexport interface ListSessionsOptions {\n /**\n * Resolve overview stats for a session, optionally from cache.\n * When provided, used instead of the default `getSessionOverview` SQL query.\n * Electron worker uses this to read from JSON file cache (resolveOverview).\n */\n resolveOverview?(db: DatabaseAdapter, sessionId: string): SessionOverview\n\n /**\n * Enrich each session DTO after construction.\n * Electron uses this to fill aiConversationCount, etc.\n */\n enrichSession?(dto: AnalysisSessionDTO): AnalysisSessionDTO\n}\n\n/**\n * Resolve a fresh session overview through the shared JSON cache. Cache I/O or\n * validation failures fall back to live SQL so the session catalog stays usable.\n */\nexport function resolveValidatedSessionOverview(\n db: DatabaseAdapter,\n sessionId: string,\n cacheDir: string\n): SessionOverview {\n try {\n return getValidatedOverviewCache(db, sessionId, cacheDir)\n } catch {\n return getSessionOverview(db)\n }\n}\n\nfunction buildSession(\n db: DatabaseAdapter,\n id: string,\n dbPath: string,\n options?: ListSessionsOptions\n): AnalysisSessionDTO | null {\n const meta = getSessionMeta(db)\n if (!meta) return null\n\n const overview = options?.resolveOverview ? options.resolveOverview(db, id) : undefined\n const info = overview ? buildSessionInfo(meta, overview, getSummaryCount(db)) : getSessionInfo(db)\n if (!info) return null\n\n let memberAvatar: string | null = null\n if (meta.type === 'private') {\n memberAvatar = getPrivateChatMemberAvatar(db, meta.name, meta.ownerId)\n }\n\n const ownerMember = meta.ownerId\n ? (db\n .prepare(\n `SELECT COALESCE(NULLIF(group_nickname, ''), NULLIF(account_name, ''), platform_id) as displayName\n FROM member\n WHERE platform_id = ?\n LIMIT 1`\n )\n .get(meta.ownerId) as { displayName: string } | undefined)\n : undefined\n\n let dto: AnalysisSessionDTO = {\n ...info,\n id,\n dbPath,\n memberAvatar,\n aiConversationCount: 0,\n ownerName: ownerMember?.displayName ?? null,\n ownerStatus: !meta.ownerId ? 'missing' : ownerMember ? 'resolved' : 'unresolved',\n ownerExcluded: false,\n }\n if (options?.enrichSession) {\n dto = options.enrichSession(dto)\n }\n return dto\n}\n\n/**\n * List all valid sessions, sorted by importedAt descending.\n *\n * Accepts optional hooks so Electron can plug in cached overview resolution\n * and session enrichment without duplicating the listing logic.\n */\nexport function listAnalysisSessions(\n adapter: SessionRuntimeAdapter,\n options?: ListSessionsOptions\n): AnalysisSessionDTO[] {\n const sessionIds = adapter.listSessionIds()\n const sessions: AnalysisSessionDTO[] = []\n\n for (const id of sessionIds) {\n const db = adapter.openReadonly(id)\n if (!db) continue\n if (!isChatSessionDb(db)) continue\n\n const dto = buildSession(db, id, adapter.getDbPath(id), options)\n if (dto) sessions.push(dto)\n }\n\n return sessions.sort((a, b) => b.importedAt - a.importedAt)\n}\n\n/**\n * Get a single session by ID.\n */\nexport function getAnalysisSession(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n options?: ListSessionsOptions\n): AnalysisSessionDTO | null {\n const db = adapter.openReadonly(sessionId)\n if (!db) return null\n return buildSession(db, sessionId, adapter.getDbPath(sessionId), options)\n}\n\n/**\n * Rename a session (updates meta.name).\n */\nexport function renameSession(adapter: SessionRuntimeAdapter, sessionId: string, name: string): void {\n const db = adapter.ensureWritable(sessionId)\n coreRenameSession(db, name)\n}\n\n/**\n * Update session owner_id.\n */\nexport function updateSessionOwnerId(adapter: SessionRuntimeAdapter, sessionId: string, ownerId: string | null): void {\n const db = adapter.ensureWritable(sessionId)\n coreUpdateSessionOwnerId(db, ownerId)\n}\n\n/**\n * Delete a session (close DB + remove files/cache).\n * Returns true if deleted, false if file not found.\n */\nexport function deleteSession(adapter: SessionRuntimeAdapter, sessionId: string): boolean {\n return adapter.deleteSessionFile(sessionId)\n}\n","/**\n * Shared member service layer.\n *\n * Delegates to @openchatlab/core member-ops and query functions,\n * ensuring CLI Web and Electron use identical business semantics\n * (merge strategy, delete scope, name history source, etc.).\n */\n\nimport {\n getMembersWithAliases as coreGetMembersWithAliases,\n getMembersPaginated as coreGetMembersPaginated,\n getMemberNameHistory as coreGetMemberNameHistory,\n updateMemberAliases as coreUpdateMemberAliases,\n mergeMembers as coreMergeMembers,\n deleteMember as coreDeleteMember,\n deleteMembers as coreDeleteMembers,\n} from '@openchatlab/core'\nimport type { MemberWithAliases, MembersPaginationParams, MembersPaginatedResult } from '@openchatlab/core'\nimport { CACHE_KEY_MEMBERS, CACHE_KEY_OVERVIEW, invalidateCache } from '../cache/session-cache'\nimport type { SessionRuntimeAdapter } from './adapters'\n\nexport type { MemberWithAliases, MembersPaginationParams, MembersPaginatedResult }\n\nexport interface MembersPaginatedDTO {\n items: MemberWithAliases[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\nfunction invalidateMemberMutationCaches(sessionId: string, cacheDir: string): void {\n invalidateCache(sessionId, cacheDir, CACHE_KEY_OVERVIEW)\n invalidateCache(sessionId, cacheDir, CACHE_KEY_MEMBERS)\n}\n\n/**\n * Get all members with aliases for a session.\n */\nexport function getMembers(adapter: SessionRuntimeAdapter, sessionId: string): MemberWithAliases[] {\n const db = adapter.ensureReadonly(sessionId)\n return coreGetMembersWithAliases(db)\n}\n\n/**\n * Get paginated member list with search and sort.\n */\nexport function getMembersPaginated(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n params: MembersPaginationParams\n): MembersPaginatedDTO {\n const db = adapter.ensureReadonly(sessionId)\n const result = coreGetMembersPaginated(db, params)\n return {\n items: result.members,\n total: result.total,\n page: result.page,\n pageSize: result.pageSize,\n totalPages: result.totalPages,\n }\n}\n\n/**\n * Update member aliases.\n * Uses core's updateMemberAliases which properly serializes the JSON array.\n */\nexport function updateMemberAliases(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n memberId: number,\n aliases: string[]\n): boolean {\n const db = adapter.ensureWritable(sessionId)\n return coreUpdateMemberAliases(db, memberId, aliases)\n}\n\n/**\n * Merge two members using core semantics:\n * - Primary determined by message count (higher wins, lower id on tie)\n * - Merges aliases, avatar, account_name, group_nickname\n * - Reassigns messages and member_name_history\n * - Updates meta.owner_id if secondary was owner\n * - Runs in a transaction\n */\nexport function mergeMembers(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n memberId1: number,\n memberId2: number,\n cacheDir: string\n): boolean {\n const db = adapter.ensureWritable(sessionId)\n const success = coreMergeMembers(db, memberId1, memberId2)\n if (success) invalidateMemberMutationCaches(sessionId, cacheDir)\n return success\n}\n\n/**\n * Delete a member and all associated data:\n * - messages, member_name_history, member row (in transaction)\n */\nexport function deleteMember(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n memberId: number,\n cacheDir: string\n): boolean {\n const db = adapter.ensureWritable(sessionId)\n const success = coreDeleteMember(db, memberId)\n if (success) invalidateMemberMutationCaches(sessionId, cacheDir)\n return success\n}\n\n/**\n * Delete multiple members and all associated data atomically.\n */\nexport function deleteMembers(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n memberIds: readonly number[],\n cacheDir: string\n): boolean {\n const db = adapter.ensureWritable(sessionId)\n const success = coreDeleteMembers(db, memberIds)\n if (success) invalidateMemberMutationCaches(sessionId, cacheDir)\n return success\n}\n\n/**\n * Get member name change history.\n * Core prefers member_name_history and falls back to message-derived names for legacy sessions.\n */\nexport function getMemberNameHistory(adapter: SessionRuntimeAdapter, sessionId: string, memberId: number) {\n const db = adapter.ensureReadonly(sessionId)\n return coreGetMemberNameHistory(db, memberId)\n}\n","/**\n * Shared session index service.\n *\n * Wraps core's session index operations with proper writable DB handling.\n */\n\nimport {\n generateSessionIndex as coreGenerateSessionIndex,\n generateIncrementalSessionIndex as coreGenerateIncrementalSessionIndex,\n clearSessionIndex as coreClearSessionIndex,\n getSessionIndexStats,\n} from '@openchatlab/core'\nimport type { SessionRuntimeAdapter } from './adapters'\n\nexport function generateIndex(adapter: SessionRuntimeAdapter, sessionId: string, gapThreshold: number = 1800): number {\n const db = adapter.ensureWritable(sessionId)\n return coreGenerateSessionIndex(db, gapThreshold)\n}\n\nexport function generateIncrementalIndex(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n gapThreshold: number = 1800\n): number {\n const db = adapter.ensureWritable(sessionId)\n return coreGenerateIncrementalSessionIndex(db, gapThreshold)\n}\n\nexport function clearIndex(adapter: SessionRuntimeAdapter, sessionId: string): void {\n const db = adapter.ensureWritable(sessionId)\n coreClearSessionIndex(db)\n}\n\nexport interface SessionIndexStatusItem {\n sessionId: string\n hasIndex: boolean\n sessionCount: number\n}\n\nexport function getAllIndexStats(adapter: SessionRuntimeAdapter): SessionIndexStatusItem[] {\n const ids = adapter.listSessionIds()\n return ids.map((sessionId) => {\n try {\n const db = adapter.ensureReadonly(sessionId)\n const stats = getSessionIndexStats(db)\n return { sessionId, hasIndex: stats.hasIndex, sessionCount: stats.sessionCount }\n } catch {\n return { sessionId, hasIndex: false, sessionCount: 0 }\n }\n })\n}\n","/**\n * Shared summary service.\n *\n * Encapsulates LLM config loading, SummaryDeps construction,\n * and summary generation loop — shared across CLI Web and Electron.\n */\n\nimport { getSegmentSummary, saveSegmentSummary, getChatSessionList, loadSegmentMessages } from '@openchatlab/core'\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport {\n generateSessionSummary,\n checkSessionsCanGenerateSummary,\n type SummaryDeps,\n type SummaryOptions,\n completeSimple,\n type PiTextContent,\n} from '../ai'\nimport type { SessionRuntimeAdapter } from './adapters'\n\nexport interface LlmConfig {\n apiKey: string\n}\n\nexport interface SummaryServiceDeps {\n getLlmConfig(): LlmConfig | null\n buildPiModel(config: LlmConfig): ReturnType<typeof import('../ai').buildPiModel>\n}\n\nfunction buildSummaryDeps(db: DatabaseAdapter, llmConfig: LlmConfig, deps: SummaryServiceDeps): SummaryDeps {\n const piModel = deps.buildPiModel(llmConfig)\n return {\n loadMessages(segmentId, limit) {\n return loadSegmentMessages(db, segmentId, limit)\n },\n saveSummary(segmentId, summary, expectedMessageCount) {\n return saveSegmentSummary(db, segmentId, summary, expectedMessageCount)\n },\n getSummary(segmentId) {\n return getSegmentSummary(db, segmentId)\n },\n async llmComplete(systemPrompt, userPrompt, options) {\n const result = await completeSimple(\n piModel,\n {\n systemPrompt,\n messages: [{ role: 'user', content: [{ type: 'text', text: userPrompt }], timestamp: Date.now() }] as any,\n },\n { apiKey: llmConfig.apiKey, maxTokens: options?.maxTokens, temperature: options?.temperature }\n )\n return result.content\n .filter((item): item is PiTextContent => item.type === 'text')\n .map((item) => item.text)\n .join('')\n },\n t: (key: string) => key,\n }\n}\n\nexport async function generateSummary(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n segmentId: number,\n serviceDeps: SummaryServiceDeps,\n options?: SummaryOptions\n) {\n const llmConfig = serviceDeps.getLlmConfig()\n if (!llmConfig) {\n return { success: false as const, error: 'No LLM configuration available' }\n }\n\n const db = adapter.ensureWritable(sessionId)\n const deps = buildSummaryDeps(db, llmConfig, serviceDeps)\n return generateSessionSummary(deps, segmentId, options)\n}\n\nexport async function generateAllSummaries(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n serviceDeps: SummaryServiceDeps,\n options?: SummaryOptions\n) {\n const llmConfig = serviceDeps.getLlmConfig()\n if (!llmConfig) {\n return { success: 0, failed: 0, total: 0, error: 'No LLM configuration available' }\n }\n\n const db = adapter.ensureWritable(sessionId)\n const chatSessions = getChatSessionList(db)\n const deps = buildSummaryDeps(db, llmConfig, serviceDeps)\n\n let success = 0\n let failed = 0\n\n for (const cs of chatSessions) {\n const result = await generateSessionSummary(deps, cs.id, options)\n if (result.success) success++\n else failed++\n }\n\n return { success, failed, total: chatSessions.length }\n}\n\nexport function checkCanGenerate(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n segmentIds: number[]\n): Record<number, { canGenerate: boolean; reason?: string }> {\n const db = adapter.ensureReadonly(sessionId)\n const deps: Pick<SummaryDeps, 'loadMessages' | 't'> = {\n loadMessages(segmentId, limit) {\n return loadSegmentMessages(db, segmentId, limit)\n },\n t: (key: string) => key,\n }\n const resultMap = checkSessionsCanGenerateSummary(deps, segmentIds)\n const result: Record<number, { canGenerate: boolean; reason?: string }> = {}\n for (const [id, info] of resultMap) {\n result[id] = info\n }\n return result\n}\n","/**\n * Shared export service.\n *\n * Wraps node-runtime's exporters with adapter-based DB opening.\n */\n\nimport { exportFilterResultToMarkdown, exportWithFormat, type ExportFilterParams, type ExportResult } from '../export'\nimport type { ExportFormat, FormatExportResult } from '../export'\nimport type { SessionRuntimeAdapter } from './adapters'\n\nexport function exportMarkdown(\n adapter: SessionRuntimeAdapter,\n params: ExportFilterParams\n): { result: ExportResult; content: string } {\n const chunks: string[] = []\n const result = exportFilterResultToMarkdown(\n params,\n {\n openDatabase(sessionId: string) {\n return adapter.openReadonly(sessionId)\n },\n },\n {\n write(chunk: string) {\n chunks.push(chunk)\n },\n end() {\n /* collected in chunks array */\n },\n }\n )\n return { result, content: chunks.join('') }\n}\n\nexport function exportFormatted(\n adapter: SessionRuntimeAdapter,\n params: {\n sessionId: string\n sessionName: string\n format: ExportFormat\n timeFilter?: { startTs: number; endTs: number }\n }\n): FormatExportResult {\n return exportWithFormat(params, (sessionId) => adapter.openReadonly(sessionId))\n}\n","/**\n * Shared owner profile service.\n *\n * Manages the platform-level \"who am I\" identity: writes per-session\n * meta.owner_id, maintains ownerProfilesByPlatform in preferences.json, and\n * applies a confirmed profile to other unowned sessions of the same platform.\n * Used by both CLI Web routes and Electron IPC.\n */\n\nimport {\n getSessionMeta,\n getMembersWithAliases,\n isChatSessionDb,\n updateSessionOwnerId as coreUpdateSessionOwnerId,\n matchOwnerProfile,\n mergeConfirmedNames,\n isNameMatchPlatform,\n} from '@openchatlab/core'\nimport type { MemberWithAliases } from '@openchatlab/core'\nimport type {\n OwnerProfile,\n ApplyOwnerProfileReason,\n ApplyOwnerProfileResult,\n SetOwnerAndApplyProfileResult,\n} from '@openchatlab/shared-types'\nimport type { PreferencesManager } from '../preferences'\nimport type { SessionRuntimeAdapter } from './adapters'\n\nexport type { ApplyOwnerProfileReason, ApplyOwnerProfileResult, SetOwnerAndApplyProfileResult }\n\nfunction memberDisplayName(member: MemberWithAliases): string {\n return member.groupNickname || member.accountName || member.platformId\n}\n\nfunction isExcluded(preferences: PreferencesManager, sessionId: string): boolean {\n return preferences.load().ownerExcludedSessionIds.includes(sessionId)\n}\n\n/**\n * Try to apply the stored platform owner profile to one session.\n * Writes meta.owner_id only on a unique (exact or name) match.\n * Never overrides an existing owner.\n */\nexport function tryApplyOwnerProfile(\n adapter: SessionRuntimeAdapter,\n preferences: PreferencesManager,\n sessionId: string\n): ApplyOwnerProfileResult {\n const db = adapter.openReadonly(sessionId)\n if (!db || !isChatSessionDb(db)) {\n return { applied: false, reason: 'missing_session', excluded: false }\n }\n const meta = getSessionMeta(db)\n if (!meta) {\n return { applied: false, reason: 'missing_session', excluded: false }\n }\n if (meta.ownerId) {\n clearOwnerExclusions(preferences, [sessionId])\n return { applied: false, ownerId: meta.ownerId, reason: 'already_set', excluded: false }\n }\n if (isExcluded(preferences, sessionId)) {\n return { applied: false, reason: 'excluded', excluded: true }\n }\n\n const profile = preferences.load().ownerProfilesByPlatform[meta.platform]\n if (!profile) {\n return { applied: false, reason: 'no_profile', excluded: false }\n }\n\n const result = matchOwnerProfile(meta.platform, profile, getMembersWithAliases(db))\n if (result.type === 'exact' || result.type === 'name') {\n const writable = adapter.ensureWritable(sessionId)\n coreUpdateSessionOwnerId(writable, result.platformId)\n return { applied: true, ownerId: result.platformId, excluded: false }\n }\n return { applied: false, reason: result.type === 'ambiguous' ? 'ambiguous' : 'no_match', excluded: false }\n}\n\n/**\n * Manually set the owner of one session, update the platform profile, and\n * batch-apply the profile to other unowned sessions of the same platform.\n *\n * Re-selecting the same identity merges new names into confirmedNames;\n * selecting a different member replaces the profile (names start fresh,\n * so the previous person's names cannot cause wrong matches).\n */\nexport function setOwnerAndApplyProfile(\n adapter: SessionRuntimeAdapter,\n preferences: PreferencesManager,\n sessionId: string,\n ownerPlatformId: string\n): SetOwnerAndApplyProfileResult {\n const db = adapter.ensureWritable(sessionId)\n const meta = getSessionMeta(db)\n if (!meta) {\n throw Object.assign(new Error(`Session has no meta: ${sessionId}`), { statusCode: 404 })\n }\n\n const members = getMembersWithAliases(db)\n const selected = members.find((m) => m.platformId === ownerPlatformId)\n if (!selected) {\n throw Object.assign(new Error(`Member not found in session: ${ownerPlatformId}`), { statusCode: 400 })\n }\n\n coreUpdateSessionOwnerId(db, ownerPlatformId)\n\n const prefs = preferences.load()\n const existing = prefs.ownerProfilesByPlatform[meta.platform]\n const baseNames = existing && existing.platformId === ownerPlatformId ? existing.confirmedNames : []\n const profile: OwnerProfile = {\n platformId: ownerPlatformId,\n displayName: memberDisplayName(selected),\n confirmedNames: mergeConfirmedNames(baseNames, { ...selected, displayName: memberDisplayName(selected) }),\n matchMode: isNameMatchPlatform(meta.platform) ? 'name' : 'platform_id',\n updatedAt: Date.now(),\n }\n\n const updatedSessions = applyProfileToOtherSessions(\n adapter,\n meta.platform,\n profile,\n sessionId,\n new Set(prefs.ownerExcludedSessionIds)\n )\n const updatedSessionIds = updatedSessions.map((s) => s.id)\n const updatedSessionOwnerIds: Record<string, string> = {}\n for (const s of updatedSessions) {\n updatedSessionOwnerIds[s.id] = s.ownerId\n }\n\n const noLongerExcluded = new Set([sessionId, ...updatedSessionIds])\n preferences.save({\n ownerProfilesByPlatform: { ...prefs.ownerProfilesByPlatform, [meta.platform]: profile },\n ownerExcludedSessionIds: prefs.ownerExcludedSessionIds.filter((id) => !noLongerExcluded.has(id)),\n })\n\n return { sessionId, platform: meta.platform, ownerId: ownerPlatformId, updatedSessionIds, updatedSessionOwnerIds }\n}\n\n/**\n * Apply a profile to all other unowned sessions of the same platform.\n * Unique match only; sessions with an existing owner are never touched.\n * Returns the id and the actual platformId written for each updated session\n * (may differ from profile.platformId on name-match platforms).\n */\nfunction applyProfileToOtherSessions(\n adapter: SessionRuntimeAdapter,\n platform: string,\n profile: OwnerProfile,\n excludeSessionId: string,\n ownerExcludedSessionIds: ReadonlySet<string>\n): Array<{ id: string; ownerId: string }> {\n const updated: Array<{ id: string; ownerId: string }> = []\n for (const id of adapter.listSessionIds()) {\n if (id === excludeSessionId || ownerExcludedSessionIds.has(id)) continue\n try {\n const db = adapter.openReadonly(id)\n if (!db || !isChatSessionDb(db)) continue\n const meta = getSessionMeta(db)\n if (!meta || meta.platform !== platform || meta.ownerId) continue\n\n const result = matchOwnerProfile(platform, profile, getMembersWithAliases(db))\n if (result.type === 'exact' || result.type === 'name') {\n coreUpdateSessionOwnerId(adapter.ensureWritable(id), result.platformId)\n updated.push({ id, ownerId: result.platformId })\n }\n } catch (err) {\n console.warn(`[OwnerProfile] Failed to apply profile to session ${id}:`, err)\n }\n }\n return updated\n}\n\n/**\n * Mark one session as not containing the user. The session is excluded from\n * owner-dependent insights and automatic owner profile application.\n */\nexport function excludeOwnerSession(preferences: PreferencesManager, sessionId: string): void {\n const prefs = preferences.load()\n if (prefs.ownerExcludedSessionIds.includes(sessionId)) return\n preferences.save({\n ownerExcludedSessionIds: [...prefs.ownerExcludedSessionIds, sessionId],\n })\n}\n\nexport function clearOwnerExclusions(preferences: PreferencesManager, sessionIds: Iterable<string>): void {\n const excluded = new Set(sessionIds)\n if (excluded.size === 0) return\n const prefs = preferences.load()\n const next = prefs.ownerExcludedSessionIds.filter((id) => !excluded.has(id))\n if (next.length === prefs.ownerExcludedSessionIds.length) return\n preferences.save({ ownerExcludedSessionIds: next })\n}\n\n/**\n * Clear the owner of one session only. The platform profile is kept and may\n * re-apply automatically later if it still uniquely matches.\n */\nexport function clearSessionOwner(adapter: SessionRuntimeAdapter, sessionId: string): void {\n const db = adapter.ensureWritable(sessionId)\n coreUpdateSessionOwnerId(db, null)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { appLogger } from '../../logging/app-logger'\n\nconst CONTACT_OVERRIDES_FILE_NAME = 'contact-overrides.json'\nconst CONTACT_OVERRIDES_TMP_PREFIX = 'contact-overrides.tmp-'\nconst CONTACT_OVERRIDES_VERSION = 1\n\nexport interface ManualFriendOverride {\n key: string\n createdAt: number\n updatedAt: number\n}\n\nexport interface ContactOverridesFile {\n version: 1\n manualFriends: Record<string, ManualFriendOverride>\n}\n\nexport interface ContactOverrideMutationResult {\n success: boolean\n}\n\nexport function createEmptyContactOverridesFile(): ContactOverridesFile {\n return {\n version: CONTACT_OVERRIDES_VERSION,\n manualFriends: {},\n }\n}\n\nexport function getContactOverridesPath(snapshotDir: string): string {\n return path.join(snapshotDir, CONTACT_OVERRIDES_FILE_NAME)\n}\n\nexport function readContactOverrides(snapshotDir: string): ContactOverridesFile {\n const filePath = getContactOverridesPath(snapshotDir)\n if (!fs.existsSync(filePath)) return createEmptyContactOverridesFile()\n\n try {\n return normalizeContactOverrides(JSON.parse(fs.readFileSync(filePath, 'utf-8')))\n } catch (error) {\n appLogger.warn('contacts', 'contact overrides file is unreadable', error)\n return createEmptyContactOverridesFile()\n }\n}\n\nexport function writeContactOverrides(snapshotDir: string, overrides: ContactOverridesFile): void {\n if (!fs.existsSync(snapshotDir)) fs.mkdirSync(snapshotDir, { recursive: true })\n const tmpPath = path.join(snapshotDir, `${CONTACT_OVERRIDES_TMP_PREFIX}${process.pid}-${Date.now()}`)\n fs.writeFileSync(tmpPath, JSON.stringify(normalizeContactOverrides(overrides), null, 2), 'utf-8')\n fs.renameSync(tmpPath, getContactOverridesPath(snapshotDir))\n}\n\nfunction normalizeContactOverrides(value: unknown): ContactOverridesFile {\n const input = isRecord(value) ? value : {}\n const manualFriendsInput = isRecord(input.manualFriends) ? input.manualFriends : {}\n const manualFriends: Record<string, ManualFriendOverride> = {}\n\n for (const [key, entry] of Object.entries(manualFriendsInput)) {\n if (!key || !isRecord(entry)) continue\n const createdAt = Number.isFinite(entry.createdAt) ? Number(entry.createdAt) : Date.now()\n const updatedAt = Number.isFinite(entry.updatedAt) ? Number(entry.updatedAt) : createdAt\n manualFriends[key] = { key, createdAt, updatedAt }\n }\n\n return {\n version: CONTACT_OVERRIDES_VERSION,\n manualFriends,\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { getDbFileVersion } from '../../cache/analytics-cache'\nimport type { SessionRuntimeAdapter } from '../adapters'\nimport { CONTACTS_ALGORITHM_VERSION } from './compute'\nimport { normalizeContactsTimeRangePreset } from './time-range'\nimport type { ContactsTimeRangePreset } from '@openchatlab/shared-types'\n\nexport function buildContactsSignature(\n adapter: SessionRuntimeAdapter,\n timeRangePreset?: ContactsTimeRangePreset\n): string {\n const parts = [\n `algorithm:${CONTACTS_ALGORITHM_VERSION}`,\n `range:${normalizeContactsTimeRangePreset(timeRangePreset)}`,\n ]\n for (const sessionId of [...adapter.listSessionIds()].sort()) {\n const dbPath = adapter.getDbPath(sessionId)\n parts.push(`${sessionId}:${getDbFileVersion(dbPath)}`)\n }\n return parts.join('|')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ContactsTimeRangePreset } from '@openchatlab/shared-types'\nimport { appLogger } from '../../logging/app-logger'\nimport type { ContactsSnapshot } from './compute'\nimport { normalizeContactsTimeRangePreset } from './time-range'\n\nconst CONTACTS_SNAPSHOT_TMP_PREFIX = 'contacts-snapshot.tmp-'\n\nexport interface ReadContactsSnapshotOptions {\n now?: () => number\n}\n\nexport function getContactsSnapshotPath(snapshotDir: string, timeRangePreset?: ContactsTimeRangePreset): string {\n const preset = normalizeContactsTimeRangePreset(timeRangePreset)\n return path.join(snapshotDir, `contacts-snapshot-${preset}.json`)\n}\n\nexport function readContactsSnapshot(\n snapshotDir: string,\n timeRangePreset?: ContactsTimeRangePreset,\n options: ReadContactsSnapshotOptions = {}\n): ContactsSnapshot | null {\n const snapshotPath = getContactsSnapshotPath(snapshotDir, timeRangePreset)\n if (!fs.existsSync(snapshotPath)) return null\n\n try {\n return JSON.parse(fs.readFileSync(snapshotPath, 'utf-8')) as ContactsSnapshot\n } catch (error) {\n const ts = options.now?.() ?? Date.now()\n const backupPath = path.join(snapshotDir, `contacts-snapshot.corrupt-${ts}.json`)\n try {\n fs.renameSync(snapshotPath, backupPath)\n } catch (renameError) {\n appLogger.warn('contacts', 'failed to backup corrupt contacts snapshot', renameError)\n }\n appLogger.warn('contacts', 'contacts snapshot is corrupt', error)\n return null\n }\n}\n\nexport function writeContactsSnapshot(snapshotDir: string, snapshot: ContactsSnapshot): void {\n if (!fs.existsSync(snapshotDir)) fs.mkdirSync(snapshotDir, { recursive: true })\n const tmpPath = path.join(snapshotDir, `${CONTACTS_SNAPSHOT_TMP_PREFIX}${process.pid}-${Date.now()}`)\n fs.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), 'utf-8')\n fs.renameSync(tmpPath, getContactsSnapshotPath(snapshotDir, snapshot.timeRange.preset))\n}\n\nexport function cleanupContactsSnapshotTempFiles(snapshotDir: string): void {\n if (!fs.existsSync(snapshotDir)) return\n for (const name of fs.readdirSync(snapshotDir)) {\n if (!name.startsWith(CONTACTS_SNAPSHOT_TMP_PREFIX)) continue\n try {\n fs.rmSync(path.join(snapshotDir, name), { force: true })\n } catch (error) {\n appLogger.warn('contacts', 'failed to remove contacts snapshot temp file', error)\n }\n }\n}\n","import { Worker } from 'node:worker_threads'\nimport type { WorkerOptions } from 'node:worker_threads'\nimport { existsSync } from 'node:fs'\nimport type { PathProvider } from '@openchatlab/core'\nimport type { RuntimeIdentity } from '../../data-dir-compat'\nimport { snapshotPathProvider } from '../../semantic-index/static-path-provider'\nimport type { ContactsComputeProgress, ContactsSnapshot } from './compute'\nimport type { ContactsComputeRunner } from './service'\n\nexport interface ContactsWorkerRunnerOptions {\n pathProvider: PathProvider\n runtimeIdentity?: RuntimeIdentity\n nativeBinding?: string\n workerEntryUrl?: string | URL\n}\n\ninterface ContactsWorkerMessage {\n type: 'progress' | 'success' | 'error'\n progress?: ContactsComputeProgress\n snapshot?: ContactsSnapshot\n error?: string\n}\n\ntype ModuleWorkerOptions = WorkerOptions & { type: 'module' }\ntype EntryExists = (url: URL) => boolean\n\nexport function resolveDefaultContactsWorkerEntryUrl(\n currentModuleUrl: string | URL = import.meta.url,\n entryExists: EntryExists = (url) => existsSync(url)\n): URL {\n const moduleUrl = typeof currentModuleUrl === 'string' ? currentModuleUrl : currentModuleUrl.href\n if (moduleUrl.endsWith('.ts')) return new URL('./worker-entry.ts', moduleUrl)\n if (moduleUrl.endsWith('.mjs')) return new URL('./contacts-worker.mjs', moduleUrl)\n\n const siblingWorkerEntry = new URL('./worker-entry.js', moduleUrl)\n return entryExists(siblingWorkerEntry) ? siblingWorkerEntry : new URL('./contacts-worker.js', moduleUrl)\n}\n\nfunction normalizeWorkerEntryUrl(entryUrl?: string | URL): URL {\n if (!entryUrl) return resolveDefaultContactsWorkerEntryUrl()\n return typeof entryUrl === 'string' ? new URL(entryUrl) : entryUrl\n}\n\nfunction createWorker(workerData: unknown, entryUrlInput?: string | URL): Worker {\n const entryUrl = normalizeWorkerEntryUrl(entryUrlInput)\n if (!entryUrl.href.endsWith('.ts')) return new Worker(entryUrl, { workerData })\n\n const bootstrap = `\n import { register } from 'tsx/esm/api';\n register();\n await import(${JSON.stringify(entryUrl.href)});\n `\n const options: ModuleWorkerOptions = {\n eval: true,\n type: 'module',\n workerData,\n execArgv: [],\n }\n return new Worker(bootstrap, options)\n}\n\nexport function createContactsWorkerRunner(options: ContactsWorkerRunnerOptions): ContactsComputeRunner {\n return ({ signature, timeRangePreset, signal, onProgress }) =>\n new Promise<ContactsSnapshot>((resolve, reject) => {\n if (signal.aborted) {\n reject(createAbortError())\n return\n }\n\n const worker = createWorker(\n {\n paths: snapshotPathProvider(options.pathProvider),\n runtimeIdentity: options.runtimeIdentity,\n nativeBinding: options.nativeBinding,\n signature,\n timeRangePreset,\n },\n options.workerEntryUrl\n )\n let settled = false\n const abort = () => {\n if (settled) return\n settled = true\n void worker.terminate()\n reject(createAbortError())\n }\n signal.addEventListener('abort', abort, { once: true })\n\n worker.on('message', (message: ContactsWorkerMessage) => {\n if (message.type === 'progress' && message.progress) {\n onProgress(message.progress)\n return\n }\n if (message.type === 'success' && message.snapshot) {\n settled = true\n signal.removeEventListener('abort', abort)\n resolve(message.snapshot)\n void worker.terminate()\n return\n }\n if (message.type === 'error') {\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(message.error ?? 'contacts worker failed'))\n void worker.terminate()\n }\n })\n worker.on('error', (error) => {\n if (settled) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(error)\n })\n worker.on('exit', (code) => {\n if (settled || code === 0) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(`contacts worker exited with code ${code}`))\n })\n })\n}\n\nfunction createAbortError(): Error {\n return new Error('contacts worker aborted')\n}\n","import type { PathProvider } from '@openchatlab/core'\nimport type {\n ContactItem,\n ContactListItem,\n ContactPool,\n ContactDetailResponse,\n ContactsCacheState,\n ContactsDiagnostics,\n ContactsResponse,\n ContactsTaskState,\n ContactsTimeRangePreset,\n} from '@openchatlab/shared-types'\nimport type { RuntimeIdentity } from '../../data-dir-compat'\nimport { appLogger } from '../../logging/app-logger'\nimport type { SessionRuntimeAdapter } from '../adapters'\nimport {\n CONTACTS_ALGORITHM_VERSION,\n createEmptyContactsDiagnostics,\n type ContactsComputeProgress,\n type ContactsSnapshot,\n} from './compute'\nimport {\n readContactOverrides,\n writeContactOverrides,\n type ContactOverrideMutationResult,\n type ContactOverridesFile,\n} from './overrides'\nimport { buildContactsSignature } from './signature'\nimport { cleanupContactsSnapshotTempFiles, readContactsSnapshot, writeContactsSnapshot } from './snapshot'\nimport { normalizeContactsTimeRangePreset, resolveContactsTimeRange } from './time-range'\nimport { createContactsWorkerRunner } from './worker-runner'\nimport { getContactsDir } from './paths'\n\nconst DEFAULT_CONTACTS_PAGE_SIZE = 100\nconst MAX_CONTACTS_PAGE_SIZE = 200\n\nexport interface ContactsServiceOptions {\n forceRecompute?: boolean\n acceptStale?: boolean\n timeRangePreset?: ContactsTimeRangePreset\n pool?: ContactPool\n page?: number\n pageSize?: number\n query?: string\n}\n\nexport interface ContactsRunnerOptions {\n signature: string\n timeRangePreset: ContactsTimeRangePreset\n onProgress: (progress: ContactsComputeProgress) => void\n signal: AbortSignal\n}\n\nexport type ContactsComputeRunner = (options: ContactsRunnerOptions) => Promise<ContactsSnapshot>\n\nexport interface ContactsServiceDeps {\n adapter: SessionRuntimeAdapter\n systemDir?: string\n pathProvider?: PathProvider\n runtimeIdentity?: RuntimeIdentity\n nativeBinding?: string\n workerEntryUrl?: string | URL\n runner?: ContactsComputeRunner\n now?: () => number\n}\n\nexport interface ContactsService {\n getContacts(options?: ContactsServiceOptions): ContactsResponse\n getContactsPage(options?: ContactsServiceOptions): ContactsResponse\n getContactDetail(key: string, options?: ContactsServiceOptions): ContactDetailResponse\n markContactAsFriend(key: string, options?: ContactsServiceOptions): ContactOverrideMutationResult\n unmarkContactAsFriend(key: string, options?: ContactsServiceOptions): ContactOverrideMutationResult\n startRecompute(options?: ContactsServiceOptions): ContactsResponse\n invalidateContactsCache(): void\n close(): Promise<void>\n replaceSnapshotForTests?(snapshot: ContactsSnapshot): void\n}\n\ninterface InFlightTask {\n id: string\n signature: string\n promise: Promise<ContactsSnapshot>\n abortController: AbortController\n}\n\nexport function createContactsService(deps: ContactsServiceDeps): ContactsService {\n return new DefaultContactsService(deps)\n}\n\nclass DefaultContactsService implements ContactsService {\n private readonly snapshots = new Map<ContactsTimeRangePreset, ContactsSnapshot | null>()\n private inFlight: InFlightTask | null = null\n private task: ContactsTaskState = createIdleTaskState()\n private readonly snapshotDir: string\n private readonly runner: ContactsComputeRunner\n\n constructor(private readonly deps: ContactsServiceDeps) {\n this.snapshotDir = resolveContactsSnapshotDir(deps)\n cleanupContactsSnapshotTempFiles(this.snapshotDir)\n this.runner =\n deps.runner ??\n createContactsWorkerRunner({\n pathProvider: requirePathProvider(deps),\n runtimeIdentity: deps.runtimeIdentity,\n nativeBinding: deps.nativeBinding,\n workerEntryUrl: deps.workerEntryUrl,\n })\n }\n\n getContacts(options: ContactsServiceOptions = {}): ContactsResponse {\n return this.getContactsPage(options)\n }\n\n getContactsPage(options: ContactsServiceOptions = {}): ContactsResponse {\n const timeRangePreset = normalizeContactsTimeRangePreset(options.timeRangePreset)\n const signature = buildContactsSignature(this.deps.adapter, timeRangePreset)\n const cacheStatus = this.getCacheStatus(signature, timeRangePreset)\n if (this.shouldStartTaskFromRead(options, cacheStatus)) this.ensureTaskStarted(signature, timeRangePreset)\n return this.toResponse(signature, { ...options, timeRangePreset })\n }\n\n getContactDetail(key: string, options: ContactsServiceOptions = {}): ContactDetailResponse {\n const timeRangePreset = normalizeContactsTimeRangePreset(options.timeRangePreset)\n const signature = buildContactsSignature(this.deps.adapter, timeRangePreset)\n const cacheStatus = this.getCacheStatus(signature, timeRangePreset)\n if (this.shouldStartTaskFromRead(options, cacheStatus)) this.ensureTaskStarted(signature, timeRangePreset)\n const snapshot = this.getSnapshot(timeRangePreset)\n const status = this.getCacheStatus(signature, timeRangePreset)\n const includeSnapshot = status === 'fresh' || (status === 'stale' && options.acceptStale === true)\n const contacts = includeSnapshot ? this.getContactsWithOverrides(snapshot?.contacts ?? []) : []\n const contact = contacts.find((item) => item.key === key) ?? null\n return {\n contact: contact ? sanitizeContactItem(contact) : null,\n algorithmVersion: includeSnapshot\n ? (snapshot?.algorithmVersion ?? CONTACTS_ALGORITHM_VERSION)\n : CONTACTS_ALGORITHM_VERSION,\n timeRange: snapshot?.timeRange ?? resolveContactsTimeRange(timeRangePreset, null),\n cache: this.toCacheState(status, snapshot),\n task: this.task,\n }\n }\n\n markContactAsFriend(key: string, options: ContactsServiceOptions = {}): ContactOverrideMutationResult {\n const timeRangePreset = normalizeContactsTimeRangePreset(options.timeRangePreset)\n const snapshot = this.getSnapshot(timeRangePreset)\n const contact = snapshot?.contacts.find((item) => item.key === key)\n if (!contact) throw createContactNotFoundError(key)\n if (contact.pool === 'friend') return { success: true }\n\n const now = this.now()\n const overrides = this.readOverrides()\n const existing = overrides.manualFriends[key]\n overrides.manualFriends[key] = {\n key,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n }\n this.writeOverrides(overrides, 'mark contact as friend')\n return { success: true }\n }\n\n unmarkContactAsFriend(key: string): ContactOverrideMutationResult {\n const overrides = this.readOverrides()\n if (!overrides.manualFriends[key]) return { success: true }\n delete overrides.manualFriends[key]\n this.writeOverrides(overrides, 'unmark contact as friend')\n return { success: true }\n }\n\n startRecompute(options: ContactsServiceOptions = {}): ContactsResponse {\n const timeRangePreset = normalizeContactsTimeRangePreset(options.timeRangePreset)\n const signature = buildContactsSignature(this.deps.adapter, timeRangePreset)\n this.ensureTaskStarted(signature, timeRangePreset)\n return this.toResponse(signature, { ...options, acceptStale: true, timeRangePreset })\n }\n\n invalidateContactsCache(): void {\n this.snapshots.clear()\n }\n\n async close(): Promise<void> {\n const inFlight = this.inFlight\n if (!inFlight) return\n this.inFlight = null\n inFlight.abortController.abort()\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n lastError: 'contacts task aborted',\n }\n }\n\n replaceSnapshotForTests(snapshot: ContactsSnapshot): void {\n this.snapshots.set(snapshot.timeRange.preset, snapshot)\n }\n\n private shouldStartTaskFromRead(options: ContactsServiceOptions, cacheStatus: ContactsCacheState['status']): boolean {\n if (options.forceRecompute) return true\n if (cacheStatus === 'fresh') return false\n return this.task.status !== 'failed'\n }\n\n private ensureTaskStarted(signature: string, timeRangePreset: ContactsTimeRangePreset): void {\n if (this.inFlight) return\n\n const taskId = `contacts_${this.now()}_${Math.random().toString(36).slice(2)}`\n this.task = {\n id: taskId,\n status: 'running',\n startedAt: this.now(),\n finishedAt: null,\n processedSessions: 0,\n totalSessions: this.deps.adapter.listSessionIds().length,\n timeRangePreset,\n }\n\n const abortController = new AbortController()\n const promise = this.runner({\n signature,\n timeRangePreset,\n signal: abortController.signal,\n onProgress: (progress) => {\n if (this.task.id !== taskId || this.task.status !== 'running') return\n this.task = {\n ...this.task,\n processedSessions: progress.processedSessions,\n totalSessions: progress.totalSessions,\n currentSessionId: progress.currentSessionId,\n }\n },\n })\n this.inFlight = { id: taskId, signature, promise, abortController }\n\n promise\n .then((snapshot) => this.handleTaskSuccess(taskId, signature, snapshot))\n .catch((error) => this.handleTaskFailure(taskId, error))\n }\n\n private handleTaskSuccess(taskId: string, inputSignature: string, snapshot: ContactsSnapshot): void {\n if (this.inFlight?.id !== taskId) return\n this.inFlight = null\n const latestSignature = buildContactsSignature(this.deps.adapter, snapshot.timeRange.preset)\n const finishedAt = this.now()\n\n if (inputSignature !== latestSignature || snapshot.signature !== latestSignature) {\n this.task = {\n ...this.task,\n status: 'superseded',\n finishedAt,\n }\n appLogger.info('contacts', 'contacts worker result discarded because signature changed', {\n inputSignature,\n latestSignature,\n })\n return\n }\n\n try {\n writeContactsSnapshot(this.snapshotDir, snapshot)\n this.snapshots.set(snapshot.timeRange.preset, snapshot)\n this.task = {\n ...this.task,\n status: 'succeeded',\n finishedAt,\n processedSessions: snapshot.workerStats.processedSessions,\n totalSessions: snapshot.workerStats.totalSessions,\n currentSessionId: undefined,\n }\n appLogger.info('contacts', 'contacts worker snapshot persisted', {\n contactCount: snapshot.contacts.length,\n durationMs: snapshot.workerStats.durationMs,\n })\n } catch (error) {\n this.handleTaskFailure(taskId, error)\n }\n }\n\n private handleTaskFailure(taskId: string, error: unknown): void {\n if (this.inFlight?.id === taskId) this.inFlight = null\n const message = error instanceof Error ? error.message : String(error)\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n lastError: message,\n }\n appLogger.error('contacts', 'contacts worker failed', error)\n }\n\n private getCacheStatus(signature: string, timeRangePreset: ContactsTimeRangePreset): ContactsCacheState['status'] {\n const snapshot = this.getSnapshot(timeRangePreset)\n if (!snapshot) return 'missing'\n return snapshot.signature === signature ? 'fresh' : 'stale'\n }\n\n private toResponse(signature: string, options: ContactsServiceOptions = {}): ContactsResponse {\n const timeRangePreset = normalizeContactsTimeRangePreset(options.timeRangePreset)\n const snapshot = this.getSnapshot(timeRangePreset)\n const status = this.getCacheStatus(signature, timeRangePreset)\n const includeSnapshot = status === 'fresh' || (status === 'stale' && options.acceptStale === true)\n const allContacts = includeSnapshot ? this.getContactsWithOverrides(snapshot?.contacts ?? []) : []\n const stats = buildContactsStats(allContacts)\n const page = normalizePositiveInt(options.page, 1)\n const pageSize = normalizePageSize(options.pageSize)\n const query = options.query?.trim().toLowerCase() ?? ''\n const filteredContacts = allContacts\n .filter((contact) => {\n if (options.pool && contact.pool !== options.pool) return false\n if (query && !contact.searchText.includes(query)) return false\n return true\n })\n .sort((a, b) => compareContactsForResponse(a, b, options.pool))\n const offset = (page - 1) * pageSize\n const pageContacts = filteredContacts.slice(offset, offset + pageSize).map(toContactListItem)\n return {\n contacts: pageContacts,\n diagnostics: includeSnapshot\n ? sanitizeContactsDiagnostics(snapshot?.diagnostics ?? createEmptyContactsDiagnostics())\n : createEmptyContactsDiagnostics(),\n algorithmVersion: includeSnapshot\n ? (snapshot?.algorithmVersion ?? CONTACTS_ALGORITHM_VERSION)\n : CONTACTS_ALGORITHM_VERSION,\n timeRange: snapshot?.timeRange ?? resolveContactsTimeRange(timeRangePreset, null),\n cache: this.toCacheState(status, snapshot),\n pagination: {\n page,\n pageSize,\n total: filteredContacts.length,\n hasMore: offset + pageContacts.length < filteredContacts.length,\n },\n stats,\n task: this.task,\n }\n }\n\n private toCacheState(status: ContactsCacheState['status'], snapshot: ContactsSnapshot | null): ContactsCacheState {\n return {\n status,\n computedAt: snapshot?.computedAt ?? null,\n signature: snapshot?.signature,\n staleReason: status === 'stale' ? 'signature_changed' : undefined,\n }\n }\n\n private getSnapshot(timeRangePreset: ContactsTimeRangePreset): ContactsSnapshot | null {\n if (!this.snapshots.has(timeRangePreset)) {\n this.snapshots.set(\n timeRangePreset,\n readContactsSnapshot(this.snapshotDir, timeRangePreset, { now: this.deps.now })\n )\n }\n return this.snapshots.get(timeRangePreset) ?? null\n }\n\n private now(): number {\n return this.deps.now?.() ?? Date.now()\n }\n\n private getContactsWithOverrides(contacts: ContactItem[]): ContactItem[] {\n return applyContactOverrides(contacts, this.readOverrides())\n }\n\n private readOverrides(): ContactOverridesFile {\n return readContactOverrides(this.snapshotDir)\n }\n\n private writeOverrides(overrides: ContactOverridesFile, action: string): void {\n try {\n writeContactOverrides(this.snapshotDir, overrides)\n appLogger.info('contacts', `contact override saved: ${action}`, {\n manualFriendCount: Object.keys(overrides.manualFriends).length,\n })\n } catch (error) {\n appLogger.error('contacts', `failed to save contact override: ${action}`, error)\n throw error\n }\n }\n}\n\nfunction applyContactOverrides(contacts: ContactItem[], overrides: ContactOverridesFile): ContactItem[] {\n return contacts.map((contact) => {\n if (contact.pool === 'friend') {\n return {\n ...contact,\n isFriend: true,\n pool: 'friend',\n friendSource: 'private',\n }\n }\n if (overrides.manualFriends[contact.key]) {\n return {\n ...contact,\n isFriend: true,\n pool: 'friend',\n friendSource: 'manual',\n }\n }\n const { friendSource: _friendSource, ...rest } = contact\n return {\n ...rest,\n isFriend: false,\n pool: 'non_friend',\n }\n })\n}\n\nfunction compareContactsForResponse(a: ContactItem, b: ContactItem, pool: ContactPool | undefined): number {\n if (pool === 'friend') {\n const rankDiff = friendSortRank(a) - friendSortRank(b)\n if (rankDiff !== 0) return rankDiff\n }\n return b.score - a.score || a.displayName.localeCompare(b.displayName)\n}\n\nfunction friendSortRank(contact: ContactItem): number {\n return contact.friendSource === 'manual' ? 1 : 0\n}\n\nfunction buildContactsStats(contacts: ContactItem[]): { friendsTotal: number; nonFriendsTotal: number } {\n let friendsTotal = 0\n let nonFriendsTotal = 0\n for (const contact of contacts) {\n if (contact.pool === 'friend') friendsTotal++\n else if (contact.pool === 'non_friend') nonFriendsTotal++\n }\n return { friendsTotal, nonFriendsTotal }\n}\n\nfunction toContactListItem(contact: ContactItem): ContactListItem {\n const item = sanitizeContactItem(contact)\n const { sourceSessions: _sourceSessions, searchText: _searchText, ...listItem } = item\n return listItem\n}\n\nfunction sanitizeContactItem(contact: ContactItem): ContactItem {\n const item: ContactItem = {\n key: contact.key,\n platform: contact.platform,\n platformId: contact.platformId,\n sessionScoped: contact.sessionScoped,\n displayName: contact.displayName,\n aliases: contact.aliases,\n avatar: contact.avatar,\n isFriend: contact.isFriend,\n pool: contact.pool,\n score: contact.score,\n scoreBreakdown: contact.scoreBreakdown,\n sourceSessions: contact.sourceSessions,\n searchText: contact.searchText,\n lastInteractionTs: contact.lastInteractionTs,\n }\n if (contact.sessionId) item.sessionId = contact.sessionId\n if (contact.friendSource) item.friendSource = contact.friendSource\n return item\n}\n\nfunction createContactNotFoundError(key: string): Error {\n return Object.assign(new Error(`Contact not found: ${key}`), {\n statusCode: 404,\n code: 'CONTACT_NOT_FOUND',\n })\n}\n\nfunction sanitizeContactsDiagnostics(diagnostics: ContactsDiagnostics): ContactsDiagnostics {\n return {\n privateSessionCount: diagnostics.privateSessionCount,\n activePrivateSessionCount: diagnostics.activePrivateSessionCount,\n contactsEnabled: diagnostics.contactsEnabled,\n skippedMissingOwnerSessions: diagnostics.skippedMissingOwnerSessions,\n skippedUnresolvedOwnerSessions: diagnostics.skippedUnresolvedOwnerSessions,\n skippedAmbiguousPrivateSessions: diagnostics.skippedAmbiguousPrivateSessions,\n skippedInvalidPlatformIdMembers: diagnostics.skippedInvalidPlatformIdMembers,\n skippedFailedSessions: diagnostics.skippedFailedSessions,\n warnings: diagnostics.warnings,\n }\n}\n\nfunction normalizePositiveInt(value: number | undefined, fallback: number): number {\n if (!Number.isFinite(value)) return fallback\n return Math.max(1, Math.trunc(value!))\n}\n\nfunction normalizePageSize(value: number | undefined): number {\n return Math.min(MAX_CONTACTS_PAGE_SIZE, normalizePositiveInt(value, DEFAULT_CONTACTS_PAGE_SIZE))\n}\n\nfunction createIdleTaskState(): ContactsTaskState {\n return {\n id: null,\n status: 'idle',\n startedAt: null,\n finishedAt: null,\n processedSessions: 0,\n totalSessions: 0,\n }\n}\n\nfunction requirePathProvider(deps: ContactsServiceDeps): PathProvider {\n if (!deps.pathProvider) {\n throw new Error('contacts worker runner requires pathProvider')\n }\n return deps.pathProvider\n}\n\nfunction resolveContactsSnapshotDir(deps: ContactsServiceDeps): string {\n if (deps.pathProvider) return getContactsDir(deps.pathProvider.getUserDataDir())\n if (deps.systemDir) return deps.systemDir\n throw new Error('contacts service requires systemDir or pathProvider')\n}\n","export { CONTACTS_ALGORITHM_VERSION } from './compute'\nexport { createContactsService } from './service'\nexport { getContactsDir, getContactsFactsCacheDir } from './paths'\nexport type {\n ContactsComputeRunner,\n ContactsService,\n ContactsServiceDeps,\n ContactsServiceOptions,\n ContactsRunnerOptions,\n} from './service'\n","import type { ContactsTimeRangePreset } from '@openchatlab/shared-types'\nimport { getDbFileVersion } from '../../../cache/analytics-cache'\nimport type { SessionRuntimeAdapter } from '../../adapters'\nimport { PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION } from './compute'\nimport { normalizePeopleRelationshipsTimeRangePreset } from './time-range'\n\nexport function buildPeopleRelationshipsSignature(\n adapter: SessionRuntimeAdapter,\n timeRangePreset?: ContactsTimeRangePreset\n): string {\n const parts = [\n `algorithm:${PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION}`,\n `range:${normalizePeopleRelationshipsTimeRangePreset(timeRangePreset)}`,\n ]\n for (const sessionId of [...adapter.listSessionIds()].sort()) {\n const dbPath = adapter.getDbPath(sessionId)\n parts.push(`${sessionId}:${getDbFileVersion(dbPath)}`)\n }\n return parts.join('|')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ContactsTimeRangePreset } from '@openchatlab/shared-types'\nimport { appLogger } from '../../../logging/app-logger'\nimport type { PeopleRelationshipsSnapshot } from './compute'\nimport { normalizePeopleRelationshipsTimeRangePreset } from './time-range'\n\nconst PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX = 'people-relationships-snapshot.tmp-'\n\nexport interface ReadPeopleRelationshipsSnapshotOptions {\n now?: () => number\n}\n\nexport function getPeopleRelationshipsSnapshotPath(\n snapshotDir: string,\n timeRangePreset?: ContactsTimeRangePreset\n): string {\n const preset = normalizePeopleRelationshipsTimeRangePreset(timeRangePreset)\n return path.join(snapshotDir, `graph-snapshot-${preset}.json`)\n}\n\nexport function readPeopleRelationshipsSnapshot(\n snapshotDir: string,\n timeRangePreset?: ContactsTimeRangePreset,\n options: ReadPeopleRelationshipsSnapshotOptions = {}\n): PeopleRelationshipsSnapshot | null {\n const snapshotPath = getPeopleRelationshipsSnapshotPath(snapshotDir, timeRangePreset)\n if (!fs.existsSync(snapshotPath)) return null\n\n try {\n return JSON.parse(fs.readFileSync(snapshotPath, 'utf-8')) as PeopleRelationshipsSnapshot\n } catch (error) {\n const ts = options.now?.() ?? Date.now()\n const backupPath = path.join(snapshotDir, `graph-snapshot.corrupt-${ts}.json`)\n try {\n fs.renameSync(snapshotPath, backupPath)\n } catch (renameError) {\n appLogger.warn('people-relationships', 'failed to backup corrupt people relationships snapshot', renameError)\n }\n appLogger.warn('people-relationships', 'people relationships snapshot is corrupt', error)\n return null\n }\n}\n\nexport function writePeopleRelationshipsSnapshot(snapshotDir: string, snapshot: PeopleRelationshipsSnapshot): void {\n if (!fs.existsSync(snapshotDir)) fs.mkdirSync(snapshotDir, { recursive: true })\n const tmpPath = path.join(snapshotDir, `${PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX}${process.pid}-${Date.now()}`)\n fs.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), 'utf-8')\n fs.renameSync(tmpPath, getPeopleRelationshipsSnapshotPath(snapshotDir, snapshot.timeRange.preset))\n}\n\nexport function cleanupPeopleRelationshipsSnapshotTempFiles(snapshotDir: string): void {\n if (!fs.existsSync(snapshotDir)) return\n for (const name of fs.readdirSync(snapshotDir)) {\n if (!name.startsWith(PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX)) continue\n try {\n fs.rmSync(path.join(snapshotDir, name), { force: true })\n } catch (error) {\n appLogger.warn('people-relationships', 'failed to remove people relationships snapshot temp file', error)\n }\n }\n}\n","import { existsSync } from 'node:fs'\nimport { Worker } from 'node:worker_threads'\nimport type { WorkerOptions } from 'node:worker_threads'\nimport type { PathProvider } from '@openchatlab/core'\nimport type { RuntimeIdentity } from '../../../data-dir-compat'\nimport { snapshotPathProvider } from '../../../semantic-index/static-path-provider'\nimport type { PeopleRelationshipsComputeProgress, PeopleRelationshipsSnapshot } from './compute'\nimport type { PeopleRelationshipsComputeRunner } from './service'\n\nexport interface PeopleRelationshipsWorkerRunnerOptions {\n pathProvider: PathProvider\n runtimeIdentity?: RuntimeIdentity\n nativeBinding?: string\n workerEntryUrl?: string | URL\n}\n\ninterface PeopleRelationshipsWorkerMessage {\n type: 'progress' | 'success' | 'error'\n progress?: PeopleRelationshipsComputeProgress\n snapshot?: PeopleRelationshipsSnapshot\n error?: string\n}\n\ntype ModuleWorkerOptions = WorkerOptions & { type: 'module' }\ntype EntryExists = (url: URL) => boolean\n\nexport function resolveDefaultPeopleRelationshipsWorkerEntryUrl(\n currentModuleUrl: string | URL = import.meta.url,\n entryExists: EntryExists = (url) => existsSync(url)\n): URL {\n const moduleUrl = typeof currentModuleUrl === 'string' ? currentModuleUrl : currentModuleUrl.href\n if (moduleUrl.endsWith('.ts')) return new URL('./worker-entry.ts', moduleUrl)\n if (moduleUrl.endsWith('.mjs')) return new URL('./people-relationships-worker.mjs', moduleUrl)\n\n const siblingWorkerEntry = new URL('./worker-entry.js', moduleUrl)\n return entryExists(siblingWorkerEntry) ? siblingWorkerEntry : new URL('./people-relationships-worker.js', moduleUrl)\n}\n\nfunction normalizeWorkerEntryUrl(entryUrl?: string | URL): URL {\n if (!entryUrl) return resolveDefaultPeopleRelationshipsWorkerEntryUrl()\n return typeof entryUrl === 'string' ? new URL(entryUrl) : entryUrl\n}\n\nfunction createWorker(workerData: unknown, entryUrlInput?: string | URL): Worker {\n const entryUrl = normalizeWorkerEntryUrl(entryUrlInput)\n if (!entryUrl.href.endsWith('.ts')) return new Worker(entryUrl, { workerData })\n\n const bootstrap = `\n import { register } from 'tsx/esm/api';\n register();\n await import(${JSON.stringify(entryUrl.href)});\n `\n const options: ModuleWorkerOptions = {\n eval: true,\n type: 'module',\n workerData,\n execArgv: [],\n }\n return new Worker(bootstrap, options)\n}\n\nexport function createPeopleRelationshipsWorkerRunner(\n options: PeopleRelationshipsWorkerRunnerOptions\n): PeopleRelationshipsComputeRunner {\n return ({ signature, timeRangePreset, signal, onProgress }) =>\n new Promise<PeopleRelationshipsSnapshot>((resolve, reject) => {\n if (signal.aborted) {\n reject(createAbortError())\n return\n }\n\n const worker = createWorker(\n {\n paths: snapshotPathProvider(options.pathProvider),\n runtimeIdentity: options.runtimeIdentity,\n nativeBinding: options.nativeBinding,\n signature,\n timeRangePreset,\n },\n options.workerEntryUrl\n )\n let settled = false\n const abort = () => {\n if (settled) return\n settled = true\n void worker.terminate()\n reject(createAbortError())\n }\n signal.addEventListener('abort', abort, { once: true })\n\n worker.on('message', (message: PeopleRelationshipsWorkerMessage) => {\n if (message.type === 'progress' && message.progress) {\n onProgress(message.progress)\n return\n }\n if (message.type === 'success' && message.snapshot) {\n settled = true\n signal.removeEventListener('abort', abort)\n resolve(message.snapshot)\n void worker.terminate()\n return\n }\n if (message.type === 'error') {\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(message.error ?? 'people relationships worker failed'))\n void worker.terminate()\n }\n })\n worker.on('error', (error) => {\n if (settled) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(error)\n })\n worker.on('exit', (code) => {\n if (settled || code === 0) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(`people relationships worker exited with code ${code}`))\n })\n })\n}\n\nfunction createAbortError(): Error {\n return new Error('people relationships worker aborted')\n}\n","import type { PathProvider } from '@openchatlab/core'\nimport type {\n ContactsTimeRangePreset,\n PeopleRelationshipCommunity,\n PeopleRelationshipGraphNode,\n PeopleRelationshipsGraphData,\n PeopleRelationshipsCacheState,\n PeopleRelationshipsDiagnostics,\n PeopleRelationshipsGraphScope,\n PeopleRelationshipsGraphResponse,\n PeopleRelationshipsNeighborhoodResponse,\n PeopleRelationshipsSearchResult,\n PeopleRelationshipsTaskState,\n} from '@openchatlab/shared-types'\nimport type { RuntimeIdentity } from '../../../data-dir-compat'\nimport { appLogger } from '../../../logging/app-logger'\nimport type { SessionRuntimeAdapter } from '../../adapters'\nimport { readContactOverrides } from '../../contacts/overrides'\nimport { getContactsDir } from '../../contacts/paths'\nimport {\n buildPeopleRelationshipsNeighborhoodGraph,\n compareEdges,\n compareNodes,\n PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION,\n type PeopleRelationshipsComputeProgress,\n type PeopleRelationshipsSnapshot,\n} from './compute'\nimport { getPeopleRelationshipsDir } from './paths'\nimport { buildPeopleRelationshipsSignature } from './signature'\nimport {\n cleanupPeopleRelationshipsSnapshotTempFiles,\n readPeopleRelationshipsSnapshot,\n writePeopleRelationshipsSnapshot,\n} from './snapshot'\nimport { normalizePeopleRelationshipsTimeRangePreset, resolvePeopleRelationshipsTimeRange } from './time-range'\nimport { createPeopleRelationshipsWorkerRunner } from './worker-runner'\n\nexport interface PeopleRelationshipsServiceOptions {\n forceRecompute?: boolean\n acceptStale?: boolean\n timeRangePreset?: ContactsTimeRangePreset\n graphScope?: PeopleRelationshipsGraphScope\n query?: string\n}\n\nexport interface PeopleRelationshipsRunnerOptions {\n signature: string\n timeRangePreset: ContactsTimeRangePreset\n onProgress: (progress: PeopleRelationshipsComputeProgress) => void\n signal: AbortSignal\n}\n\nexport type PeopleRelationshipsComputeRunner = (\n options: PeopleRelationshipsRunnerOptions\n) => Promise<PeopleRelationshipsSnapshot>\n\nexport interface PeopleRelationshipsServiceDeps {\n adapter: SessionRuntimeAdapter\n systemDir?: string\n pathProvider?: PathProvider\n runtimeIdentity?: RuntimeIdentity\n nativeBinding?: string\n workerEntryUrl?: string | URL\n runner?: PeopleRelationshipsComputeRunner\n now?: () => number\n}\n\nexport interface PeopleRelationshipsService {\n getGraph(options?: PeopleRelationshipsServiceOptions): PeopleRelationshipsGraphResponse\n getNeighborhood(key: string, options?: PeopleRelationshipsServiceOptions): PeopleRelationshipsNeighborhoodResponse\n startRecompute(options?: PeopleRelationshipsServiceOptions): PeopleRelationshipsGraphResponse\n invalidateRelationshipsCache(): void\n close(): Promise<void>\n replaceSnapshotForTests?(snapshot: PeopleRelationshipsSnapshot): void\n}\n\ninterface InFlightTask {\n id: string\n signature: string\n promise: Promise<PeopleRelationshipsSnapshot>\n abortController: AbortController\n}\n\nconst CLOSE_GRAPH_NON_FRIEND_LIMIT = 50\n\nexport function createPeopleRelationshipsService(deps: PeopleRelationshipsServiceDeps): PeopleRelationshipsService {\n return new DefaultPeopleRelationshipsService(deps)\n}\n\nclass DefaultPeopleRelationshipsService implements PeopleRelationshipsService {\n private readonly snapshots = new Map<ContactsTimeRangePreset, PeopleRelationshipsSnapshot | null>()\n private inFlight: InFlightTask | null = null\n private task: PeopleRelationshipsTaskState = createIdleTaskState()\n private readonly snapshotDir: string\n private readonly runner: PeopleRelationshipsComputeRunner\n\n constructor(private readonly deps: PeopleRelationshipsServiceDeps) {\n this.snapshotDir = resolvePeopleRelationshipsSnapshotDir(deps)\n cleanupPeopleRelationshipsSnapshotTempFiles(this.snapshotDir)\n this.runner =\n deps.runner ??\n createPeopleRelationshipsWorkerRunner({\n pathProvider: requirePathProvider(deps),\n runtimeIdentity: deps.runtimeIdentity,\n nativeBinding: deps.nativeBinding,\n workerEntryUrl: deps.workerEntryUrl,\n })\n }\n\n getGraph(options: PeopleRelationshipsServiceOptions = {}): PeopleRelationshipsGraphResponse {\n const timeRangePreset = normalizePeopleRelationshipsTimeRangePreset(options.timeRangePreset)\n const signature = buildPeopleRelationshipsSignature(this.deps.adapter, timeRangePreset)\n const cacheStatus = this.getCacheStatus(signature, timeRangePreset)\n if (this.shouldStartTaskFromRead(options, cacheStatus)) this.ensureTaskStarted(signature, timeRangePreset)\n return this.toGraphResponse(signature, { ...options, timeRangePreset })\n }\n\n getNeighborhood(\n key: string,\n options: PeopleRelationshipsServiceOptions = {}\n ): PeopleRelationshipsNeighborhoodResponse {\n const timeRangePreset = normalizePeopleRelationshipsTimeRangePreset(options.timeRangePreset)\n const signature = buildPeopleRelationshipsSignature(this.deps.adapter, timeRangePreset)\n const cacheStatus = this.getCacheStatus(signature, timeRangePreset)\n if (this.shouldStartTaskFromRead(options, cacheStatus)) this.ensureTaskStarted(signature, timeRangePreset)\n const snapshot = this.getSnapshotForResponse(timeRangePreset)\n const status = this.getCacheStatus(signature, timeRangePreset)\n const includeSnapshot = shouldIncludeSnapshot(status, options.acceptStale)\n const graphScope = normalizePeopleRelationshipsGraphScope(options.graphScope)\n const scopedSnapshot = snapshot ? buildNeighborhoodSnapshotForScope(snapshot, graphScope) : null\n const graph =\n includeSnapshot && scopedSnapshot ? buildPeopleRelationshipsNeighborhoodGraph(scopedSnapshot, key) : emptyGraph()\n const contact = includeSnapshot ? (scopedSnapshot?.nodes.find((node) => node.key === key) ?? null) : null\n return {\n contact,\n graph,\n diagnostics: includeSnapshot\n ? sanitizePeopleRelationshipsDiagnostics(snapshot?.diagnostics ?? createEmptyDiagnostics())\n : createEmptyDiagnostics(),\n algorithmVersion: includeSnapshot\n ? (snapshot?.algorithmVersion ?? PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION)\n : PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION,\n timeRange: snapshot?.timeRange ?? resolvePeopleRelationshipsTimeRange(timeRangePreset, null),\n cache: this.toCacheState(status, snapshot),\n task: this.task,\n }\n }\n\n startRecompute(options: PeopleRelationshipsServiceOptions = {}): PeopleRelationshipsGraphResponse {\n const timeRangePreset = normalizePeopleRelationshipsTimeRangePreset(options.timeRangePreset)\n const signature = buildPeopleRelationshipsSignature(this.deps.adapter, timeRangePreset)\n this.ensureTaskStarted(signature, timeRangePreset)\n return this.toGraphResponse(signature, { ...options, acceptStale: true, timeRangePreset })\n }\n\n invalidateRelationshipsCache(): void {\n this.snapshots.clear()\n }\n\n async close(): Promise<void> {\n const inFlight = this.inFlight\n if (!inFlight) return\n this.inFlight = null\n inFlight.abortController.abort()\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n lastError: 'people relationships task aborted',\n }\n }\n\n replaceSnapshotForTests(snapshot: PeopleRelationshipsSnapshot): void {\n this.snapshots.set(snapshot.timeRange.preset, snapshot)\n }\n\n private shouldStartTaskFromRead(\n options: PeopleRelationshipsServiceOptions,\n cacheStatus: PeopleRelationshipsCacheState['status']\n ): boolean {\n if (options.forceRecompute) return true\n if (cacheStatus === 'fresh') return false\n return this.task.status !== 'failed'\n }\n\n private ensureTaskStarted(signature: string, timeRangePreset: ContactsTimeRangePreset): void {\n if (this.inFlight) return\n\n const taskId = `people_relationships_${this.now()}_${Math.random().toString(36).slice(2)}`\n this.task = {\n id: taskId,\n status: 'running',\n startedAt: this.now(),\n finishedAt: null,\n processedSessions: 0,\n totalSessions: this.deps.adapter.listSessionIds().length,\n timeRangePreset,\n }\n\n const abortController = new AbortController()\n const promise = this.runner({\n signature,\n timeRangePreset,\n signal: abortController.signal,\n onProgress: (progress) => {\n if (this.task.id !== taskId || this.task.status !== 'running') return\n this.task = {\n ...this.task,\n processedSessions: progress.processedSessions,\n totalSessions: progress.totalSessions,\n currentSessionId: progress.currentSessionId,\n }\n },\n })\n this.inFlight = { id: taskId, signature, promise, abortController }\n\n promise\n .then((snapshot) => this.handleTaskSuccess(taskId, signature, snapshot))\n .catch((error) => this.handleTaskFailure(taskId, error))\n }\n\n private handleTaskSuccess(taskId: string, inputSignature: string, snapshot: PeopleRelationshipsSnapshot): void {\n if (this.inFlight?.id !== taskId) return\n this.inFlight = null\n const latestSignature = buildPeopleRelationshipsSignature(this.deps.adapter, snapshot.timeRange.preset)\n const finishedAt = this.now()\n\n if (inputSignature !== latestSignature || snapshot.signature !== latestSignature) {\n this.task = {\n ...this.task,\n status: 'superseded',\n finishedAt,\n }\n appLogger.info('people-relationships', 'people relationships worker result discarded because signature changed', {\n inputSignature,\n latestSignature,\n })\n return\n }\n\n try {\n writePeopleRelationshipsSnapshot(this.snapshotDir, snapshot)\n this.snapshots.set(snapshot.timeRange.preset, snapshot)\n this.task = {\n ...this.task,\n status: 'succeeded',\n finishedAt,\n processedSessions: snapshot.workerStats.processedSessions,\n totalSessions: snapshot.workerStats.totalSessions,\n currentSessionId: undefined,\n }\n appLogger.info('people-relationships', 'people relationships worker snapshot persisted', {\n nodeCount: snapshot.nodes.length,\n edgeCount: snapshot.edges.length,\n durationMs: snapshot.workerStats.durationMs,\n })\n } catch (error) {\n this.handleTaskFailure(taskId, error)\n }\n }\n\n private handleTaskFailure(taskId: string, error: unknown): void {\n if (this.inFlight?.id === taskId) this.inFlight = null\n const message = error instanceof Error ? error.message : String(error)\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n lastError: message,\n }\n appLogger.error('people-relationships', 'people relationships worker failed', error)\n }\n\n private getCacheStatus(\n signature: string,\n timeRangePreset: ContactsTimeRangePreset\n ): PeopleRelationshipsCacheState['status'] {\n const snapshot = this.getSnapshot(timeRangePreset)\n if (!snapshot) return 'missing'\n return snapshot.signature === signature ? 'fresh' : 'stale'\n }\n\n private toGraphResponse(\n signature: string,\n options: PeopleRelationshipsServiceOptions = {}\n ): PeopleRelationshipsGraphResponse {\n const timeRangePreset = normalizePeopleRelationshipsTimeRangePreset(options.timeRangePreset)\n const graphScope = normalizePeopleRelationshipsGraphScope(options.graphScope)\n const snapshot = this.getSnapshotForResponse(timeRangePreset)\n const status = this.getCacheStatus(signature, timeRangePreset)\n const includeSnapshot = shouldIncludeSnapshot(status, options.acceptStale)\n const graph = includeSnapshot && snapshot ? buildGraphForScope(snapshot, graphScope) : emptyGraph()\n return {\n graph,\n searchResults: includeSnapshot && snapshot ? buildSearchResults(snapshot, options.query, graph, graphScope) : [],\n diagnostics: includeSnapshot\n ? sanitizePeopleRelationshipsDiagnostics(snapshot?.diagnostics ?? createEmptyDiagnostics())\n : createEmptyDiagnostics(),\n algorithmVersion: includeSnapshot\n ? (snapshot?.algorithmVersion ?? PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION)\n : PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION,\n timeRange: snapshot?.timeRange ?? resolvePeopleRelationshipsTimeRange(timeRangePreset, null),\n cache: this.toCacheState(status, snapshot),\n task: this.task,\n }\n }\n\n private toCacheState(\n status: PeopleRelationshipsCacheState['status'],\n snapshot: PeopleRelationshipsSnapshot | null\n ): PeopleRelationshipsCacheState {\n return {\n status,\n computedAt: snapshot?.computedAt ?? null,\n signature: snapshot?.signature,\n staleReason: status === 'stale' ? 'signature_changed' : undefined,\n }\n }\n\n private getSnapshot(timeRangePreset: ContactsTimeRangePreset): PeopleRelationshipsSnapshot | null {\n if (!this.snapshots.has(timeRangePreset)) {\n this.snapshots.set(\n timeRangePreset,\n readPeopleRelationshipsSnapshot(this.snapshotDir, timeRangePreset, { now: this.deps.now })\n )\n }\n return this.snapshots.get(timeRangePreset) ?? null\n }\n\n private getSnapshotForResponse(timeRangePreset: ContactsTimeRangePreset): PeopleRelationshipsSnapshot | null {\n const snapshot = this.getSnapshot(timeRangePreset)\n if (!snapshot || !this.deps.pathProvider) return snapshot\n return applyManualFriendOverridesToSnapshot(\n snapshot,\n readContactOverrides(getContactsDir(this.deps.pathProvider.getUserDataDir()))\n )\n }\n\n private now(): number {\n return this.deps.now?.() ?? Date.now()\n }\n}\n\nfunction shouldIncludeSnapshot(status: PeopleRelationshipsCacheState['status'], acceptStale?: boolean): boolean {\n return status === 'fresh' || (status === 'stale' && acceptStale === true)\n}\n\nfunction normalizePeopleRelationshipsGraphScope(\n scope: PeopleRelationshipsGraphScope | undefined\n): PeopleRelationshipsGraphScope {\n return scope === 'close' || scope === 'friends' ? scope : 'panorama'\n}\n\nfunction buildGraphForScope(\n snapshot: PeopleRelationshipsSnapshot,\n scope: PeopleRelationshipsGraphScope\n): PeopleRelationshipsGraphData {\n if (scope === 'friends') return buildFriendsRelationshipsGraph(snapshot)\n if (scope === 'close') return buildCloseRelationshipsGraph(snapshot)\n return snapshot.graph\n}\n\nfunction buildFriendsRelationshipsGraph(snapshot: PeopleRelationshipsSnapshot): PeopleRelationshipsGraphData {\n const nodes = snapshot.nodes.filter((node) => node.kind === 'owner' || node.pool === 'friend').sort(compareNodes)\n const selectedKeys = new Set(nodes.map((node) => node.key))\n const edges = snapshot.edges.filter((edge) => selectedKeys.has(edge.sourceKey) && selectedKeys.has(edge.targetKey))\n\n return {\n nodes,\n edges,\n communities: filterCommunitiesForNodes(snapshot.communities, nodes),\n }\n}\n\nfunction buildNeighborhoodSnapshotForScope(\n snapshot: PeopleRelationshipsSnapshot,\n scope: PeopleRelationshipsGraphScope\n): PeopleRelationshipsSnapshot {\n if (scope !== 'friends') return snapshot\n\n const graph = buildFriendsRelationshipsGraph(snapshot)\n return {\n ...snapshot,\n nodes: graph.nodes,\n edges: graph.edges,\n communities: graph.communities,\n }\n}\n\nfunction buildCloseRelationshipsGraph(snapshot: PeopleRelationshipsSnapshot): PeopleRelationshipsGraphData {\n const selectedKeys = new Set<string>()\n for (const node of snapshot.nodes) {\n if (node.kind === 'owner' || node.pool === 'friend') selectedKeys.add(node.key)\n }\n\n const edgeConnectedKeys = buildEdgeConnectedNodeKeys(snapshot.edges)\n const topGroupmates = snapshot.nodes\n .filter(\n (node) =>\n node.kind !== 'owner' &&\n node.pool !== 'friend' &&\n node.score > 0 &&\n hasCloseGraphNonFriendActivity(node, edgeConnectedKeys)\n )\n .sort(compareCloseGroupmates)\n .slice(0, CLOSE_GRAPH_NON_FRIEND_LIMIT)\n for (const node of topGroupmates) selectedKeys.add(node.key)\n\n const nodes = snapshot.nodes.filter((node) => selectedKeys.has(node.key)).sort(compareNodes)\n const edges = snapshot.edges.filter((edge) => selectedKeys.has(edge.sourceKey) && selectedKeys.has(edge.targetKey))\n\n return {\n nodes,\n edges,\n communities: filterCommunitiesForNodes(snapshot.communities, nodes),\n }\n}\n\nfunction buildEdgeConnectedNodeKeys(edges: PeopleRelationshipsSnapshot['edges']): Set<string> {\n const keys = new Set<string>()\n for (const edge of edges) {\n keys.add(edge.sourceKey)\n keys.add(edge.targetKey)\n }\n return keys\n}\n\nfunction hasCloseGraphNonFriendActivity(\n node: PeopleRelationshipsSnapshot['nodes'][number],\n edgeConnectedKeys: Set<string>\n): boolean {\n // close 图只展示有真实互动的非好友:共同群 roster 本身不算互动信号。\n return node.groupMessageCount > 0 || edgeConnectedKeys.has(node.key)\n}\n\nfunction compareCloseGroupmates(\n a: PeopleRelationshipsSnapshot['nodes'][number],\n b: PeopleRelationshipsSnapshot['nodes'][number]\n): number {\n return b.score - a.score || a.rank - b.rank || a.key.localeCompare(b.key)\n}\n\nfunction filterCommunitiesForNodes(\n communities: PeopleRelationshipCommunity[],\n nodes: PeopleRelationshipsGraphData['nodes']\n): PeopleRelationshipCommunity[] {\n const communitySizes = new Map<string, number>()\n for (const node of nodes) communitySizes.set(node.communityId, (communitySizes.get(node.communityId) ?? 0) + 1)\n return communities\n .filter((community) => communitySizes.has(community.id))\n .map((community) => ({\n ...community,\n size: communitySizes.get(community.id) ?? community.size,\n }))\n}\n\nfunction buildSearchResults(\n snapshot: PeopleRelationshipsSnapshot,\n queryInput: string | undefined,\n graph: PeopleRelationshipsGraphData,\n graphScope: PeopleRelationshipsGraphScope\n): PeopleRelationshipsSearchResult[] {\n const query = queryInput?.trim().toLowerCase() ?? ''\n if (!query) return []\n const visibleKeys = new Set(graph.nodes.map((node) => node.key))\n const searchableNodes = graphScope === 'friends' ? graph.nodes : snapshot.nodes\n return searchableNodes\n .filter((node) => node.searchText.includes(query))\n .sort(compareNodes)\n .slice(0, snapshot.limits.searchResultLimit)\n .map((node) => ({\n key: node.key,\n kind: node.kind,\n displayName: node.displayName,\n platform: node.platform,\n platformId: node.platformId,\n avatar: node.avatar,\n pool: node.pool,\n friendSource: node.friendSource,\n score: node.score,\n rank: node.rank,\n communityId: node.communityId,\n inCoreGraph: visibleKeys.has(node.key),\n }))\n}\n\nfunction applyManualFriendOverridesToSnapshot(\n snapshot: PeopleRelationshipsSnapshot,\n overrides: ReturnType<typeof readContactOverrides>\n): PeopleRelationshipsSnapshot {\n const manualFriendKeys = new Set(Object.keys(overrides.manualFriends))\n if (manualFriendKeys.size === 0) return snapshot\n\n // 手动好友是本机覆盖数据,不写入派生 snapshot;响应阶段克隆节点,确保各图谱 scope 即时生效。\n const applyOverride = (node: PeopleRelationshipGraphNode): PeopleRelationshipGraphNode => {\n if (node.kind === 'owner' || node.pool === 'friend' || !manualFriendKeys.has(node.key)) return node\n return {\n ...node,\n pool: 'friend',\n friendSource: 'manual',\n }\n }\n const nodes = snapshot.nodes.map(applyOverride)\n const nodeByKey = new Map(nodes.map((node) => [node.key, node]))\n const graphNodes = includeManualFriendsInPanoramaGraphNodes(\n snapshot.graph.nodes.map((node) => nodeByKey.get(node.key) ?? applyOverride(node)),\n nodes,\n manualFriendKeys\n )\n const graphNodeKeys = new Set(graphNodes.map((node) => node.key))\n const graphEdges = includeManualFriendPanoramaEdges(\n snapshot.graph.edges,\n snapshot.edges,\n graphNodeKeys,\n manualFriendKeys\n )\n return {\n ...snapshot,\n nodes,\n graph: {\n ...snapshot.graph,\n nodes: graphNodes,\n edges: graphEdges,\n communities: filterCommunitiesForNodes(snapshot.communities, graphNodes),\n },\n }\n}\n\nfunction includeManualFriendsInPanoramaGraphNodes(\n graphNodes: PeopleRelationshipGraphNode[],\n nodes: PeopleRelationshipGraphNode[],\n manualFriendKeys: Set<string>\n): PeopleRelationshipGraphNode[] {\n const graphNodeKeys = new Set(graphNodes.map((node) => node.key))\n const result = [...graphNodes]\n for (const node of nodes) {\n if (!manualFriendKeys.has(node.key) || graphNodeKeys.has(node.key) || node.kind === 'owner') continue\n result.push(node)\n graphNodeKeys.add(node.key)\n }\n return result.sort(compareNodes)\n}\n\nfunction includeManualFriendPanoramaEdges(\n graphEdges: PeopleRelationshipsGraphData['edges'],\n edges: PeopleRelationshipsSnapshot['edges'],\n graphNodeKeys: Set<string>,\n manualFriendKeys: Set<string>\n): PeopleRelationshipsGraphData['edges'] {\n // 手动好友是用户显式修正,只补它和当前全景节点之间的边,避免把被裁剪的全量图重新展开。\n const edgeIds = new Set(graphEdges.map((edge) => edge.id))\n const result = [...graphEdges]\n for (const edge of edges) {\n if (edgeIds.has(edge.id)) continue\n if (!graphNodeKeys.has(edge.sourceKey) || !graphNodeKeys.has(edge.targetKey)) continue\n if (!manualFriendKeys.has(edge.sourceKey) && !manualFriendKeys.has(edge.targetKey)) continue\n result.push(edge)\n edgeIds.add(edge.id)\n }\n return result.sort(compareEdges)\n}\n\nfunction emptyGraph(): PeopleRelationshipsGraphData {\n return { nodes: [], edges: [], communities: [] }\n}\n\nfunction createEmptyDiagnostics(): PeopleRelationshipsDiagnostics {\n return {\n processedPrivateSessions: 0,\n processedGroupSessions: 0,\n skippedMissingOwnerSessions: 0,\n skippedUnresolvedOwnerSessions: 0,\n skippedAmbiguousPrivateSessions: 0,\n skippedFailedSessions: 0,\n totalNodes: 0,\n totalEdges: 0,\n panoramaIncludedGroupSessions: 0,\n panoramaExcludedLowValueGroupSessions: 0,\n panoramaIncludedGroupMembers: 0,\n panoramaExcludedGroupMembers: 0,\n panoramaCandidateNodes: 0,\n panoramaGroupInclusionReasons: {},\n coreNodeCount: 0,\n coreEdgeCount: 0,\n warnings: [],\n }\n}\n\nfunction sanitizePeopleRelationshipsDiagnostics(\n diagnostics: PeopleRelationshipsDiagnostics\n): PeopleRelationshipsDiagnostics {\n return {\n processedPrivateSessions: diagnostics.processedPrivateSessions,\n processedGroupSessions: diagnostics.processedGroupSessions,\n skippedMissingOwnerSessions: diagnostics.skippedMissingOwnerSessions,\n skippedUnresolvedOwnerSessions: diagnostics.skippedUnresolvedOwnerSessions,\n skippedAmbiguousPrivateSessions: diagnostics.skippedAmbiguousPrivateSessions,\n skippedFailedSessions: diagnostics.skippedFailedSessions,\n totalNodes: diagnostics.totalNodes,\n totalEdges: diagnostics.totalEdges,\n panoramaIncludedGroupSessions: diagnostics.panoramaIncludedGroupSessions ?? 0,\n panoramaExcludedLowValueGroupSessions: diagnostics.panoramaExcludedLowValueGroupSessions ?? 0,\n panoramaIncludedGroupMembers: diagnostics.panoramaIncludedGroupMembers ?? 0,\n panoramaExcludedGroupMembers: diagnostics.panoramaExcludedGroupMembers ?? 0,\n panoramaCandidateNodes: diagnostics.panoramaCandidateNodes ?? 0,\n panoramaGroupInclusionReasons: diagnostics.panoramaGroupInclusionReasons ?? {},\n coreNodeCount: diagnostics.coreNodeCount,\n coreEdgeCount: diagnostics.coreEdgeCount,\n warnings: diagnostics.warnings,\n }\n}\n\nfunction createIdleTaskState(): PeopleRelationshipsTaskState {\n return {\n id: null,\n status: 'idle',\n startedAt: null,\n finishedAt: null,\n processedSessions: 0,\n totalSessions: 0,\n }\n}\n\nfunction requirePathProvider(deps: PeopleRelationshipsServiceDeps): PathProvider {\n if (!deps.pathProvider) {\n throw new Error('people relationships worker runner requires pathProvider')\n }\n return deps.pathProvider\n}\n\nfunction resolvePeopleRelationshipsSnapshotDir(deps: PeopleRelationshipsServiceDeps): string {\n if (deps.pathProvider) return getPeopleRelationshipsDir(deps.pathProvider.getUserDataDir())\n if (deps.systemDir) return deps.systemDir\n throw new Error('people relationships service requires systemDir or pathProvider')\n}\n","export { PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION, computePeopleRelationshipsSnapshot } from './compute'\nexport type {\n PeopleRelationshipsComputeLimits,\n PeopleRelationshipsComputeProgress,\n PeopleRelationshipsSnapshot,\n} from './compute'\nexport { createPeopleRelationshipsService } from './service'\nexport type {\n PeopleRelationshipsComputeRunner,\n PeopleRelationshipsService,\n PeopleRelationshipsServiceDeps,\n PeopleRelationshipsServiceOptions,\n} from './service'\nexport { getPeopleRelationshipsDir, getPeopleRelationshipsFactsCacheDir } from './paths'\n","import fs from 'node:fs'\nimport type { PathProvider } from '@openchatlab/core'\nimport type { AnnualSummaryRange, AnnualSummaryResponse, AnnualSummaryTaskState } from '@openchatlab/shared-types'\nimport type { RuntimeIdentity } from '../../data-dir-compat'\nimport { appLogger } from '../../logging/app-logger'\nimport type { SessionRuntimeAdapter } from '../adapters'\nimport { getGlobalInsightDir } from './paths'\nimport { buildAnnualSummarySignature } from './signature'\nimport {\n cleanupAnnualSummarySnapshotTempFiles,\n getAnnualSummarySnapshotPath,\n readAnnualSummarySnapshot,\n writeAnnualSummarySnapshot,\n} from './snapshot'\nimport { normalizeAnnualSummaryRange, toAnnualSummaryRangeKey, type AnnualSummaryRangeInput } from './time-range'\nimport { createAnnualSummaryWorkerRunner, type AnnualSummaryComputeRunner } from './worker-runner'\nimport type { AnnualSummarySnapshot } from './types'\nimport { listOwnerInsightSessionIds } from './session-scope'\n\nexport interface GlobalInsightServiceOptions extends AnnualSummaryRangeInput {\n acceptStale?: boolean\n forceRecompute?: boolean\n}\n\nexport interface GlobalInsightServiceDeps {\n adapter: SessionRuntimeAdapter\n pathProvider?: PathProvider\n userDataDir?: string\n runtimeIdentity?: RuntimeIdentity\n nativeBinding?: string\n workerEntryUrl?: string | URL\n runner?: AnnualSummaryComputeRunner\n getExcludedSessionIds?: () => readonly string[]\n now?: () => number\n}\n\nexport interface GlobalInsightService {\n getAnnualSummary(options?: GlobalInsightServiceOptions): AnnualSummaryResponse\n startRecompute(options?: GlobalInsightServiceOptions): AnnualSummaryResponse\n invalidateCache(): void\n close(): Promise<void>\n normalizeRange(input?: AnnualSummaryRangeInput): AnnualSummaryRange\n replaceSnapshotForTests(snapshot: AnnualSummarySnapshot): void\n}\n\ninterface InFlightTask {\n id: string\n signature: string\n promise: Promise<AnnualSummarySnapshot>\n abortController: AbortController\n}\n\nexport function createGlobalInsightService(deps: GlobalInsightServiceDeps): GlobalInsightService {\n return new DefaultGlobalInsightService(deps)\n}\n\nclass DefaultGlobalInsightService implements GlobalInsightService {\n private readonly snapshots = new Map<string, AnnualSummarySnapshot | null>()\n private readonly snapshotDir: string\n private readonly runner: AnnualSummaryComputeRunner\n private inFlight: InFlightTask | null = null\n private task: AnnualSummaryTaskState = createIdleTaskState()\n\n constructor(private readonly deps: GlobalInsightServiceDeps) {\n this.snapshotDir = deps.userDataDir\n ? getGlobalInsightDir(deps.userDataDir)\n : getGlobalInsightDir(requirePathProvider(deps).getUserDataDir())\n cleanupAnnualSummarySnapshotTempFiles(this.snapshotDir)\n this.runner =\n deps.runner ??\n createAnnualSummaryWorkerRunner({\n pathProvider: requirePathProvider(deps),\n runtimeIdentity: deps.runtimeIdentity,\n nativeBinding: deps.nativeBinding,\n workerEntryUrl: deps.workerEntryUrl,\n })\n }\n\n getAnnualSummary(options: GlobalInsightServiceOptions = {}): AnnualSummaryResponse {\n const range = this.normalizeRange(options)\n const excludedSessionIds = this.getExcludedSessionIds()\n const signature = buildAnnualSummarySignature(this.deps.adapter, range, excludedSessionIds)\n const status = this.getCacheStatus(signature, range)\n if (this.shouldStartTask(options, status)) this.ensureTaskStarted(signature, range, excludedSessionIds)\n return this.toResponse(signature, range, options.acceptStale === true)\n }\n\n startRecompute(options: GlobalInsightServiceOptions = {}): AnnualSummaryResponse {\n const range = this.normalizeRange(options)\n const excludedSessionIds = this.getExcludedSessionIds()\n const signature = buildAnnualSummarySignature(this.deps.adapter, range, excludedSessionIds)\n this.ensureTaskStarted(signature, range, excludedSessionIds)\n return this.toResponse(signature, range, true)\n }\n\n invalidateCache(): void {\n this.snapshots.clear()\n }\n\n normalizeRange(input: AnnualSummaryRangeInput = {}): AnnualSummaryRange {\n return normalizeAnnualSummaryRange(input, new Date(this.now()))\n }\n\n replaceSnapshotForTests(snapshot: AnnualSummarySnapshot): void {\n writeAnnualSummarySnapshot(this.snapshotDir, snapshot)\n this.snapshots.set(toAnnualSummaryRangeKey(snapshot.range), snapshot)\n }\n\n async close(): Promise<void> {\n const inFlight = this.inFlight\n if (!inFlight) return\n this.inFlight = null\n inFlight.abortController.abort()\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n lastError: 'annual summary task aborted',\n }\n }\n\n private shouldStartTask(\n options: GlobalInsightServiceOptions,\n status: AnnualSummaryResponse['cache']['status']\n ): boolean {\n if (options.forceRecompute) return true\n if (status === 'fresh') return false\n return this.task.status !== 'failed'\n }\n\n private ensureTaskStarted(signature: string, range: AnnualSummaryRange, excludedSessionIds: readonly string[]): void {\n if (this.inFlight) return\n const id = `annual_summary_${this.now()}_${Math.random().toString(36).slice(2)}`\n this.task = {\n id,\n status: 'running',\n startedAt: this.now(),\n finishedAt: null,\n processedSessions: 0,\n totalSessions: listOwnerInsightSessionIds(this.deps.adapter, excludedSessionIds).length,\n }\n const abortController = new AbortController()\n const promise = this.runner({\n signature,\n range,\n excludedSessionIds,\n signal: abortController.signal,\n onProgress: (progress) => {\n if (this.task.id !== id || this.task.status !== 'running') return\n this.task = { ...this.task, ...progress }\n },\n })\n this.inFlight = { id, signature, promise, abortController }\n promise\n .then((snapshot) => this.handleTaskSuccess(id, signature, snapshot))\n .catch((error) => this.handleTaskFailure(id, error))\n }\n\n private handleTaskSuccess(id: string, inputSignature: string, snapshot: AnnualSummarySnapshot): void {\n if (this.inFlight?.id !== id) return\n const latestSignature = buildAnnualSummarySignature(this.deps.adapter, snapshot.range, this.getExcludedSessionIds())\n const finishedAt = this.now()\n if (inputSignature !== latestSignature || snapshot.signature !== latestSignature) {\n this.inFlight = null\n this.task = { ...this.task, status: 'superseded', finishedAt, currentSessionId: undefined }\n appLogger.info('global-insight', 'annual summary worker result discarded because signature changed')\n return\n }\n try {\n writeAnnualSummarySnapshot(this.snapshotDir, snapshot)\n this.inFlight = null\n this.snapshots.set(toAnnualSummaryRangeKey(snapshot.range), snapshot)\n this.task = {\n ...this.task,\n status: 'succeeded',\n finishedAt,\n processedSessions: snapshot.workerStats.processedSessions,\n totalSessions: snapshot.workerStats.totalSessions,\n currentSessionId: undefined,\n }\n appLogger.info('global-insight', 'annual summary snapshot persisted', {\n analyzedSessions: snapshot.coverage.analyzedSessions,\n durationMs: snapshot.workerStats.durationMs,\n })\n } catch (error) {\n this.handleTaskFailure(id, error)\n }\n }\n\n private handleTaskFailure(id: string, error: unknown): void {\n if (this.inFlight?.id !== id) return\n this.inFlight = null\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n currentSessionId: undefined,\n lastError: error instanceof Error ? error.message : String(error),\n }\n appLogger.error('global-insight', 'annual summary worker failed', error)\n }\n\n private toResponse(signature: string, range: AnnualSummaryRange, acceptStale: boolean): AnnualSummaryResponse {\n const snapshot = this.getSnapshot(range)\n const status = this.getCacheStatus(signature, range)\n const includeSnapshot = status === 'fresh' || (status === 'stale' && acceptStale)\n return {\n range,\n availableDataYears: includeSnapshot ? (snapshot?.availableDataYears ?? []) : [],\n latestDataYear: includeSnapshot ? (snapshot?.latestDataYear ?? null) : null,\n metrics: includeSnapshot ? (snapshot?.metrics ?? null) : null,\n monthlyActivity: includeSnapshot ? (snapshot?.monthlyActivity ?? []) : [],\n monthlyDirectContacts: includeSnapshot ? (snapshot?.monthlyDirectContacts ?? []) : [],\n dailyActivity: includeSnapshot ? (snapshot?.dailyActivity ?? []) : [],\n messageTypes: includeSnapshot ? (snapshot?.messageTypes ?? []) : [],\n textLength: includeSnapshot ? (snapshot?.textLength ?? null) : null,\n coverage: includeSnapshot ? (snapshot?.coverage ?? emptyCoverage()) : emptyCoverage(),\n cache: {\n status,\n computedAt: snapshot?.computedAt ?? null,\n signature: snapshot?.signature,\n staleReason: status === 'stale' ? 'signature_changed' : undefined,\n },\n task: this.task,\n }\n }\n\n private getCacheStatus(signature: string, range: AnnualSummaryRange): AnnualSummaryResponse['cache']['status'] {\n const snapshot = this.getSnapshot(range)\n if (!snapshot) return 'missing'\n return snapshot.signature === signature ? 'fresh' : 'stale'\n }\n\n private getSnapshot(range: AnnualSummaryRange): AnnualSummarySnapshot | null {\n const key = toAnnualSummaryRangeKey(range)\n if (this.snapshots.has(key) && !fs.existsSync(getAnnualSummarySnapshotPath(this.snapshotDir, range))) {\n this.snapshots.delete(key)\n }\n if (!this.snapshots.has(key)) {\n this.snapshots.set(key, readAnnualSummarySnapshot(this.snapshotDir, range, { now: this.deps.now }))\n }\n return this.snapshots.get(key) ?? null\n }\n\n private now(): number {\n return this.deps.now?.() ?? Date.now()\n }\n\n private getExcludedSessionIds(): readonly string[] {\n return this.deps.getExcludedSessionIds?.() ?? []\n }\n}\n\nfunction requirePathProvider(deps: GlobalInsightServiceDeps): PathProvider {\n if (!deps.pathProvider)\n throw new Error('GlobalInsightService requires pathProvider unless userDataDir and runner are provided')\n return deps.pathProvider\n}\n\nfunction createIdleTaskState(): AnnualSummaryTaskState {\n return {\n id: null,\n status: 'idle',\n startedAt: null,\n finishedAt: null,\n processedSessions: 0,\n totalSessions: 0,\n }\n}\n\nfunction emptyCoverage(): AnnualSummaryResponse['coverage'] {\n return {\n totalSessions: 0,\n analyzedSessions: 0,\n missingOwnerSessions: 0,\n unresolvedOwnerSessions: 0,\n failedSessions: 0,\n }\n}\n","import type { AnnualSummaryRange } from '@openchatlab/shared-types'\nimport { getDbFileVersion } from '../../cache/analytics-cache'\nimport type { SessionRuntimeAdapter } from '../adapters'\nimport { toAnnualSummaryLocalDate, toAnnualSummaryRangeKey } from './time-range'\nimport { ANNUAL_SUMMARY_ALGORITHM_VERSION } from './types'\nimport { listOwnerInsightSessionIds } from './session-scope'\n\nexport function buildAnnualSummarySignature(\n adapter: SessionRuntimeAdapter,\n range: AnnualSummaryRange,\n excludedSessionIds: readonly string[] = []\n): string {\n const parts = [\n `algorithm:${ANNUAL_SUMMARY_ALGORITHM_VERSION}`,\n `range:${toAnnualSummaryRangeKey(range)}`,\n `start:${range.startTs}`,\n `day:${toAnnualSummaryLocalDate(range)}`,\n ]\n for (const sessionId of listOwnerInsightSessionIds(adapter, excludedSessionIds).sort()) {\n parts.push(`${sessionId}:${getDbFileVersion(adapter.getDbPath(sessionId))}`)\n }\n return parts.join('|')\n}\n","import { existsSync } from 'node:fs'\nimport { Worker, type WorkerOptions } from 'node:worker_threads'\nimport type { PathProvider } from '@openchatlab/core'\nimport type { AnnualSummaryRange } from '@openchatlab/shared-types'\nimport type { RuntimeIdentity } from '../../data-dir-compat'\nimport { snapshotPathProvider } from '../../semantic-index/static-path-provider'\nimport type { AnnualSummaryComputeProgress, AnnualSummarySnapshot } from './types'\nimport type { TimeInvestmentSnapshot } from './time-investment-types'\n\ntype ModuleWorkerOptions = WorkerOptions & { type: 'module' }\ntype EntryExists = (url: URL) => boolean\n\nexport interface AnnualSummaryRunnerOptions {\n signature: string\n range: AnnualSummaryRange\n excludedSessionIds: readonly string[]\n onProgress: (progress: AnnualSummaryComputeProgress) => void\n signal: AbortSignal\n}\n\nexport type AnnualSummaryComputeRunner = (options: AnnualSummaryRunnerOptions) => Promise<AnnualSummarySnapshot>\nexport type TimeInvestmentComputeRunner = (options: AnnualSummaryRunnerOptions) => Promise<TimeInvestmentSnapshot>\n\nexport interface AnnualSummaryWorkerRunnerOptions {\n pathProvider: PathProvider\n runtimeIdentity?: RuntimeIdentity\n nativeBinding?: string\n workerEntryUrl?: string | URL\n}\n\ninterface WorkerMessage {\n type: 'progress' | 'success' | 'error'\n progress?: AnnualSummaryComputeProgress\n snapshot?: AnnualSummarySnapshot | TimeInvestmentSnapshot\n error?: string\n}\n\nexport function resolveDefaultAnnualSummaryWorkerEntryUrl(\n currentModuleUrl: string | URL = import.meta.url,\n entryExists: EntryExists = (url) => existsSync(url)\n): URL {\n const moduleUrl = typeof currentModuleUrl === 'string' ? currentModuleUrl : currentModuleUrl.href\n if (moduleUrl.endsWith('.ts')) return new URL('./worker-entry.ts', moduleUrl)\n if (moduleUrl.endsWith('.mjs')) return new URL('./global-insight-worker.mjs', moduleUrl)\n const sibling = new URL('./worker-entry.js', moduleUrl)\n return entryExists(sibling) ? sibling : new URL('./global-insight-worker.js', moduleUrl)\n}\n\nexport function createAnnualSummaryWorkerRunner(options: AnnualSummaryWorkerRunnerOptions): AnnualSummaryComputeRunner {\n return ({ signature, range, excludedSessionIds, signal, onProgress }) =>\n new Promise<AnnualSummarySnapshot>((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error('annual summary worker aborted'))\n return\n }\n const worker = createWorker(\n {\n paths: snapshotPathProvider(options.pathProvider),\n runtimeIdentity: options.runtimeIdentity,\n nativeBinding: options.nativeBinding,\n signature,\n range,\n excludedSessionIds,\n },\n options.workerEntryUrl\n )\n let settled = false\n const abort = () => {\n if (settled) return\n settled = true\n void worker.terminate()\n reject(new Error('annual summary worker aborted'))\n }\n signal.addEventListener('abort', abort, { once: true })\n worker.on('message', (message: WorkerMessage) => {\n if (message.type === 'progress' && message.progress) {\n onProgress(message.progress)\n return\n }\n if (message.type === 'success' && message.snapshot) {\n settled = true\n signal.removeEventListener('abort', abort)\n resolve(message.snapshot as AnnualSummarySnapshot)\n void worker.terminate()\n return\n }\n if (message.type === 'error') {\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(message.error ?? 'annual summary worker failed'))\n void worker.terminate()\n }\n })\n worker.on('error', (error) => {\n if (settled) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(error)\n })\n worker.on('exit', (code) => {\n if (settled || code === 0) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(`annual summary worker exited with code ${code}`))\n })\n })\n}\n\nexport function createTimeInvestmentWorkerRunner(\n options: AnnualSummaryWorkerRunnerOptions\n): TimeInvestmentComputeRunner {\n return ({ signature, range, excludedSessionIds, signal, onProgress }) =>\n new Promise<TimeInvestmentSnapshot>((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error('time investment worker aborted'))\n return\n }\n const worker = createWorker(\n {\n reportType: 'time-investment',\n paths: snapshotPathProvider(options.pathProvider),\n runtimeIdentity: options.runtimeIdentity,\n nativeBinding: options.nativeBinding,\n signature,\n range,\n excludedSessionIds,\n },\n options.workerEntryUrl\n )\n let settled = false\n const abort = () => {\n if (settled) return\n settled = true\n void worker.terminate()\n reject(new Error('time investment worker aborted'))\n }\n signal.addEventListener('abort', abort, { once: true })\n worker.on('message', (message: WorkerMessage) => {\n if (message.type === 'progress' && message.progress) {\n onProgress(message.progress)\n return\n }\n if (message.type === 'success' && message.snapshot) {\n settled = true\n signal.removeEventListener('abort', abort)\n resolve(message.snapshot as TimeInvestmentSnapshot)\n void worker.terminate()\n return\n }\n if (message.type === 'error') {\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(message.error ?? 'time investment worker failed'))\n void worker.terminate()\n }\n })\n worker.on('error', (error) => {\n if (settled) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(error)\n })\n worker.on('exit', (code) => {\n if (settled || code === 0) return\n settled = true\n signal.removeEventListener('abort', abort)\n reject(new Error(`time investment worker exited with code ${code}`))\n })\n })\n}\n\nfunction createWorker(workerData: unknown, entryUrlInput?: string | URL): Worker {\n const entryUrl = entryUrlInput\n ? typeof entryUrlInput === 'string'\n ? new URL(entryUrlInput)\n : entryUrlInput\n : resolveDefaultAnnualSummaryWorkerEntryUrl()\n if (!entryUrl.href.endsWith('.ts')) return new Worker(entryUrl, { workerData })\n const bootstrap = `\n import { register } from 'tsx/esm/api';\n register();\n await import(${JSON.stringify(entryUrl.href)});\n `\n const workerOptions: ModuleWorkerOptions = { eval: true, type: 'module', workerData, execArgv: [] }\n return new Worker(bootstrap, workerOptions)\n}\n","import fs from 'node:fs'\nimport type { PathProvider } from '@openchatlab/core'\nimport type { AnnualSummaryRange, AnnualSummaryTaskState, TimeInvestmentResponse } from '@openchatlab/shared-types'\nimport type { RuntimeIdentity } from '../../data-dir-compat'\nimport { appLogger } from '../../logging/app-logger'\nimport type { SessionRuntimeAdapter } from '../adapters'\nimport { getTimeInvestmentDir } from './paths'\nimport { normalizeAnnualSummaryRange, toAnnualSummaryRangeKey, type AnnualSummaryRangeInput } from './time-range'\nimport { buildTimeInvestmentSignature } from './time-investment-signature'\nimport {\n cleanupTimeInvestmentSnapshotTempFiles,\n getTimeInvestmentSnapshotPath,\n readTimeInvestmentSnapshot,\n writeTimeInvestmentSnapshot,\n} from './time-investment-snapshot'\nimport type { TimeInvestmentSnapshot } from './time-investment-types'\nimport { createTimeInvestmentWorkerRunner, type TimeInvestmentComputeRunner } from './worker-runner'\nimport { listOwnerInsightSessionIds } from './session-scope'\n\nexport interface TimeInvestmentServiceOptions extends AnnualSummaryRangeInput {\n acceptStale?: boolean\n}\n\nexport interface TimeInvestmentServiceDeps {\n adapter: SessionRuntimeAdapter\n pathProvider?: PathProvider\n userDataDir?: string\n runtimeIdentity?: RuntimeIdentity\n nativeBinding?: string\n workerEntryUrl?: string | URL\n runner?: TimeInvestmentComputeRunner\n getExcludedSessionIds?: () => readonly string[]\n now?: () => number\n}\n\nexport interface TimeInvestmentService {\n getTimeInvestment(options?: TimeInvestmentServiceOptions): TimeInvestmentResponse\n startRecompute(options?: TimeInvestmentServiceOptions): TimeInvestmentResponse\n close(): Promise<void>\n}\n\ninterface InFlightTask {\n id: string\n promise: Promise<TimeInvestmentSnapshot>\n abortController: AbortController\n}\n\nexport function createTimeInvestmentService(deps: TimeInvestmentServiceDeps): TimeInvestmentService {\n return new DefaultTimeInvestmentService(deps)\n}\n\nclass DefaultTimeInvestmentService implements TimeInvestmentService {\n private readonly snapshots = new Map<string, TimeInvestmentSnapshot | null>()\n private readonly snapshotDir: string\n private readonly runner: TimeInvestmentComputeRunner\n private inFlight: InFlightTask | null = null\n private task: AnnualSummaryTaskState = createIdleTaskState()\n\n constructor(private readonly deps: TimeInvestmentServiceDeps) {\n this.snapshotDir = deps.userDataDir\n ? getTimeInvestmentDir(deps.userDataDir)\n : getTimeInvestmentDir(requirePathProvider(deps).getUserDataDir())\n cleanupTimeInvestmentSnapshotTempFiles(this.snapshotDir)\n this.runner =\n deps.runner ??\n createTimeInvestmentWorkerRunner({\n pathProvider: requirePathProvider(deps),\n runtimeIdentity: deps.runtimeIdentity,\n nativeBinding: deps.nativeBinding,\n workerEntryUrl: deps.workerEntryUrl,\n })\n }\n\n getTimeInvestment(options: TimeInvestmentServiceOptions = {}): TimeInvestmentResponse {\n const range = this.normalizeRange(options)\n const excludedSessionIds = this.getExcludedSessionIds()\n const signature = buildTimeInvestmentSignature(this.deps.adapter, range, excludedSessionIds)\n const status = this.getCacheStatus(signature, range)\n if (this.shouldStartTask(status)) this.ensureTaskStarted(signature, range, excludedSessionIds)\n return this.toResponse(signature, range, options.acceptStale === true)\n }\n\n startRecompute(options: TimeInvestmentServiceOptions = {}): TimeInvestmentResponse {\n const range = this.normalizeRange(options)\n const excludedSessionIds = this.getExcludedSessionIds()\n const signature = buildTimeInvestmentSignature(this.deps.adapter, range, excludedSessionIds)\n this.ensureTaskStarted(signature, range, excludedSessionIds)\n return this.toResponse(signature, range, true)\n }\n\n async close(): Promise<void> {\n const inFlight = this.inFlight\n if (!inFlight) return\n this.inFlight = null\n inFlight.abortController.abort()\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n lastError: 'time investment task aborted',\n }\n }\n\n private shouldStartTask(status: TimeInvestmentResponse['cache']['status']): boolean {\n if (status === 'fresh') return false\n return this.task.status !== 'failed'\n }\n\n private normalizeRange(input: AnnualSummaryRangeInput = {}): AnnualSummaryRange {\n return normalizeAnnualSummaryRange(input, new Date(this.now()))\n }\n\n private ensureTaskStarted(signature: string, range: AnnualSummaryRange, excludedSessionIds: readonly string[]): void {\n if (this.inFlight) return\n const id = `time_investment_${this.now()}_${Math.random().toString(36).slice(2)}`\n this.task = {\n id,\n status: 'running',\n startedAt: this.now(),\n finishedAt: null,\n processedSessions: 0,\n totalSessions: listOwnerInsightSessionIds(this.deps.adapter, excludedSessionIds).length,\n }\n const abortController = new AbortController()\n const promise = this.runner({\n signature,\n range,\n excludedSessionIds,\n signal: abortController.signal,\n onProgress: (progress) => {\n if (this.task.id !== id || this.task.status !== 'running') return\n this.task = { ...this.task, ...progress }\n },\n })\n this.inFlight = { id, promise, abortController }\n promise\n .then((snapshot) => this.handleTaskSuccess(id, signature, snapshot))\n .catch((error) => this.handleTaskFailure(id, error))\n }\n\n private handleTaskSuccess(id: string, inputSignature: string, snapshot: TimeInvestmentSnapshot): void {\n if (this.inFlight?.id !== id) return\n const latestSignature = buildTimeInvestmentSignature(\n this.deps.adapter,\n snapshot.range,\n this.getExcludedSessionIds()\n )\n const finishedAt = this.now()\n if (inputSignature !== latestSignature || snapshot.signature !== latestSignature) {\n this.inFlight = null\n this.task = { ...this.task, status: 'superseded', finishedAt, currentSessionId: undefined }\n appLogger.info('global-insight', 'time investment worker result discarded because signature changed')\n return\n }\n try {\n writeTimeInvestmentSnapshot(this.snapshotDir, snapshot)\n this.inFlight = null\n this.snapshots.set(toAnnualSummaryRangeKey(snapshot.range), snapshot)\n this.task = {\n ...this.task,\n status: 'succeeded',\n finishedAt,\n processedSessions: snapshot.workerStats.processedSessions,\n totalSessions: snapshot.workerStats.totalSessions,\n currentSessionId: undefined,\n }\n appLogger.info('global-insight', 'time investment snapshot persisted', {\n analyzedSessions: snapshot.coverage.analyzedSessions,\n durationMs: snapshot.workerStats.durationMs,\n })\n } catch (error) {\n this.handleTaskFailure(id, error)\n }\n }\n\n private handleTaskFailure(id: string, error: unknown): void {\n if (this.inFlight?.id !== id) return\n this.inFlight = null\n this.task = {\n ...this.task,\n status: 'failed',\n finishedAt: this.now(),\n currentSessionId: undefined,\n lastError: error instanceof Error ? error.message : String(error),\n }\n appLogger.error('global-insight', 'time investment worker failed', error)\n }\n\n private toResponse(signature: string, range: AnnualSummaryRange, acceptStale: boolean): TimeInvestmentResponse {\n const snapshot = this.getSnapshot(range)\n const status = this.getCacheStatus(signature, range)\n const includeSnapshot = status === 'fresh' || (status === 'stale' && acceptStale)\n return {\n range,\n availableDataYears: includeSnapshot ? (snapshot?.availableDataYears ?? []) : [],\n latestDataYear: includeSnapshot ? (snapshot?.latestDataYear ?? null) : null,\n metrics: includeSnapshot ? (snapshot?.metrics ?? null) : null,\n monthlyActivity: includeSnapshot ? (snapshot?.monthlyActivity ?? []) : [],\n dailyActivity: includeSnapshot ? (snapshot?.dailyActivity ?? []) : [],\n sessionRanking: includeSnapshot ? (snapshot?.sessionRanking ?? []) : [],\n chatTypes: includeSnapshot ? (snapshot?.chatTypes ?? []) : [],\n coverage: includeSnapshot ? (snapshot?.coverage ?? emptyCoverage()) : emptyCoverage(),\n cache: {\n status,\n computedAt: snapshot?.computedAt ?? null,\n signature: snapshot?.signature,\n staleReason: status === 'stale' ? 'signature_changed' : undefined,\n },\n task: this.task,\n }\n }\n\n private getCacheStatus(signature: string, range: AnnualSummaryRange): TimeInvestmentResponse['cache']['status'] {\n const snapshot = this.getSnapshot(range)\n if (!snapshot) return 'missing'\n return snapshot.signature === signature ? 'fresh' : 'stale'\n }\n\n private getSnapshot(range: AnnualSummaryRange): TimeInvestmentSnapshot | null {\n const key = toAnnualSummaryRangeKey(range)\n if (this.snapshots.has(key) && !fs.existsSync(getTimeInvestmentSnapshotPath(this.snapshotDir, range))) {\n this.snapshots.delete(key)\n }\n if (!this.snapshots.has(key)) {\n this.snapshots.set(key, readTimeInvestmentSnapshot(this.snapshotDir, range, { now: this.deps.now }))\n }\n return this.snapshots.get(key) ?? null\n }\n\n private now(): number {\n return this.deps.now?.() ?? Date.now()\n }\n\n private getExcludedSessionIds(): readonly string[] {\n return this.deps.getExcludedSessionIds?.() ?? []\n }\n}\n\nfunction requirePathProvider(deps: TimeInvestmentServiceDeps): PathProvider {\n if (!deps.pathProvider)\n throw new Error('TimeInvestmentService requires pathProvider unless userDataDir and runner are provided')\n return deps.pathProvider\n}\n\nfunction createIdleTaskState(): AnnualSummaryTaskState {\n return {\n id: null,\n status: 'idle',\n startedAt: null,\n finishedAt: null,\n processedSessions: 0,\n totalSessions: 0,\n }\n}\n\nfunction emptyCoverage(): TimeInvestmentResponse['coverage'] {\n return {\n totalSessions: 0,\n analyzedSessions: 0,\n missingOwnerSessions: 0,\n unresolvedOwnerSessions: 0,\n failedSessions: 0,\n }\n}\n","import type { AnnualSummaryRange } from '@openchatlab/shared-types'\nimport { getDbFileVersion } from '../../cache/analytics-cache'\nimport type { SessionRuntimeAdapter } from '../adapters'\nimport { toAnnualSummaryLocalDate, toAnnualSummaryRangeKey } from './time-range'\nimport { TIME_INVESTMENT_ALGORITHM_VERSION } from './time-investment-types'\nimport { listOwnerInsightSessionIds } from './session-scope'\n\nexport function buildTimeInvestmentSignature(\n adapter: SessionRuntimeAdapter,\n range: AnnualSummaryRange,\n excludedSessionIds: readonly string[] = []\n): string {\n const parts = [\n `algorithm:${TIME_INVESTMENT_ALGORITHM_VERSION}`,\n `range:${toAnnualSummaryRangeKey(range)}`,\n `start:${range.startTs}`,\n `day:${toAnnualSummaryLocalDate(range)}`,\n ]\n for (const sessionId of listOwnerInsightSessionIds(adapter, excludedSessionIds).sort()) {\n parts.push(`${sessionId}:${getDbFileVersion(adapter.getDbPath(sessionId))}`)\n }\n return parts.join('|')\n}\n","export { createGlobalInsightService } from './service'\nexport type { GlobalInsightService, GlobalInsightServiceDeps, GlobalInsightServiceOptions } from './service'\nexport { ANNUAL_SUMMARY_ALGORITHM_VERSION } from './types'\nexport type { AnnualSummaryComputeRunner, AnnualSummaryRunnerOptions } from './worker-runner'\nexport { getGlobalInsightDir, getGlobalInsightFactsCacheDir } from './paths'\nexport { deleteAnnualSummarySnapshots } from './snapshot'\nexport { createTimeInvestmentService } from './time-investment-service'\nexport type {\n TimeInvestmentService,\n TimeInvestmentServiceDeps,\n TimeInvestmentServiceOptions,\n} from './time-investment-service'\nexport { TIME_INVESTMENT_ALGORITHM_VERSION } from './time-investment-types'\nexport type { TimeInvestmentComputeRunner } from './worker-runner'\nexport { getTimeInvestmentDir, getTimeInvestmentFactsCacheDir } from './paths'\nexport { deleteTimeInvestmentSnapshots } from './time-investment-snapshot'\n","/**\n * 语义索引独立配置\n *\n * 存储位置:~/.chatlab/ai/semantic-index-config.json(独立于聊天 LLM 的 provider/model 管理)。\n * API Key 不落本配置,只保存 auth-profiles.json 中的 authProfile 引用。\n *\n * Phase 1 只有一个\"当前向量配置\":local 或 OpenAI-compatible。\n *\n * 重建信号:模型身份(resolveModelId)变化即需要重建。\n * - 换本地模型 / API baseUrl / API 模型名 -> 身份变化 -> 已启用对话索引需重建。\n * - 只改 API Key(authProfile 指向内容)-> 身份不变 -> 不重建。\n * chunk 以模型身份作为 model_id 分区,新配置重建完成前自然查不到旧索引。\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nexport type SemanticIndexMode = 'local' | 'api'\nexport type SemanticIndexModelDownloadSource = 'huggingface' | 'hf-mirror'\n\nexport const DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE: SemanticIndexModelDownloadSource = 'huggingface'\n\nexport interface SemanticIndexLocalConfig {\n /** 本地模型 profile 的 modelId */\n modelId: string\n /** 模型文件下载源;旧配置缺失时归一化为 Hugging Face 官方源 */\n downloadSource?: SemanticIndexModelDownloadSource\n}\n\nexport interface SemanticIndexApiConfig {\n baseUrl: string\n model: string\n /** auth-profiles.json 中的 profile 引用(只存引用,不存 key) */\n authProfile?: string\n /** API 模型维度;未知时运行时由返回向量长度确定 */\n dim?: number\n}\n\nexport interface SemanticIndexConfig {\n version: number\n /** 全局功能开关:关闭后不暴露 AI 检索工具、不建立/检索索引(已有索引数据保留) */\n enabled: boolean\n mode: SemanticIndexMode\n local: SemanticIndexLocalConfig\n api: SemanticIndexApiConfig | null\n /** AI 单次语义检索默认返回片段数(范围 5-15) */\n searchMaxResults: number\n}\n\n/** setConfig 入参:searchMaxResults / enabled 可省略(缺省由 normalize 填充默认值) */\nexport type SemanticIndexConfigInput = Omit<SemanticIndexConfig, 'searchMaxResults' | 'enabled'> & {\n searchMaxResults?: number\n enabled?: boolean\n}\n\nexport const SEMANTIC_INDEX_CONFIG_VERSION = 1\n\n/** 单次检索默认片段数与用户可配置范围 */\nexport const SEARCH_MAX_RESULTS_DEFAULT = 10\nexport const SEARCH_MAX_RESULTS_MIN = 5\nexport const SEARCH_MAX_RESULTS_MAX = 15\n/** LLM 单次检索片段数硬上限(高于用户可配置范围,仍受证据/token 预算约束) */\nexport const SEARCH_MAX_RESULTS_HARD_CAP = 20\n\nexport function clampSearchMaxResults(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return SEARCH_MAX_RESULTS_DEFAULT\n return Math.max(SEARCH_MAX_RESULTS_MIN, Math.min(SEARCH_MAX_RESULTS_MAX, Math.floor(value)))\n}\n\n/**\n * 默认配置:功能开启,但不预选任何向量模型(modelId 为空 = 未选择)。\n * 用户必须在设置中显式选择模型后才视为已配置(isConfigured)。\n */\nexport function defaultSemanticIndexConfig(): SemanticIndexConfig {\n return {\n version: SEMANTIC_INDEX_CONFIG_VERSION,\n enabled: true,\n mode: 'local',\n local: { modelId: '', downloadSource: DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE },\n api: null,\n searchMaxResults: SEARCH_MAX_RESULTS_DEFAULT,\n }\n}\n\n/**\n * 当前配置的模型身份。作为 chunk 的 model_id 分区键与重建信号。\n */\nexport function resolveModelId(config: SemanticIndexConfig): string {\n if (config.mode === 'api' && config.api) {\n return `api:${config.api.baseUrl}#${config.api.model}`\n }\n return config.local.modelId\n}\n\n/** 用户是否已显式选择向量模型(本地有 modelId,或 API 已填 baseUrl+model)。 */\nexport function isSemanticIndexConfigured(config: SemanticIndexConfig): boolean {\n if (config.mode === 'local') return config.local.modelId.trim().length > 0\n return !!config.api && config.api.baseUrl.trim().length > 0 && config.api.model.trim().length > 0\n}\n\n/** 是否允许建立/检索索引:必须同时开启全局开关并完成模型配置。 */\nexport function canRunSemanticIndex(config: SemanticIndexConfig): boolean {\n return config.enabled && isSemanticIndexConfigured(config)\n}\n\n/** Local Ollama's OpenAI-compatible endpoint does not require authentication. */\nexport function isKeylessSemanticIndexApiBaseUrl(baseUrl: string | undefined): boolean {\n if (!baseUrl) return false\n try {\n const url = new URL(baseUrl)\n const host = url.hostname.toLowerCase()\n const isLoopback = host === 'localhost' || host === '127.0.0.1' || host === '::1'\n const port = url.port || (url.protocol === 'https:' ? '443' : '80')\n return isLoopback && port === '11434'\n } catch {\n return false\n }\n}\n\nfunction normalizeConfig(raw: Partial<SemanticIndexConfig> | null | undefined): SemanticIndexConfig {\n const base = defaultSemanticIndexConfig()\n if (!raw || typeof raw !== 'object') return base\n const mode: SemanticIndexMode = raw.mode === 'api' ? 'api' : 'local'\n const api = raw.api\n ? {\n baseUrl: raw.api.baseUrl ?? '',\n model: raw.api.model ?? '',\n authProfile: raw.api.authProfile,\n dim: raw.api.dim,\n }\n : null\n return {\n version: SEMANTIC_INDEX_CONFIG_VERSION,\n // 旧配置无 enabled 字段时默认开启,保证既有用户功能不被意外关闭\n enabled: raw.enabled ?? true,\n mode,\n local: {\n modelId: raw.local?.modelId ?? base.local.modelId,\n downloadSource:\n raw.local?.downloadSource === 'hf-mirror' ? 'hf-mirror' : DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE,\n },\n api,\n searchMaxResults: SEARCH_MAX_RESULTS_DEFAULT,\n }\n}\n\nexport class SemanticIndexConfigStore {\n private filePath: string\n\n constructor(filePath: string) {\n this.filePath = filePath\n }\n\n /** 用户是否已显式选择向量模型(本地有 modelId,或 API 已填 baseUrl+model)。未选择时为 false。 */\n isConfigured(): boolean {\n return isSemanticIndexConfigured(this.get())\n }\n\n isEnabled(): boolean {\n return this.get().enabled\n }\n\n canRun(): boolean {\n return canRunSemanticIndex(this.get())\n }\n\n /** 仅更新全局开关,保持已选模型与其余配置不变 */\n setEnabled(enabled: boolean): SemanticIndexConfig {\n return this.set({ ...this.get(), enabled })\n }\n\n get(): SemanticIndexConfig {\n if (!fs.existsSync(this.filePath)) return defaultSemanticIndexConfig()\n try {\n const raw = JSON.parse(fs.readFileSync(this.filePath, 'utf-8')) as Partial<SemanticIndexConfig>\n return normalizeConfig(raw)\n } catch {\n return defaultSemanticIndexConfig()\n }\n }\n\n set(config: SemanticIndexConfigInput): SemanticIndexConfig {\n const normalized = normalizeConfig(config)\n const dir = path.dirname(this.filePath)\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(this.filePath, JSON.stringify(normalized, null, 2), 'utf-8')\n return normalized\n }\n\n resolveModelId(): string {\n return resolveModelId(this.get())\n }\n}\n","/**\n * Embedding 输入文本处理(纯函数)\n */\n\n/** 给 query 加模型 queryInstruction 前缀;document 不调用此函数 */\nexport function applyQueryInstruction(instruction: string, query: string): string {\n const trimmed = instruction.trim()\n if (!trimmed) return query\n if (trimmed.endsWith(':') || trimmed.endsWith(':')) return `${trimmed}${query}`\n return `Instruct: ${trimmed}\\nQuery: ${query}`\n}\n\n/** 按字符上限截断文本;maxChars 未提供或文本更短时原样返回 */\nexport function clampTextChars(text: string, maxChars?: number): string {\n if (maxChars === undefined || text.length <= maxChars) return text\n return text.slice(0, maxChars)\n}\n","/**\n * 本地 ONNX embedding provider(@huggingface/transformers)\n *\n * 关键约束(P0-3):Qwen3 必须 batch=1,batch 会污染 last_token embedding;本地 CPU\n * 上 batch 也无吞吐收益。profile.maxBatchSize 控制单次推理的最大文本数。\n *\n * pipeline 工厂可注入,单元测试用 fake 工厂验证 batch 切分与 query instruction,不下载模型;\n * 真实模型加载在 env-gated smoke 测试中验证。\n */\n\nimport type { EmbeddingProvider } from './types'\nimport type { LocalEmbeddingProfile } from './profiles'\nimport { applyQueryInstruction, clampTextChars } from './text'\nimport type { SemanticIndexModelDownloadSource } from '../config'\n\ntype UndiciFetch = (typeof import('undici'))['fetch']\ntype UndiciRequestInit = NonNullable<Parameters<UndiciFetch>[1]>\nconst SOCKS_PROXY_PROTOCOLS = new Set(['socks:', 'socks4:', 'socks5:'])\nexport const LOCAL_ONNX_SESSION_OPTIONS = { intraOpNumThreads: 1, interOpNumThreads: 1 } as const\nexport const HUGGING_FACE_MODEL_HOST = 'https://huggingface.co/'\nexport const HF_MIRROR_MODEL_HOST = 'https://hf-mirror.com/'\n\nexport function resolveModelDownloadRemoteHost(source?: SemanticIndexModelDownloadSource): string {\n return source === 'hf-mirror' ? HF_MIRROR_MODEL_HOST : HUGGING_FACE_MODEL_HOST\n}\n\n/** 一次特征抽取调用:输入文本数组,返回每条文本的向量(number[][]) */\nexport type FeatureExtractFn = (\n texts: string[],\n options: { pooling: LocalEmbeddingProfile['pooling']; normalize: boolean }\n) => Promise<number[][]>\n\n/** pipeline 工厂:按 modelId/dtype/cacheDir 构造特征抽取器 */\nexport type LocalPipelineFactory = (params: {\n modelId: string\n dtype?: 'fp32' | 'q8'\n cacheDir?: string\n modelDownloadProxyUrl?: string\n modelDownloadSource?: SemanticIndexModelDownloadSource\n sessionOptions?: typeof LOCAL_ONNX_SESSION_OPTIONS\n}) => Promise<FeatureExtractFn>\n\nexport type TransformersModule = typeof import('@huggingface/transformers')\nexport type LoadTransformers = () => Promise<TransformersModule>\n\nexport async function createProxyFetch(proxyUrl: string): Promise<typeof fetch> {\n const parsed = new URL(proxyUrl)\n if (SOCKS_PROXY_PROTOCOLS.has(parsed.protocol)) {\n throw new Error(\n `SOCKS proxy is not supported for local embedding model downloads: ${parsed.protocol}//${parsed.host}`\n )\n }\n const { fetch: undiciFetch, ProxyAgent } = await import('undici')\n const dispatcher = new ProxyAgent(proxyUrl)\n return ((input, init) => {\n const nextInit: UndiciRequestInit = { ...(init as UndiciRequestInit | undefined), dispatcher }\n return undiciFetch(input as Parameters<UndiciFetch>[0], nextInit) as unknown as ReturnType<typeof fetch>\n }) as typeof fetch\n}\n\nexport function createTransformersPipelineFactory(loadTransformers: LoadTransformers): LocalPipelineFactory {\n return async ({ modelId, dtype, cacheDir, modelDownloadProxyUrl, modelDownloadSource }) => {\n const transformers = await loadTransformers()\n // Transformers.js 的 env 是 worker 进程级全局对象,因此每次创建 pipeline 都显式覆盖 host,\n // 避免用户从镜像切回官方源后继续复用旧的 remoteHost。\n transformers.env.remoteHost = resolveModelDownloadRemoteHost(modelDownloadSource)\n if (cacheDir) {\n transformers.env.cacheDir = cacheDir\n transformers.env.allowRemoteModels = true\n }\n if (modelDownloadProxyUrl) {\n transformers.env.fetch = await createProxyFetch(modelDownloadProxyUrl)\n }\n const extractor = await transformers.pipeline('feature-extraction', modelId, {\n ...(dtype ? { dtype } : {}),\n session_options: LOCAL_ONNX_SESSION_OPTIONS,\n })\n return async (texts, options) => {\n const out = await extractor(texts, { pooling: options.pooling, normalize: options.normalize })\n return out.tolist() as number[][]\n }\n }\n}\n\nconst defaultPipelineFactory = createTransformersPipelineFactory(() => import('@huggingface/transformers'))\n\nexport interface LocalEmbeddingProviderOptions {\n cacheDir?: string\n modelDownloadProxyUrl?: string\n modelDownloadSource?: SemanticIndexModelDownloadSource\n pipelineFactory?: LocalPipelineFactory\n}\n\nexport class LocalEmbeddingProvider implements EmbeddingProvider {\n readonly modelId: string\n readonly dim: number\n readonly maxTokens: number\n readonly documentBatchSize: number\n\n private profile: LocalEmbeddingProfile\n private options: LocalEmbeddingProviderOptions\n private extractorPromise: Promise<FeatureExtractFn> | null = null\n\n constructor(profile: LocalEmbeddingProfile, options: LocalEmbeddingProviderOptions = {}) {\n this.profile = profile\n this.options = options\n this.modelId = profile.modelId\n this.dim = profile.dim\n this.maxTokens = profile.maxTokens\n this.documentBatchSize = profile.maxBatchSize ?? 1\n }\n\n private getExtractor(): Promise<FeatureExtractFn> {\n if (!this.extractorPromise) {\n const factory = this.options.pipelineFactory ?? defaultPipelineFactory\n this.extractorPromise = factory({\n modelId: this.profile.modelId,\n dtype: this.profile.dtype,\n cacheDir: this.options.cacheDir,\n modelDownloadProxyUrl: this.options.modelDownloadProxyUrl,\n modelDownloadSource: this.options.modelDownloadSource,\n sessionOptions: LOCAL_ONNX_SESSION_OPTIONS,\n }).catch((error) => {\n this.extractorPromise = null\n throw error\n })\n }\n return this.extractorPromise\n }\n\n private toFloat32(vector: number[]): Float32Array {\n if (vector.length !== this.profile.dim) {\n throw new Error(`model ${this.profile.modelId} returned dim ${vector.length}, expected ${this.profile.dim}`)\n }\n return Float32Array.from(vector)\n }\n\n async embedDocuments(texts: string[]): Promise<Float32Array[]> {\n if (texts.length === 0) return []\n const extractor = await this.getExtractor()\n const batchSize = this.profile.maxBatchSize ?? texts.length\n const clamped = texts.map((t) => clampTextChars(t, this.profile.maxTextChars))\n\n const result: Float32Array[] = []\n for (let i = 0; i < clamped.length; i += batchSize) {\n const batch = clamped.slice(i, i + batchSize)\n const vectors = await extractor(batch, { pooling: this.profile.pooling, normalize: this.profile.normalize })\n for (const v of vectors) result.push(this.toFloat32(v))\n }\n return result\n }\n\n async embedQuery(text: string): Promise<Float32Array> {\n const withInstruction = applyQueryInstruction(this.profile.queryInstruction, text)\n const [vector] = await this.embedDocuments([withInstruction])\n return vector\n }\n\n async preload(): Promise<void> {\n await this.getExtractor()\n }\n}\n","/**\n * 语义索引共享 service(node-runtime,Electron 与 CLI Web 复用)\n *\n * 职责:把配置、向量库、业务状态、串行队列、warmup、检索、证据组装与聊天库适配器\n * 编排成一个有状态服务。每个运行时进程一个实例,由 server 启动时创建、停止时 close。\n * 不做隐藏全局单例。\n *\n * 关键约定(见用户决策):\n * - 启用/状态以业务状态表为权威,队列只负责执行。\n * - 模型身份(resolveModelId)变化即需重建;模型身份不变(仅换 API Key)不重建。\n * 身份不一致时检索不使用旧索引。\n * - 启动恢复只把 stale running 标记为 paused,不自动续跑。\n */\n\nimport path from 'node:path'\nimport {\n writeAuthProfile as defaultWriteAuthProfile,\n resolveApiKey as defaultResolveApiKey,\n type AuthProfile,\n} from '@openchatlab/config'\nimport type { DatabaseAdapter, PathProvider } from '@openchatlab/core'\nimport { CHUNKER_VERSION, DEFAULT_CHUNKER_CONFIG, computeChunkerConfigHash, computeDbPathHash } from './chunker-config'\nimport type { ChunkSource } from './chunker'\nimport {\n isKeylessSemanticIndexApiBaseUrl,\n isSemanticIndexConfigured,\n SemanticIndexConfigStore,\n DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE,\n type SemanticIndexConfig,\n type SemanticIndexConfigInput,\n} from './config'\nimport { createEmbedder, type EmbedderFactoryDeps } from './embedder-factory'\nimport type { EmbeddingProvider, SemanticIndexModelStatus } from './embedding/types'\nimport type { LocalEmbeddingRuntimeStatus } from './embedding/local-runtime'\nimport { EmbeddingIndexStore, type LoadSqliteVec } from './store'\nimport {\n SemanticIndexStateStore,\n type SemanticIndexSessionState,\n type SemanticIndexStatus,\n} from './session-state-store'\nimport { SemanticIndexJobQueue, type JobContext } from './warmup/job-queue'\nimport { runWarmup } from './warmup/runner'\nimport { semanticSearch } from './retrieval/semantic-search'\nimport { assembleEvidence, type EvidenceBlock, type EvidenceBudget, type EvidenceHit } from './retrieval/evidence'\nimport { createChatDbMessageSource } from './chat-db/message-source'\nimport { createChatDbMessageRangeReader } from './chat-db/message-range-reader'\nimport type { SessionRuntimeAdapter } from '../services/adapters'\nimport { clampSearchMaxResults, SEARCH_MAX_RESULTS_HARD_CAP } from './config'\nimport { applyPreprocessingPipeline } from '../ai/preprocessor/preprocessing-pipeline'\nimport type { PreprocessConfig, PreprocessableMessage } from '../ai/preprocessor/types'\nimport { appLogger } from '../logging/app-logger'\n\nexport interface SemanticIndexServiceOptions {\n /** embedding_index.db 路径({vectorDir}/embedding_index.db) */\n vectorDbPath: string\n /** semantic-index-config.json 路径({aiDataDir}/semantic-index-config.json) */\n configPath: string\n sessionAdapter: SessionRuntimeAdapter\n /** 本地模型缓存目录 */\n modelsCacheDir?: string\n /** Optional HTTP(S) proxy URL used only for local embedding model downloads. */\n modelDownloadProxyUrl?: string\n /** embedder 工厂注入(测试/平台) */\n embedderFactoryDeps?: EmbedderFactoryDeps\n /** sqlite-vec 加载器注入(Electron 打包) */\n loadSqliteVec?: LoadSqliteVec\n /** better-sqlite3 原生绑定路径(CLI 打包需要) */\n nativeBinding?: string\n /** auth profile 写入注入(测试用,避免写真实 ~/.chatlab) */\n writeAuthProfile?: (name: string, profile: AuthProfile) => void\n /** Optional managed local runtime status for CLI; Desktop uses its bundled runtime. */\n getLocalEmbeddingRuntimeStatus?: () => LocalEmbeddingRuntimeStatus\n}\n\n/** 语义索引 API Key 固定存储在此 auth profile;config 只保存引用,不保存明文 */\nexport const SEMANTIC_INDEX_AUTH_PROFILE = 'semantic-index-embedding'\n\n/**\n * 仅持久化配置与(可选)API Key,不依赖向量库。\n * 供 SemanticIndexService.setConfig 复用,也供 service 不可用(如 sqlite-vec 加载失败)\n * 的降级路径直接写配置,保证 API 模式下 key 不被丢弃。\n */\nexport function persistSemanticIndexConfig(\n configStore: SemanticIndexConfigStore,\n config: SemanticIndexConfigInput,\n options?: { apiKey?: string; writeAuthProfile?: (name: string, profile: AuthProfile) => void }\n): SemanticIndexConfig {\n let next = config\n if (options?.apiKey && config.mode === 'api' && config.api) {\n const writer = options.writeAuthProfile ?? defaultWriteAuthProfile\n writer(SEMANTIC_INDEX_AUTH_PROFILE, { type: 'api_key', provider: 'semantic-index', key: options.apiKey })\n next = { ...config, api: { ...config.api, authProfile: SEMANTIC_INDEX_AUTH_PROFILE } }\n }\n return configStore.set(next)\n}\n\n/** 当前配置的 API 模式是否已设置可用 Key(不返回明文)。service 与降级路径共用。 */\nexport function resolveSemanticIndexApiKeySet(\n config: SemanticIndexConfig,\n resolveApiKey: (provider: string, authProfile?: string) => string = defaultResolveApiKey\n): boolean {\n if (config.mode !== 'api' || !config.api || !config.api.authProfile) return false\n return resolveApiKey('semantic-index', config.api.authProfile) !== ''\n}\n\nexport interface SemanticIndexSessionStatus {\n sessionId: string\n enabled: boolean\n indexStatus: SemanticIndexStatus\n needsRebuild: boolean\n /** completed session has received new messages since last index run */\n hasNewMessages?: boolean\n totalMessages: number\n indexedMessages: number\n chunkCount: number\n coverage: number\n queued: boolean\n running: boolean\n partial: boolean\n error: string | null\n modelId: string | null\n}\n\nexport type SemanticSearchReason = 'disabled' | 'needs-rebuild' | 'empty' | 'not-found'\n\n/** 面向 AI 工具的安全来源条目(已脱敏,不含原始消息)。结构与 @openchatlab/tools 一致。 */\nexport interface SemanticSearchToolSource {\n startMessageId: number\n endMessageId: number\n score: number\n chunkIds: string[]\n snippet: string\n text?: string\n startTime?: string\n endTime?: string\n}\n\n/** 面向 AI 工具的检索结果:text 已经过 applyPreprocessingPipeline 清洗/脱敏/匿名化/截断 */\nexport interface SemanticSearchToolResult {\n available: boolean\n reason?: string\n text: string\n returned: number\n hitCount: number\n partial: boolean\n coverage: number\n truncated: boolean\n timeRange?: { earliest: string; latest: string }\n sources: SemanticSearchToolSource[]\n}\n\nexport interface SemanticSearchToolOptions {\n maxResults?: number\n preprocessConfig?: Record<string, unknown>\n ownerPlatformId?: string\n locale?: string\n maxResultTokens?: number\n /** 毫秒级时间范围过滤(可单边);仅保留与 chunk 时间范围有交集的语义候选 */\n timeFilter?: { startTs?: number; endTs?: number }\n}\n\n/** 工具返回片段预览的最大字符数 */\nconst SEARCH_TOOL_SNIPPET_MAX = 160\n\n/**\n * 注入证据的固定 token 预算(与 max_results 解耦)。\n * max_results 只决定召回候选数,最终注入量由本预算(再以 maxResultTokens 封顶)控制,\n * 避免候选变多导致上下文 token 膨胀;单块 soft cap 保持较小,让宽泛检索优先增加结果数量。\n */\nconst SEARCH_TOOL_EVIDENCE_TOKENS = 8000\n\nexport function resolveSearchToolEvidenceTokens(maxResultTokens?: number): number {\n return maxResultTokens && maxResultTokens > 0\n ? Math.min(SEARCH_TOOL_EVIDENCE_TOKENS, maxResultTokens)\n : SEARCH_TOOL_EVIDENCE_TOKENS\n}\n\nexport interface SemanticSearchResult {\n available: boolean\n reason?: SemanticSearchReason\n blocks: EvidenceBlock[]\n coverage: number\n /** 索引未完成时为 true,证据可能不完整 */\n partial: boolean\n /** Dense retrieval candidates before evidence assembly. */\n hitCount: number\n}\n\nfunction sessionIdFromDbPath(dbPath: string): string {\n return path.basename(dbPath, '.db')\n}\n\n/** 语义索引相关文件名(统一约定,避免各端硬编码) */\nexport const SEMANTIC_INDEX_DB_FILE = 'embedding_index.db'\nexport const SEMANTIC_INDEX_CONFIG_FILE = 'semantic-index-config.json'\n\n/**\n * 按 PathProvider 约定路径创建 service:\n * - 向量库:{vectorDir}/embedding_index.db\n * - 配置:{aiDataDir}/semantic-index-config.json\n * - 本地模型:{aiDataDir}/models/semantic-index(不在 cache 目录,不随清理缓存丢失)\n */\nexport function createSemanticIndexService(params: {\n pathProvider: PathProvider\n sessionAdapter: SessionRuntimeAdapter\n nativeBinding?: string\n loadSqliteVec?: LoadSqliteVec\n embedderFactoryDeps?: EmbedderFactoryDeps\n getLocalEmbeddingRuntimeStatus?: () => LocalEmbeddingRuntimeStatus\n modelDownloadProxyUrl?: string\n}): SemanticIndexService {\n const { pathProvider } = params\n return new SemanticIndexService({\n vectorDbPath: path.join(pathProvider.getVectorDir(), SEMANTIC_INDEX_DB_FILE),\n configPath: path.join(pathProvider.getAiDataDir(), SEMANTIC_INDEX_CONFIG_FILE),\n modelsCacheDir: path.join(pathProvider.getAiDataDir(), 'models', 'semantic-index'),\n modelDownloadProxyUrl: params.modelDownloadProxyUrl,\n sessionAdapter: params.sessionAdapter,\n nativeBinding: params.nativeBinding,\n loadSqliteVec: params.loadSqliteVec,\n embedderFactoryDeps: params.embedderFactoryDeps,\n getLocalEmbeddingRuntimeStatus: params.getLocalEmbeddingRuntimeStatus,\n })\n}\n\nexport class SemanticIndexService {\n private store: EmbeddingIndexStore\n private stateStore: SemanticIndexStateStore\n private configStore: SemanticIndexConfigStore\n private queue: SemanticIndexJobQueue\n private sessionAdapter: SessionRuntimeAdapter\n private options: SemanticIndexServiceOptions\n\n private embedder: EmbeddingProvider | null = null\n private embedderModelId: string | null = null\n private modelPreloadStatus: 'idle' | 'downloading' | 'ready' | 'error' = 'idle'\n private modelPreloadGeneration = 0\n\n /** 当前激活的 chunker 身份(version + 参数 hash),随索引记录用于重建判定 */\n private readonly chunkerVersion = CHUNKER_VERSION\n private readonly chunkerConfigHash = computeChunkerConfigHash(DEFAULT_CHUNKER_CONFIG)\n\n constructor(options: SemanticIndexServiceOptions) {\n this.options = options\n this.sessionAdapter = options.sessionAdapter\n this.store = new EmbeddingIndexStore(options.vectorDbPath, {\n loadSqliteVec: options.loadSqliteVec,\n nativeBinding: options.nativeBinding,\n })\n this.stateStore = new SemanticIndexStateStore(options.vectorDbPath, { nativeBinding: options.nativeBinding })\n this.configStore = new SemanticIndexConfigStore(options.configPath)\n this.queue = new SemanticIndexJobQueue((ctx) => this.runJob(ctx))\n }\n\n // ---------- 配置 ----------\n\n getConfig(): SemanticIndexConfig {\n return this.configStore.get()\n }\n\n /** 用户是否已显式配置向量模型(区分默认兜底配置) */\n isConfigured(): boolean {\n return this.configStore.isConfigured()\n }\n\n /** 全局功能开关是否开启 */\n isEnabled(): boolean {\n return this.configStore.isEnabled()\n }\n\n /** 语义索引是否可实际建立/检索:全局开启且已选择模型 */\n canRun(): boolean {\n if (!this.configStore.canRun()) return false\n const cfg = this.configStore.get()\n return cfg.mode !== 'api' || isKeylessSemanticIndexApiBaseUrl(cfg.api?.baseUrl) || this.hasApiKey()\n }\n\n /** 当前 API 模式是否已配置可用的 API Key(不返回明文,仅布尔) */\n hasApiKey(): boolean {\n return resolveSemanticIndexApiKeySet(this.configStore.get(), this.options.embedderFactoryDeps?.resolveApiKey)\n }\n\n /**\n * 保存配置;模型身份变化会使已启用对话的索引在重建完成前不被检索使用。\n * 传入 apiKey 时写入固定 auth profile,config 内只保存引用,绝不持久化明文。\n */\n getModelStatus(): SemanticIndexModelStatus {\n if (this.modelPreloadStatus !== 'downloading') return this.modelPreloadStatus\n return this.options.getLocalEmbeddingRuntimeStatus?.() === 'installing' ? 'installing-runtime' : 'downloading-model'\n }\n\n setConfig(config: SemanticIndexConfigInput, options?: { apiKey?: string }): SemanticIndexConfig {\n const saved = persistSemanticIndexConfig(this.configStore, config, {\n apiKey: options?.apiKey,\n writeAuthProfile: this.options.writeAuthProfile,\n })\n // 每次保存配置都使旧 preload 回调失效,避免切换下载源后旧请求乱序覆盖新状态。\n const preloadGeneration = ++this.modelPreloadGeneration\n this.embedder = null\n this.embedderModelId = null\n this.modelPreloadStatus = 'idle'\n // 关闭全局开关时停止所有在跑/排队的建索引任务(保留已建立的部分数据)\n if (!saved.enabled) {\n for (const state of this.stateStore.listEnabled()) this.queue.pause(state.dbPathHash)\n }\n this.startLocalModelPreload(saved, preloadGeneration)\n return saved\n }\n\n private startLocalModelPreload(\n config: SemanticIndexConfig,\n preloadGeneration: number,\n options: { defer?: boolean } = {}\n ): void {\n if (!config.enabled || !isSemanticIndexConfigured(config) || config.mode !== 'local') return\n\n const modelId = config.local.modelId\n const downloadSource = config.local.downloadSource ?? DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE\n const cacheDir = this.options.modelsCacheDir\n this.modelPreloadStatus = 'downloading'\n const run = () => {\n if (preloadGeneration !== this.modelPreloadGeneration) return\n appLogger.info('semantic-index', 'local embedding model preload started', { modelId, downloadSource, cacheDir })\n void this.getEmbedder()\n .preload?.()\n .then(() => {\n if (preloadGeneration !== this.modelPreloadGeneration) return\n this.modelPreloadStatus = 'ready'\n appLogger.info('semantic-index', 'local embedding model preload completed', {\n modelId,\n downloadSource,\n cacheDir,\n })\n })\n .catch((error) => {\n if (preloadGeneration !== this.modelPreloadGeneration) return\n this.modelPreloadStatus = 'error'\n appLogger.error(\n 'semantic-index',\n `local embedding model preload failed: ${modelId} via ${downloadSource}`,\n error\n )\n })\n }\n if (options.defer) queueMicrotask(run)\n else run()\n }\n\n private currentModelId(): string {\n return this.configStore.resolveModelId()\n }\n\n private getEmbedder(): EmbeddingProvider {\n const modelId = this.currentModelId()\n if (!this.embedder || this.embedderModelId !== modelId) {\n this.embedder = createEmbedder(this.configStore.get(), {\n modelsCacheDir: this.options.modelsCacheDir,\n modelDownloadProxyUrl: this.options.modelDownloadProxyUrl,\n ...this.options.embedderFactoryDeps,\n })\n this.embedderModelId = modelId\n }\n return this.embedder\n }\n\n // ---------- 启用 / 生命周期 ----------\n\n private hashFor(sessionId: string): string {\n return computeDbPathHash(sessionId)\n }\n\n /**\n * 查找 sessionId 对应的实际 db_path_hash。\n * 优先用当前规则(computeDbPathHash(sessionId)),找不到时扫全表匹配 dbPath basename,\n * 兼容旧版本用完整 dbPath 计算 hash 存储的历史记录。\n */\n private resolveHash(sessionId: string): string {\n const hash = this.hashFor(sessionId)\n if (this.stateStore.getState(hash)) return hash\n const legacy = this.stateStore.listAll().find((s) => sessionIdFromDbPath(s.dbPath) === sessionId)\n return legacy?.dbPathHash ?? hash\n }\n\n /** 索引身份是否与当前运行时不一致(模型身份或 chunker 身份变化 => 需重建) */\n private isStale(state: SemanticIndexSessionState, currentModelId: string): boolean {\n return (\n state.modelId !== currentModelId ||\n state.chunkerVersion !== this.chunkerVersion ||\n state.chunkerConfigHash !== this.chunkerConfigHash\n )\n }\n\n private enableParams(\n dbPath: string,\n dbPathHash?: string\n ): {\n dbPathHash: string\n dbPath: string\n modelId: string\n chunkerVersion: string\n chunkerConfigHash: string\n } {\n return {\n dbPathHash: dbPathHash ?? computeDbPathHash(sessionIdFromDbPath(dbPath)),\n dbPath,\n modelId: this.currentModelId(),\n chunkerVersion: this.chunkerVersion,\n chunkerConfigHash: this.chunkerConfigHash,\n }\n }\n\n enable(sessionId: string): void {\n if (!this.canRun()) return\n const dbPath = this.sessionAdapter.getDbPath(sessionId)\n const hash = computeDbPathHash(sessionId)\n const existing = this.stateStore.getState(hash)\n const stale = !!existing && this.isStale(existing, this.currentModelId())\n if (stale) {\n // 保留旧身份字段,只重置 enabled 和 index_status。\n // 这样 app 在 rebuild 运行前退出后,重启时 buildAllPending() 仍能通过\n // isStale() 检测到需重建并入队 rebuild,避免永久卡死在\"新身份+无向量\"状态。\n this.stateStore.reactivate(hash, dbPath)\n this.stateStore.setIndexStatus(hash, 'idle')\n } else {\n this.stateStore.enable(this.enableParams(dbPath))\n }\n }\n\n /** 彻底移除:取消队列、删除向量数据、删除状态记录,立即生效无需二次清理 */\n remove(sessionId: string): void {\n const hash = this.resolveHash(sessionId)\n this.queue.cancel(hash)\n this.store.deleteByDbPathHash(hash)\n this.stateStore.remove(hash)\n }\n\n /** 建立 / 续跑索引(续跑从断点游标继续) */\n build(sessionId: string): void {\n if (!this.canRun()) return\n this.queue.enqueue({ type: 'build', dbPathHash: this.resolveHash(sessionId) })\n }\n\n pause(sessionId: string): void {\n this.queue.pause(this.resolveHash(sessionId))\n }\n\n cancel(sessionId: string): void {\n this.queue.cancel(this.resolveHash(sessionId))\n }\n\n /** 重建:换模型身份或用户主动重建时清空旧索引后重新建立 */\n rebuild(sessionId: string): void {\n if (!this.canRun()) return\n const dbPath = this.sessionAdapter.getDbPath(sessionId)\n const hash = this.resolveHash(sessionId)\n this.queue.cancel(hash)\n this.stateStore.enable(this.enableParams(dbPath, hash))\n this.queue.enqueue({ type: 'rebuild', dbPathHash: hash })\n }\n\n /** 为所有启用但未完成 / 需重建的对话入队(\"建立待处理索引\") */\n buildAllPending(): void {\n if (!this.canRun()) return\n const modelId = this.currentModelId()\n for (const state of this.stateStore.listEnabled()) {\n if (state.indexStatus === 'running') continue\n if (this.isStale(state, modelId)) {\n this.queue.enqueue({ type: 'rebuild', dbPathHash: state.dbPathHash })\n } else if (state.indexStatus !== 'completed') {\n this.queue.enqueue({ type: 'build', dbPathHash: state.dbPathHash })\n } else {\n // Completed sessions may have received new messages since last index run\n const sessionId = sessionIdFromDbPath(state.dbPath)\n const db = this.sessionAdapter.openReadonly(sessionId)\n if (db) {\n const liveCount = (db.prepare('SELECT COUNT(*) AS c FROM message').get() as { c: number } | undefined)?.c ?? 0\n if (liveCount > state.totalMessages) {\n this.queue.enqueue({ type: 'build', dbPathHash: state.dbPathHash })\n } else if (liveCount < state.totalMessages) {\n // Messages were deleted (e.g. member deletion); must rebuild to remove stale vectors\n this.queue.enqueue({ type: 'rebuild', dbPathHash: state.dbPathHash })\n }\n }\n }\n }\n }\n\n // ---------- 状态 ----------\n\n private toStatus(state: SemanticIndexSessionState, currentModelId: string): SemanticIndexSessionStatus {\n const needsRebuild = state.enabled && this.isStale(state, currentModelId)\n const coverage = state.totalMessages > 0 ? Math.min(1, state.indexedMessages / state.totalMessages) : 0\n return {\n sessionId: sessionIdFromDbPath(state.dbPath),\n enabled: state.enabled,\n indexStatus: state.indexStatus,\n needsRebuild,\n totalMessages: state.totalMessages,\n indexedMessages: state.indexedMessages,\n chunkCount: state.chunkCount,\n coverage,\n queued: this.queue.isQueued(state.dbPathHash),\n running: this.queue.isRunning(state.dbPathHash),\n partial: state.indexStatus !== 'completed',\n error: state.error,\n modelId: state.modelId,\n }\n }\n\n status(sessionId: string): SemanticIndexSessionStatus | null {\n const state = this.stateStore.getState(this.resolveHash(sessionId))\n if (!state) return null\n return this.toStatus(state, this.currentModelId())\n }\n\n /** 批量查询多个对话状态(未启用的返回 null 过滤) */\n statusForSessions(sessionIds: string[]): SemanticIndexSessionStatus[] {\n const modelId = this.currentModelId()\n const result: SemanticIndexSessionStatus[] = []\n for (const sessionId of sessionIds) {\n const state = this.stateStore.getState(this.resolveHash(sessionId))\n if (state) result.push(this.toStatus(state, modelId))\n }\n return result\n }\n\n listEnabledStatuses(): SemanticIndexSessionStatus[] {\n const modelId = this.currentModelId()\n return this.stateStore.listEnabled().map((s) => {\n const status = this.toStatus(s, modelId)\n if (s.indexStatus === 'completed' && !status.needsRebuild && !status.queued && !status.running) {\n const sessionId = sessionIdFromDbPath(s.dbPath)\n const db = this.sessionAdapter.openReadonly(sessionId)\n if (db) {\n const liveCount = (db.prepare('SELECT COUNT(*) AS c FROM message').get() as { c: number } | undefined)?.c ?? 0\n if (liveCount !== s.totalMessages) status.hasNewMessages = true\n }\n }\n return status\n })\n }\n\n // ---------- 检索 ----------\n\n async search(\n sessionId: string,\n query: string,\n options?: { finalTopK?: number; budget?: EvidenceBudget; timeRangeMs?: { startTs?: number; endTs?: number } }\n ): Promise<SemanticSearchResult> {\n if (!this.canRun())\n return { available: false, reason: 'disabled', blocks: [], coverage: 0, partial: false, hitCount: 0 }\n\n const hash = this.resolveHash(sessionId)\n const state = this.stateStore.getState(hash)\n const modelId = this.currentModelId()\n\n if (!state || !state.enabled)\n return { available: false, reason: 'disabled', blocks: [], coverage: 0, partial: false, hitCount: 0 }\n if (this.isStale(state, modelId))\n return { available: false, reason: 'needs-rebuild', blocks: [], coverage: 0, partial: true, hitCount: 0 }\n\n const dim = this.store.getDim(hash, modelId)\n if (!dim || state.chunkCount === 0)\n return {\n available: false,\n reason: 'empty',\n blocks: [],\n coverage: 0,\n partial: state.indexStatus !== 'completed',\n hitCount: 0,\n }\n\n const db = this.sessionAdapter.openReadonly(sessionId)\n if (!db) return { available: false, reason: 'not-found', blocks: [], coverage: 0, partial: false, hitCount: 0 }\n\n const embedder = this.getEmbedder()\n const finalTopK = options?.finalTopK ?? 5\n // 候选池随 finalTopK 放大:短 chunk 下保证高 max_results 时召回覆盖足够(下限保持默认 40)\n const candidateTopN = Math.max(40, finalTopK * 4)\n const hits = await semanticSearch(\n { embedder, store: this.store },\n {\n query,\n dbPathHash: hash,\n modelId,\n dim,\n finalTopK,\n denseTopN: candidateTopN,\n timeRangeMs: options?.timeRangeMs,\n }\n )\n\n const evidenceHits: EvidenceHit[] = hits.map((h) => ({\n chunkId: h.chunkId,\n score: h.score,\n parentId: h.record.parentId,\n startMessageId: h.record.startMessageId,\n endMessageId: h.record.endMessageId,\n startTs: h.record.startTs,\n endTs: h.record.endTs,\n }))\n const reader = createChatDbMessageRangeReader(db)\n const { blocks } = assembleEvidence(reader, evidenceHits, options?.budget)\n\n const coverage = state.totalMessages > 0 ? Math.min(1, state.indexedMessages / state.totalMessages) : 0\n return { available: true, blocks, coverage, partial: state.indexStatus !== 'completed', hitCount: hits.length }\n }\n\n /**\n * 当前会话是否可被 AI 工具检索。\n * 已启用 + 模型身份一致 + 有 chunk + 维度可用即可(running/paused/failed 只要已有 chunk 也可,结果标记 partial)。\n * 未启用 / 需重建 / 无 chunk 返回 false,runner 据此不向 LLM 暴露检索工具。\n */\n canSearch(sessionId: string): boolean {\n if (!this.canRun()) return false\n const hash = this.resolveHash(sessionId)\n const state = this.stateStore.getState(hash)\n if (!state || !state.enabled) return false\n const modelId = this.currentModelId()\n if (this.isStale(state, modelId)) return false\n if (state.chunkCount <= 0) return false\n return !!this.store.getDim(hash, modelId)\n }\n\n /**\n * 面向 AI 工具的检索:复用 search(),再用 applyPreprocessingPipeline 对证据做清洗/脱敏/匿名化/截断。\n * 工具层只拿到安全文本与安全 metadata,绝不接触原始消息。\n */\n async searchForTool(\n sessionId: string,\n query: string,\n options?: SemanticSearchToolOptions\n ): Promise<SemanticSearchToolResult> {\n const unavailable = (reason: string, partial = false): SemanticSearchToolResult => ({\n available: false,\n reason,\n text: '',\n returned: 0,\n hitCount: 0,\n partial,\n coverage: 0,\n truncated: false,\n sources: [],\n })\n\n const q = query?.trim()\n if (!q) return unavailable('empty-query')\n\n const configuredDefault = clampSearchMaxResults(this.configStore.get().searchMaxResults)\n const requested = options?.maxResults ?? configuredDefault\n const finalTopK = Math.max(1, Math.min(SEARCH_MAX_RESULTS_HARD_CAP, Math.floor(requested)))\n\n // 注入预算与 max_results 解耦:固定预算再以工具结果预算(maxResultTokens)封顶。\n // finalTopK 只放大召回候选数,不放大注入 token,避免大范围查询撑爆上下文。\n const evidenceTokens = resolveSearchToolEvidenceTokens(options?.maxResultTokens)\n\n let result: SemanticSearchResult\n try {\n result = await this.search(sessionId, q, {\n finalTopK,\n budget: { maxChunks: finalTopK, totalTokens: evidenceTokens },\n timeRangeMs: options?.timeFilter,\n })\n } catch (err) {\n return unavailable(err instanceof Error ? err.message : String(err))\n }\n if (!result.available) return unavailable(result.reason ?? 'unavailable', result.partial)\n\n // 归一化预处理配置:补齐数组字段,避免 partial 配置导致管道崩溃(脱敏规则仍以传入为准)\n const preprocessConfig = options?.preprocessConfig\n ? ({ blacklistKeywords: [], desensitizeRules: [], ...options.preprocessConfig } as unknown as PreprocessConfig)\n : undefined\n const anonymizeNames = !!preprocessConfig?.anonymizeNames\n const locale = options?.locale\n const ownerPlatformId = options?.ownerPlatformId\n\n const sources: SemanticSearchToolSource[] = []\n const blockTexts: string[] = []\n // 全局昵称映射(仅 anonymize 时使用),保证整段证据只出现一次 name map\n const nameMap = new Map<number, { name: string; platformId?: string }>()\n let earliest = Number.POSITIVE_INFINITY\n let latest = Number.NEGATIVE_INFINITY\n\n for (const block of result.blocks) {\n const rawMessages: PreprocessableMessage[] = block.messages.map((m) => ({\n id: m.id,\n senderId: m.senderId,\n senderName: m.senderName,\n senderPlatformId: m.senderPlatformId,\n content: m.content,\n // EvidenceMessage.ts 是毫秒;预处理管道按秒渲染时间(format.ts 内部 *1000),故此处回退为秒\n timestamp: Math.floor(m.ts / 1000),\n }))\n for (const m of block.messages) {\n if (m.senderId != null && !nameMap.has(m.senderId)) {\n nameMap.set(m.senderId, { name: m.senderName, platformId: m.senderPlatformId })\n }\n }\n // 每块统一过 applyPreprocessingPipeline 做清洗/脱敏/匿名化;用 details.messages(不含 name map 行)\n const { details } = applyPreprocessingPipeline({\n rawMessages,\n preprocessConfig,\n locale,\n anonymizeNames,\n ownerPlatformId,\n })\n const lines = Array.isArray(details.messages) ? (details.messages as string[]) : []\n const safeText = lines.join('\\n').trim()\n const first = block.messages[0]\n const last = block.messages[block.messages.length - 1]\n const startTime = first ? new Date(first.ts).toISOString() : undefined\n const endTime = last ? new Date(last.ts).toISOString() : undefined\n for (const m of block.messages) {\n if (m.ts < earliest) earliest = m.ts\n if (m.ts > latest) latest = m.ts\n }\n const snippet =\n safeText.length > SEARCH_TOOL_SNIPPET_MAX ? `${safeText.slice(0, SEARCH_TOOL_SNIPPET_MAX)}…` : safeText\n sources.push({\n startMessageId: block.startMessageId,\n endMessageId: block.endMessageId,\n score: block.score,\n chunkIds: block.chunkIds,\n snippet,\n text: safeText,\n startTime,\n endTime,\n })\n const rangeLabel = startTime && endTime ? `${startTime} ~ ${endTime}` : ''\n blockTexts.push(`--- ${rangeLabel}\\n${safeText}`)\n }\n\n let text = blockTexts.join('\\n\\n')\n if (anonymizeNames && nameMap.size > 0) {\n const entries = [...nameMap.entries()].map(\n ([id, { name, platformId }]) =>\n `U${id}=${name}${ownerPlatformId && platformId === ownerPlatformId ? '(owner)' : ''}`\n )\n text = `[Name Map] ${entries.join(' | ')}\\n\\n${text}`\n }\n\n const timeRange =\n Number.isFinite(earliest) && Number.isFinite(latest)\n ? { earliest: new Date(earliest).toISOString(), latest: new Date(latest).toISOString() }\n : undefined\n\n // 命中候选已填满 finalTopK 上限,说明可能还有更多相关历史,提示用更具体 query 继续\n const truncated = result.hitCount >= finalTopK\n\n return {\n available: true,\n text,\n returned: result.blocks.length,\n hitCount: result.hitCount,\n partial: result.partial,\n coverage: result.coverage,\n truncated,\n timeRange,\n sources,\n }\n }\n\n // ---------- 清理 ----------\n\n /** 清理已停用待清理的对话索引,以及无对应业务状态的孤儿 chunk */\n cleanupUnused(): { cleaned: number } {\n let cleaned = 0\n\n // 检测已删除的对话(DB 文件不再存在):将其状态标为 pending cleanup,交由下方统一清理\n const activeSessionIds = new Set(this.sessionAdapter.listSessionIds())\n for (const state of this.stateStore.listAll()) {\n if (state.cleanupStatus !== 'pending' && !activeSessionIds.has(sessionIdFromDbPath(state.dbPath))) {\n this.queue.cancel(state.dbPathHash)\n this.stateStore.disable(state.dbPathHash)\n }\n }\n\n for (const state of this.stateStore.listPendingCleanup()) {\n this.queue.cancel(state.dbPathHash)\n this.store.deleteByDbPathHash(state.dbPathHash)\n this.stateStore.resetProgress(state.dbPathHash)\n this.stateStore.setCleanupStatus(state.dbPathHash, 'done')\n cleaned++\n }\n const known = new Set(this.stateStore.listAll().map((s) => s.dbPathHash))\n for (const hash of this.store.listDbPathHashes()) {\n if (!known.has(hash)) {\n this.store.deleteByDbPathHash(hash)\n cleaned++\n }\n }\n return { cleaned }\n }\n\n // ---------- 启动恢复 ----------\n\n /** 启动时把中断的 running 标记为 paused,保留断点;不自动续跑 */\n recover(): void {\n for (const state of this.stateStore.listAll()) {\n if (state.indexStatus === 'running') {\n this.stateStore.setIndexStatus(state.dbPathHash, 'paused')\n }\n }\n const config = this.configStore.get()\n this.startLocalModelPreload(config, ++this.modelPreloadGeneration, { defer: true })\n }\n\n // ---------- 队列执行 ----------\n\n private resolveSource(db: DatabaseAdapter): ChunkSource {\n try {\n const row = db.prepare('SELECT name, type FROM meta LIMIT 1').get() as\n | { name?: string; type?: string }\n | undefined\n const kind: ChunkSource['kind'] = row?.type === 'group' ? 'group' : 'private'\n return { title: row?.name ?? '', kind }\n } catch {\n return { title: '', kind: 'private' }\n }\n }\n\n private async runJob(ctx: JobContext): Promise<void> {\n const { job, checkStop } = ctx\n if (job.type === 'cleanup') {\n this.cleanupUnused()\n return\n }\n\n const state = this.stateStore.getState(job.dbPathHash)\n if (!state || !state.enabled) return\n const sessionId = sessionIdFromDbPath(state.dbPath)\n\n const db = this.sessionAdapter.openReadonly(sessionId)\n if (!db) {\n this.stateStore.setIndexStatus(job.dbPathHash, 'failed', 'session database not found')\n return\n }\n\n if (job.type === 'rebuild') {\n this.store.deleteByDbPathHash(job.dbPathHash)\n this.stateStore.resetProgress(job.dbPathHash)\n // 以当前模型/chunker 身份重建:刷新状态身份,确保完成后 needsRebuild=false、可检索\n this.stateStore.setBuildIdentity(job.dbPathHash, {\n modelId: this.currentModelId(),\n chunkerVersion: this.chunkerVersion,\n chunkerConfigHash: this.chunkerConfigHash,\n })\n }\n\n const jobConfig = this.configStore.get()\n const jobModelId = this.currentModelId()\n const jobEmbedder = this.getEmbedder()\n const source = createChatDbMessageSource(db, this.resolveSource(db))\n const startedAt = Date.now()\n appLogger.info('semantic-index', 'warmup job started', { type: job.type, sessionId, dbPathHash: job.dbPathHash })\n const result = await runWarmup({\n dbPathHash: job.dbPathHash,\n modelId: jobModelId,\n embedder: jobEmbedder,\n store: this.store,\n stateStore: this.stateStore,\n source,\n checkStop,\n })\n // warmup 已成功写入 chunk,说明本地 extractor 通过重试加载成功;清除此前 preload 失败状态。\n const currentConfig = this.configStore.get()\n const jobDownloadSource =\n jobConfig.mode === 'local'\n ? (jobConfig.local.downloadSource ?? DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE)\n : null\n const currentDownloadSource =\n currentConfig.mode === 'local'\n ? (currentConfig.local.downloadSource ?? DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE)\n : null\n if (\n result.status === 'completed' &&\n result.chunksWritten > 0 &&\n jobConfig.mode === 'local' &&\n currentConfig.mode === 'local' &&\n currentConfig.local.modelId === jobConfig.local.modelId &&\n currentDownloadSource === jobDownloadSource\n ) {\n this.modelPreloadStatus = 'ready'\n }\n appLogger.info('semantic-index', 'warmup job finished', {\n type: job.type,\n sessionId,\n dbPathHash: job.dbPathHash,\n status: result.status,\n chunksWritten: result.chunksWritten,\n elapsedMs: Date.now() - startedAt,\n error: result.error,\n })\n }\n\n /** 等待队列清空(优雅停止 / 测试用) */\n whenIdle(): Promise<void> {\n return this.queue.whenIdle()\n }\n\n close(): void {\n this.store.close()\n this.stateStore.close()\n }\n}\n","/**\n * Chunker 版本、运行时参数与 hash 派生(纯函数)\n *\n * 设计依据 chunking-decision-final.md 第 7/8/9/11 节:\n * - chunker_version 是代码常量,算法/格式语义变化时 bump,触发全量重建。\n * - chunker_config_hash 由运行时参数生成,参数变化只影响当前索引。\n * - parent_id 必须包含 config hash,避免错误复用旧 parent 缓存。\n * - db_path_hash 用于在 embedding_index.db 中区分不同聊天库。\n */\n\nimport { createHash } from 'crypto'\n\n/**\n * Chunker 算法/格式语义版本。\n *\n * bump 条件(见文档第 11 节):\n * - header 模板结构变化。\n * - 切片算法语义变化。\n * - 语义真空过滤规则本身变化。\n */\nexport const CHUNKER_VERSION = 'v1.3'\n\n/** Phase 1 固定策略标识 */\nexport const STRATEGY_ID = 'balanced'\n\n/** 运行时 chunker 参数(进入 chunker_config_hash) */\nexport interface ChunkerConfig {\n /** parent 时间 gap 阈值(秒),兜底 1800 */\n parentGapSeconds: number\n /** parent token 硬上限 */\n parentMaxTokens: number\n /** child 目标最小有效字符:达到消息数软上限时仍需满足此下限才关闭,避免 header 主导的过短 chunk */\n childTargetMinChars: number\n /** child 目标最大有效字符 */\n childTargetMaxChars: number\n /** child 消息数软上限:消息数达到且有效字符达到 min 时关闭(应对高频短消息群聊) */\n childSoftMaxMessages: number\n /** child 消息数硬上限:消息数达到即强制关闭,即使有效字符不足 min */\n childHardMaxMessages: number\n /** child token 硬上限 */\n childHardMaxTokens: number\n /** overlap 保留末尾消息条数 */\n overlapMessages: number\n /** 过滤语义真空后有效字符低于该值的 chunk 跳过索引 */\n semanticVoidSkipThreshold: number\n}\n\n/**\n * Phase 1 默认 chunker 参数(见决策表)。\n * 短 chunk + 多候选策略:缩短 child 窗口提升语义定位精度,配合检索侧更高 max_results\n * 与 evidence/token 预算控制最终注入量。\n */\nexport const DEFAULT_CHUNKER_CONFIG: ChunkerConfig = {\n parentGapSeconds: 1800,\n parentMaxTokens: 2000,\n childTargetMinChars: 120,\n childTargetMaxChars: 360,\n childSoftMaxMessages: 12,\n childHardMaxMessages: 20,\n childHardMaxTokens: 1200,\n overlapMessages: 2,\n semanticVoidSkipThreshold: 20,\n}\n\nfunction sha256Hex(input: string): string {\n return createHash('sha256').update(input, 'utf-8').digest('hex')\n}\n\n/**\n * 运行时参数 hash。显式按固定字段顺序序列化,确保与传入对象的键顺序无关。\n */\nexport function computeChunkerConfigHash(config: ChunkerConfig): string {\n const canonical = JSON.stringify({\n parentGapSeconds: config.parentGapSeconds,\n parentMaxTokens: config.parentMaxTokens,\n childTargetMinChars: config.childTargetMinChars,\n childTargetMaxChars: config.childTargetMaxChars,\n childSoftMaxMessages: config.childSoftMaxMessages,\n childHardMaxMessages: config.childHardMaxMessages,\n childHardMaxTokens: config.childHardMaxTokens,\n overlapMessages: config.overlapMessages,\n semanticVoidSkipThreshold: config.semanticVoidSkipThreshold,\n })\n return sha256Hex(canonical)\n}\n\n/** 对话稳定标识(sessionId 或相关稳定字符串)的 SHA256 前缀,用于区分不同聊天库的 chunk */\nexport function computeDbPathHash(stableKey: string): string {\n return sha256Hex(stableKey).slice(0, 16)\n}\n\n/**\n * 组合全局唯一 chunk_id。chunker 输出模型无关的 localChunkId,写入存储时由\n * db_path_hash + model_id 组合成跨对话、跨模型唯一的 chunk_id。\n */\nexport function composeChunkId(dbPathHash: string, modelId: string, localChunkId: string): string {\n return `${dbPathHash}:${modelId}:${localChunkId}`\n}\n\n/**\n * 稳定派生 parent id。包含 config hash,参数变化后 parent id 也变化。\n * 格式:`parent:${start}:${end}:${gap}:${version}:${configHash}`\n */\nexport function deriveParentId(params: {\n startMessageId: number\n endMessageId: number\n gapSeconds: number\n chunkerVersion: string\n chunkerConfigHash: string\n}): string {\n return `parent:${params.startMessageId}:${params.endMessageId}:${params.gapSeconds}:${params.chunkerVersion}:${params.chunkerConfigHash}`\n}\n\n/**\n * 从 parent id 解析 parent 的消息 id 边界,用于证据扩展时限定\"不跨 parent\"。\n * 格式不匹配时返回 null。\n */\nexport function parseParentBounds(parentId: string): { startMessageId: number; endMessageId: number } | null {\n const segments = parentId.split(':')\n if (segments.length < 3 || segments[0] !== 'parent') return null\n const startMessageId = Number(segments[1])\n const endMessageId = Number(segments[2])\n if (!Number.isFinite(startMessageId) || !Number.isFinite(endMessageId)) return null\n return { startMessageId, endMessageId }\n}\n","/**\n * token 粗略估算(共享)\n *\n * 仅作硬上限护栏与证据预算控制,真实 token 由 embedder / LLM 决定。\n * 估算规则:CJK 每字约 1 token,其余字符约 1/4 token。\n */\nimport { Buffer } from 'node:buffer'\n\nconst UTF8_BYTES_PER_ESTIMATED_TOKEN = 4\n\nfunction isCjkCharacter(char: string): boolean {\n return /[\\u3400-\\u9fff\\uf900-\\ufaff]/u.test(char)\n}\n\nexport function estimateTokens(text: string): number {\n let cjk = 0\n let other = 0\n for (const ch of text) {\n if (isCjkCharacter(ch)) cjk++\n else other++\n }\n return cjk + Math.ceil(other / 4)\n}\n\n/**\n * 按共享估算规则与 UTF-8 字节上限截断文本,避免 emoji 等高膨胀字符绕过安全护栏。\n * 这里只处理送给 embedding provider 的派生文本,不修改原始消息和证据范围。\n */\nexport function clampEstimatedTokens(text: string, maxTokens: number): string {\n const limit = Math.max(0, Math.floor(maxTokens))\n if (limit === 0) return ''\n const byteLimit = limit * UTF8_BYTES_PER_ESTIMATED_TOKEN\n if (estimateTokens(text) <= limit && Buffer.byteLength(text, 'utf8') <= byteLimit) return text\n\n let cjk = 0\n let other = 0\n let bytes = 0\n let end = 0\n for (const char of text) {\n const isCjk = isCjkCharacter(char)\n const nextCjk = cjk + (isCjk ? 1 : 0)\n const nextOther = other + (isCjk ? 0 : 1)\n const nextBytes = bytes + Buffer.byteLength(char, 'utf8')\n if (nextCjk + Math.ceil(nextOther / 4) > limit || nextBytes > byteLimit) break\n cjk = nextCjk\n other = nextOther\n bytes = nextBytes\n end += char.length\n }\n return text.slice(0, end)\n}\n","/**\n * OpenAI-compatible embedding API provider\n *\n * Phase 1 只承诺 OpenAI-compatible /embeddings 调用路径,配置仅 baseUrl/apiKey/model,\n * 不暴露 dimensions(只记录 provider 实际返回维度)。API 模式 query 与 document 一致,\n * 不加本地模型的 queryInstruction。\n *\n * fetch 可注入,单元测试验证请求结构与响应解析,不联网。\n */\n\nimport { appLogger } from '../../logging/app-logger'\nimport { estimateTokens } from '../tokens'\nimport type { EmbeddingProvider } from './types'\n\n/** API embedding 默认一次提交 8 个 chunk,兼容更多 OpenAI-compatible 服务的批量上限。 */\nexport const API_DOCUMENT_BATCH_SIZE = 8\n\nexport interface OpenAICompatibleConfig {\n baseUrl: string\n apiKey: string\n model: string\n /** 单文本 token 上限提示,默认 8192 */\n maxTokens?: number\n}\n\ninterface EmbeddingResponse {\n data: Array<{ index: number; embedding: number[] }>\n}\n\nexport type FetchFn = (\n url: string,\n init: { method: string; headers: Record<string, string>; body: string }\n) => Promise<{\n ok: boolean\n status: number\n headers?: { get(name: string): string | null }\n text(): Promise<string>\n json(): Promise<unknown>\n}>\n\nconst defaultFetchFn: FetchFn = (url, init) => fetch(url, init) as unknown as ReturnType<FetchFn>\n\nfunction buildEmbeddingsUrl(baseUrl: string): string {\n const trimmed = baseUrl.replace(/\\/+$/, '')\n return trimmed.endsWith('/embeddings') ? trimmed : `${trimmed}/embeddings`\n}\n\nexport class OpenAICompatibleEmbeddingProvider implements EmbeddingProvider {\n readonly modelId: string\n readonly maxTokens: number\n readonly documentBatchSize = API_DOCUMENT_BATCH_SIZE\n\n private config: OpenAICompatibleConfig\n private fetchFn: FetchFn\n private url: string\n private knownDim = 0\n\n constructor(config: OpenAICompatibleConfig, options: { fetchFn?: FetchFn } = {}) {\n this.config = config\n this.fetchFn = options.fetchFn ?? defaultFetchFn\n this.modelId = config.model\n this.maxTokens = config.maxTokens ?? 8192\n this.url = buildEmbeddingsUrl(config.baseUrl)\n }\n\n /** 实际返回维度;首次成功调用后才确定,调用前为 0 */\n get dim(): number {\n return this.knownDim\n }\n\n async embedDocuments(texts: string[]): Promise<Float32Array[]> {\n if (texts.length === 0) return []\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n }\n if (this.config.apiKey.trim()) headers.Authorization = `Bearer ${this.config.apiKey}`\n\n const response = await this.fetchFn(this.url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ model: this.config.model, input: texts }),\n })\n\n if (!response.ok) {\n await response.text().catch(() => '') // consume body to avoid connection leak\n const estimatedTokens = texts.map(estimateTokens)\n const traceId = response.headers?.get('x-siliconcloud-trace-id')?.trim()\n const diagnostics = [\n `model=${this.config.model}`,\n `batchSize=${texts.length}`,\n `maxInputTokens=${Math.max(...estimatedTokens)}`,\n `totalInputTokens=${estimatedTokens.reduce((sum, count) => sum + count, 0)}`,\n `maxInputChars=${Math.max(...texts.map((text) => text.length))}`,\n ]\n if (traceId) diagnostics.push(`traceId=${traceId}`)\n\n const error = new Error(`Embedding API request failed: ${response.status} (${diagnostics.join(', ')})`)\n appLogger.error('semantic-index', 'embedding API request failed', error)\n throw error\n }\n\n const payload = (await response.json()) as EmbeddingResponse\n const sorted = [...payload.data].sort((a, b) => a.index - b.index)\n const vectors = sorted.map((item) => Float32Array.from(item.embedding))\n if (vectors.length > 0) this.knownDim = vectors[0].length\n return vectors\n }\n\n async embedQuery(text: string): Promise<Float32Array> {\n const [vector] = await this.embedDocuments([text])\n return vector\n }\n\n async preload(): Promise<void> {\n // API mode: no local model to download\n }\n}\n","/**\n * 本地 embedding 模型 profile(静态定义,单一事实来源)\n *\n * 取值依据 chunking-decision-final.md 第 6.2/6.3 节与 P0-2/P0-3 验证结论。\n * 这些 profile 决定 chunk 的 dim/pooling/normalize,变化会触发索引重建。\n */\n\nimport type { EmbeddingPooling } from './types'\n\nexport interface LocalEmbeddingProfile {\n /** 用户展示名 */\n displayName: string\n /** transformers.js 下载源 modelId */\n modelId: string\n architecture: 'bert' | 'qwen3'\n dim: number\n /** 单文本 token 上限(ChatLab profile cap,非模型真实上限) */\n maxTokens: number\n /** 单文本字符上限(可选护栏) */\n maxTextChars?: number\n pooling: EmbeddingPooling\n normalize: boolean\n /** transformers.js dtype;BGE 用 fp32,Qwen3 用 q8 */\n dtype?: 'fp32' | 'q8'\n /** 固定 batch 上限;Qwen3 必须为 1(P0-3:batch 污染 last_token) */\n maxBatchSize?: number\n /** query 前缀指令;document 不加 */\n queryInstruction: string\n /** 近似下载体积(MB),用于 UI 提示 */\n approxDownloadMB: number\n}\n\n/** Qwen3-Embedding-0.6B:通用本地推荐模型,必须 batch=1(P0-3) */\nexport const QWEN3_PROFILE: LocalEmbeddingProfile = {\n displayName: 'Qwen3 Embedding 0.6B',\n modelId: 'onnx-community/Qwen3-Embedding-0.6B-ONNX',\n architecture: 'qwen3',\n dim: 1024,\n maxTokens: 8192,\n maxTextChars: 2400,\n pooling: 'last_token',\n normalize: true,\n dtype: 'q8',\n maxBatchSize: 1,\n queryInstruction: 'Given a chat history search query, retrieve relevant conversation messages that answer the query',\n approxDownloadMB: 593,\n}\n\n/** BGE base zh v1.5:中文轻量入口,比 small 强、比 Qwen3 轻(cls pooling,512 token 上限) */\nexport const BGE_BASE_PROFILE: LocalEmbeddingProfile = {\n displayName: 'BGE base zh',\n modelId: 'Xenova/bge-base-zh-v1.5',\n architecture: 'bert',\n dim: 768,\n maxTokens: 512,\n pooling: 'cls',\n normalize: true,\n dtype: 'fp32',\n queryInstruction: '为这个句子生成表示以用于检索相关文章:',\n approxDownloadMB: 390,\n}\n\nconst ALL_LOCAL_PROFILES: LocalEmbeddingProfile[] = [QWEN3_PROFILE, BGE_BASE_PROFILE]\n\n/**\n * 按 UI 语言返回可选本地模型列表。\n * Qwen3 为各语言通用推荐;BGE base zh 仅中文 UI 提供(中文轻量替代)。\n */\nexport function getLocalProfilesForLocale(locale: string): LocalEmbeddingProfile[] {\n if (locale.startsWith('zh')) return [QWEN3_PROFILE, BGE_BASE_PROFILE]\n return [QWEN3_PROFILE]\n}\n\nexport function getLocalProfileByModelId(modelId: string): LocalEmbeddingProfile | null {\n return ALL_LOCAL_PROFILES.find((p) => p.modelId === modelId) ?? null\n}\n","/**\n * Embedder 工厂\n *\n * 从语义索引配置构造 EmbeddingProvider:\n * - local:按 modelId 取静态 profile,注入本地模型缓存目录。\n * - api:OpenAI-compatible,API Key 从 auth-profiles.json 按 authProfile 引用解析。\n *\n * resolveApiKey / pipelineFactory / fetchFn 均可注入,便于单测不联网、不下载模型。\n */\n\nimport { resolveApiKey as defaultResolveApiKey } from '@openchatlab/config'\nimport { OpenAICompatibleEmbeddingProvider, type FetchFn } from './embedding/api'\nimport { LocalEmbeddingProvider, type LocalPipelineFactory } from './embedding/local'\nimport { getLocalProfileByModelId } from './embedding/profiles'\nimport type { EmbeddingProvider } from './embedding/types'\nimport type { SemanticIndexConfig } from './config'\n\nexport interface EmbedderFactoryDeps {\n /** 本地模型目录,例如 ~/.chatlab/ai/models/semantic-index */\n modelsCacheDir?: string\n /** Optional HTTP(S) proxy URL used only for downloading local embedding model files. */\n modelDownloadProxyUrl?: string\n /** auth-profiles 解析,默认走 @openchatlab/config */\n resolveApiKey?: (provider: string, authProfile?: string) => string\n /** 本地 pipeline 工厂注入(测试用,不下载模型) */\n localPipelineFactory?: LocalPipelineFactory\n /** API fetch 注入(测试用,不联网) */\n fetchFn?: FetchFn\n}\n\nexport function createEmbedder(config: SemanticIndexConfig, deps: EmbedderFactoryDeps = {}): EmbeddingProvider {\n if (config.mode === 'api') {\n if (!config.api || !config.api.baseUrl || !config.api.model) {\n throw new Error('semantic index API config incomplete: baseUrl and model are required')\n }\n const resolve = deps.resolveApiKey ?? defaultResolveApiKey\n const apiKey = resolve('', config.api.authProfile)\n return new OpenAICompatibleEmbeddingProvider(\n { baseUrl: config.api.baseUrl, apiKey, model: config.api.model },\n { fetchFn: deps.fetchFn }\n )\n }\n\n const profile = getLocalProfileByModelId(config.local.modelId)\n if (!profile) {\n throw new Error(`unknown local embedding model: ${config.local.modelId}`)\n }\n return new LocalEmbeddingProvider(profile, {\n cacheDir: deps.modelsCacheDir,\n modelDownloadProxyUrl: deps.modelDownloadProxyUrl,\n modelDownloadSource: config.local.downloadSource,\n pipelineFactory: deps.localPipelineFactory,\n })\n}\n","/**\n * 语义索引向量存储(sqlite-vec vec0 + better-sqlite3)\n *\n * embedding_index.db 是独立系统级向量库。本存储负责:\n * - 初始化元数据表与按维度的 vec0 表。\n * - 写入 chunk 元数据与向量(同一事务,rowid 关联)。\n * - dense ANN 查询(限定 db_path_hash + model_id 分区)。\n *\n * P0-1 硬约定:vec0 的 INTEGER 列与 k 参数绑定必须使用 CAST(? AS INTEGER)。\n */\n\nimport Database from 'better-sqlite3'\nimport * as sqliteVec from 'sqlite-vec'\nimport type { ChunkInsert, ChunkRecord, ChunkStatus, DenseQueryParams, DenseQueryResult } from './types'\nimport { EMBEDDING_INDEX_SCHEMA, vecTableName, vecTableSchema } from './schema'\n\n/** 加载 sqlite-vec 扩展。Electron 打包后可注入自定义实现处理 asar 解包路径。 */\nexport type LoadSqliteVec = (db: Database.Database) => void\n\nconst defaultLoadSqliteVec: LoadSqliteVec = (db) => sqliteVec.load(db)\n\ninterface ChunkRow {\n chunk_id: string\n db_path_hash: string\n strategy_id: string\n model_id: string\n dim: number\n parent_id: string\n start_message_id: number\n end_message_id: number\n start_ts: number\n end_ts: number\n message_count: number\n raw_content_hash: string\n embedding_input_hash: string\n chunker_version: string\n chunker_config_hash: string\n indexed_at: number\n status: string\n}\n\nfunction rowToRecord(row: ChunkRow): ChunkRecord {\n return {\n chunkId: row.chunk_id,\n dbPathHash: row.db_path_hash,\n strategyId: row.strategy_id,\n modelId: row.model_id,\n dim: row.dim,\n parentId: row.parent_id,\n startMessageId: row.start_message_id,\n endMessageId: row.end_message_id,\n startTs: row.start_ts,\n endTs: row.end_ts,\n messageCount: row.message_count,\n rawContentHash: row.raw_content_hash,\n embeddingInputHash: row.embedding_input_hash,\n chunkerVersion: row.chunker_version,\n chunkerConfigHash: row.chunker_config_hash,\n indexedAt: row.indexed_at,\n status: row.status as ChunkStatus,\n }\n}\n\nfunction toFloat32Buffer(embedding: Float32Array | number[]): Buffer {\n const arr = embedding instanceof Float32Array ? embedding : new Float32Array(embedding)\n return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength)\n}\n\nconst CHUNK_COLUMN_LIST = [\n 'chunk_id',\n 'db_path_hash',\n 'strategy_id',\n 'model_id',\n 'dim',\n 'parent_id',\n 'start_message_id',\n 'end_message_id',\n 'start_ts',\n 'end_ts',\n 'message_count',\n 'raw_content_hash',\n 'embedding_input_hash',\n 'chunker_version',\n 'chunker_config_hash',\n 'indexed_at',\n 'status',\n] as const\n\nconst CHUNK_COLUMNS = CHUNK_COLUMN_LIST.join(', ')\nconst CHUNK_COLUMNS_PREFIXED = CHUNK_COLUMN_LIST.map((c) => `m.${c}`).join(', ')\n\nexport class EmbeddingIndexStore {\n private db: Database.Database\n private ensuredVecDims = new Set<number>()\n\n constructor(dbPath: string, options?: { loadSqliteVec?: LoadSqliteVec; readonly?: boolean; nativeBinding?: string }) {\n this.db = new Database(dbPath, { readonly: options?.readonly ?? false, nativeBinding: options?.nativeBinding })\n ;(options?.loadSqliteVec ?? defaultLoadSqliteVec)(this.db)\n if (!options?.readonly) {\n this.db.pragma('journal_mode = WAL')\n this.db.exec(EMBEDDING_INDEX_SCHEMA)\n }\n }\n\n /** 确保对应维度的 vec0 表已创建(懒加载) */\n private ensureVecTable(dim: number): void {\n if (this.ensuredVecDims.has(dim)) return\n this.db.exec(vecTableSchema(dim))\n this.ensuredVecDims.add(dim)\n }\n\n private insertOne(item: ChunkInsert): void {\n const { record, embedding } = item\n if (embedding.length !== record.dim) {\n throw new Error(`embedding length ${embedding.length} mismatches dim ${record.dim} for chunk ${record.chunkId}`)\n }\n this.ensureVecTable(record.dim)\n\n const meta = this.db\n .prepare(\n // ponytail: OR IGNORE makes resume after mid-batch crash idempotent; vec insert skipped below if chunk exists\n `INSERT OR IGNORE INTO chunk_vector_index (${CHUNK_COLUMNS})\n VALUES (@chunkId, @dbPathHash, @strategyId, @modelId, @dim, @parentId, @startMessageId, @endMessageId,\n @startTs, @endTs, @messageCount, @rawContentHash, @embeddingInputHash, @chunkerVersion,\n @chunkerConfigHash, @indexedAt, @status)`\n )\n .run({\n chunkId: record.chunkId,\n dbPathHash: record.dbPathHash,\n strategyId: record.strategyId,\n modelId: record.modelId,\n dim: record.dim,\n parentId: record.parentId,\n startMessageId: record.startMessageId,\n endMessageId: record.endMessageId,\n startTs: record.startTs,\n endTs: record.endTs,\n messageCount: record.messageCount,\n rawContentHash: record.rawContentHash,\n embeddingInputHash: record.embeddingInputHash,\n chunkerVersion: record.chunkerVersion,\n chunkerConfigHash: record.chunkerConfigHash,\n indexedAt: record.indexedAt,\n status: record.status,\n })\n\n if (meta.changes === 0) return\n this.db\n .prepare(\n `INSERT INTO ${vecTableName(record.dim)} (vector_id, db_path_hash, model_id, embedding)\n VALUES (CAST(? AS INTEGER), ?, ?, ?)`\n )\n .run(Number(meta.lastInsertRowid), record.dbPathHash, record.modelId, toFloat32Buffer(embedding))\n }\n\n /** 写入单个 chunk(元数据 + 向量,同一事务) */\n insertChunk(record: ChunkRecord, embedding: Float32Array | number[]): void {\n this.db.transaction(() => this.insertOne({ record, embedding }))()\n }\n\n /** 批量写入 chunk(单事务) */\n insertChunks(items: ChunkInsert[]): void {\n this.db.transaction(() => {\n for (const item of items) this.insertOne(item)\n })()\n }\n\n /** dense ANN 查询,限定 db_path_hash + model_id 分区,cosine 距离升序 */\n queryDense(params: DenseQueryParams): DenseQueryResult[] {\n const table = vecTableName(params.dim)\n if (!this.tableExists(table)) return []\n\n const rows = this.db\n .prepare(\n `SELECT v.distance AS distance, ${CHUNK_COLUMNS_PREFIXED}\n FROM ${table} v\n JOIN chunk_vector_index m ON m.rowid = v.vector_id\n WHERE v.embedding MATCH ? AND v.db_path_hash = ? AND v.model_id = ? AND v.k = CAST(? AS INTEGER)\n ORDER BY v.distance`\n )\n .all(toFloat32Buffer(params.embedding), params.dbPathHash, params.modelId, params.k) as Array<\n ChunkRow & { distance: number }\n >\n\n return rows.map((row) => ({ chunkId: row.chunk_id, distance: row.distance, record: rowToRecord(row) }))\n }\n\n /** 删除某聊天库(全模型)的所有 chunk 元数据与向量,返回删除的元数据行数 */\n deleteByDbPathHash(dbPathHash: string): number {\n return this.db.transaction(() => {\n const rows = this.db\n .prepare('SELECT rowid AS rowid, dim FROM chunk_vector_index WHERE db_path_hash = ?')\n .all(dbPathHash) as Array<{ rowid: number; dim: number }>\n\n const delStmts = new Map<number, Database.Statement>()\n for (const { rowid, dim } of rows) {\n const table = vecTableName(dim)\n if (!this.tableExists(table)) continue\n let stmt = delStmts.get(dim)\n if (!stmt) {\n stmt = this.db.prepare(`DELETE FROM ${table} WHERE vector_id = CAST(? AS INTEGER)`)\n delStmts.set(dim, stmt)\n }\n stmt.run(rowid)\n }\n\n const res = this.db.prepare('DELETE FROM chunk_vector_index WHERE db_path_hash = ?').run(dbPathHash)\n return res.changes\n })()\n }\n\n /** 删除某聊天库 + 模型中从指定聊天顺序位置开始的 chunks,返回删除的元数据行数 */\n deleteByModelFromPosition(params: {\n dbPathHash: string\n modelId: string\n startTs: number\n startMessageId: number\n }): number {\n return this.db.transaction(() => {\n const rows = this.db\n .prepare(\n `SELECT rowid AS rowid, dim FROM chunk_vector_index\n WHERE db_path_hash = ? AND model_id = ?\n AND (start_ts > CAST(? AS INTEGER)\n OR (start_ts = CAST(? AS INTEGER) AND start_message_id >= CAST(? AS INTEGER)))`\n )\n .all(params.dbPathHash, params.modelId, params.startTs, params.startTs, params.startMessageId) as Array<{\n rowid: number\n dim: number\n }>\n\n const delStmts = new Map<number, Database.Statement>()\n for (const { rowid, dim } of rows) {\n const table = vecTableName(dim)\n if (!this.tableExists(table)) continue\n let stmt = delStmts.get(dim)\n if (!stmt) {\n stmt = this.db.prepare(`DELETE FROM ${table} WHERE vector_id = CAST(? AS INTEGER)`)\n delStmts.set(dim, stmt)\n }\n stmt.run(rowid)\n }\n\n const res = this.db\n .prepare(\n `DELETE FROM chunk_vector_index\n WHERE db_path_hash = ? AND model_id = ?\n AND (start_ts > CAST(? AS INTEGER)\n OR (start_ts = CAST(? AS INTEGER) AND start_message_id >= CAST(? AS INTEGER)))`\n )\n .run(params.dbPathHash, params.modelId, params.startTs, params.startTs, params.startMessageId)\n return res.changes\n })()\n }\n\n /** 列出存储中存在 chunk 的所有 db_path_hash */\n listDbPathHashes(): string[] {\n const rows = this.db.prepare('SELECT DISTINCT db_path_hash FROM chunk_vector_index').all() as Array<{\n db_path_hash: string\n }>\n return rows.map((r) => r.db_path_hash)\n }\n\n /** 统计某聊天库 chunk 数(可选限定 model_id) */\n countChunks(dbPathHash: string, modelId?: string): number {\n const row = modelId\n ? (this.db\n .prepare('SELECT COUNT(*) AS c FROM chunk_vector_index WHERE db_path_hash = ? AND model_id = ?')\n .get(dbPathHash, modelId) as { c: number })\n : (this.db.prepare('SELECT COUNT(*) AS c FROM chunk_vector_index WHERE db_path_hash = ?').get(dbPathHash) as {\n c: number\n })\n return row.c\n }\n\n /** 取某聊天库 + 模型已写入 chunk 的维度(用于检索选表);无 chunk 返回 null */\n getDim(dbPathHash: string, modelId: string): number | null {\n const row = this.db\n .prepare('SELECT dim FROM chunk_vector_index WHERE db_path_hash = ? AND model_id = ? LIMIT 1')\n .get(dbPathHash, modelId) as { dim: number } | undefined\n return row ? row.dim : null\n }\n\n /** 按 chunk_id 取元数据 */\n getChunkById(chunkId: string): ChunkRecord | null {\n const row = this.db.prepare(`SELECT ${CHUNK_COLUMNS} FROM chunk_vector_index WHERE chunk_id = ?`).get(chunkId) as\n | ChunkRow\n | undefined\n return row ? rowToRecord(row) : null\n }\n\n private tableExists(name: string): boolean {\n return !!this.db.prepare(`SELECT 1 FROM sqlite_master WHERE name = ?`).get(name)\n }\n\n close(): void {\n this.db.close()\n }\n}\n","/**\n * embedding_index.db schema 定义(单一事实来源)\n *\n * 结构:\n * - chunk_vector_index:dim 无关的元数据普通表,rowid 关联 vec0 向量。\n * - chunk_vec_{dim}:每个 embedding 维度一张 vec0 虚拟表(vec0 要求列内维度固定)。\n * PARTITION KEY 同时包含 db_path_hash 和 model_id,保证查询命中分区裁剪、隔离\n * 模型切换/重建期的新旧向量。详见 chunking-decision-final.md 第 11.1/17 节。\n */\n\n/** 元数据表(dim 无关),rowid 即对应 vec0 的 vector_id */\nexport const CHUNK_VECTOR_INDEX_TABLE = `\n CREATE TABLE IF NOT EXISTS chunk_vector_index (\n rowid INTEGER PRIMARY KEY,\n chunk_id TEXT NOT NULL UNIQUE,\n db_path_hash TEXT NOT NULL,\n strategy_id TEXT NOT NULL,\n model_id TEXT NOT NULL,\n dim INTEGER NOT NULL,\n parent_id TEXT NOT NULL,\n start_message_id INTEGER NOT NULL,\n end_message_id INTEGER NOT NULL,\n start_ts INTEGER NOT NULL,\n end_ts INTEGER NOT NULL,\n message_count INTEGER NOT NULL,\n raw_content_hash TEXT NOT NULL,\n embedding_input_hash TEXT NOT NULL,\n chunker_version TEXT NOT NULL,\n chunker_config_hash TEXT NOT NULL,\n indexed_at INTEGER NOT NULL,\n status TEXT NOT NULL\n );\n`\n\n/**\n * 范围映射索引:\n * - idx_chunk_range 保留 message_id 领先的旧查询路径和既有数据库兼容性。\n * - idx_chunk_ts_range 支撑当前 `start_ts, start_message_id` 组合查找,避免按会话扫描排序。\n */\nexport const CHUNK_VECTOR_INDEX_INDEXES = `\n CREATE INDEX IF NOT EXISTS idx_chunk_range\n ON chunk_vector_index(db_path_hash, model_id, strategy_id, start_message_id, end_message_id);\n CREATE INDEX IF NOT EXISTS idx_chunk_ts_range\n ON chunk_vector_index(db_path_hash, model_id, strategy_id, start_ts, start_message_id);\n`\n\nexport const EMBEDDING_INDEX_SCHEMA = CHUNK_VECTOR_INDEX_TABLE + CHUNK_VECTOR_INDEX_INDEXES\n\n/**\n * 语义索引业务状态表(与向量表同库)。\n *\n * 保存\"对话是否启用索引\"、索引进度/状态与清理状态。这些状态是权威来源,\n * 不能从后台任务队列反推(见 chunking-decision-final.md 第 11.1/15 节)。\n * 以 db_path_hash 作为对话主键(一个聊天库 = 一个对话)。\n */\nexport const SEMANTIC_INDEX_SESSION_TABLE = `\n CREATE TABLE IF NOT EXISTS semantic_index_session (\n db_path_hash TEXT PRIMARY KEY,\n db_path TEXT NOT NULL,\n enabled INTEGER NOT NULL DEFAULT 0,\n model_id TEXT,\n index_status TEXT NOT NULL DEFAULT 'idle',\n cleanup_status TEXT NOT NULL DEFAULT 'none',\n total_messages INTEGER NOT NULL DEFAULT 0,\n indexed_messages INTEGER NOT NULL DEFAULT 0,\n last_indexed_message_id INTEGER,\n chunk_count INTEGER NOT NULL DEFAULT 0,\n chunker_version TEXT,\n chunker_config_hash TEXT,\n error TEXT,\n enabled_at INTEGER,\n updated_at INTEGER NOT NULL\n );\n`\n\n/** 给定维度的 vec0 表名 */\nexport function vecTableName(dim: number): string {\n return `chunk_vec_${dim}`\n}\n\n/** 给定维度的 vec0 虚拟表 DDL */\nexport function vecTableSchema(dim: number): string {\n return `\n CREATE VIRTUAL TABLE IF NOT EXISTS ${vecTableName(dim)} USING vec0(\n vector_id INTEGER PRIMARY KEY,\n db_path_hash TEXT PARTITION KEY,\n model_id TEXT PARTITION KEY,\n embedding FLOAT[${dim}] distance_metric=cosine\n );\n `\n}\n","/**\n * 语义索引业务状态存储(与向量库同一 embedding_index.db 文件)\n *\n * 保存对话级权威状态:是否启用、索引进度/状态、清理状态。后台任务队列只负责执行,\n * 启用与结果状态不从队列反推(见 chunking-decision-final.md 第 11.1/15 节)。\n * 以 db_path_hash 作为对话主键(一个聊天库 = 一个对话)。\n */\n\nimport Database from 'better-sqlite3'\nimport { SEMANTIC_INDEX_SESSION_TABLE } from './schema'\n\n/** 索引生命周期状态 */\nexport type SemanticIndexStatus = 'idle' | 'running' | 'paused' | 'completed' | 'failed' | 'cancelled'\n\n/** 清理生命周期状态 */\nexport type SemanticIndexCleanupStatus = 'none' | 'pending' | 'running' | 'done'\n\nexport interface SemanticIndexSessionState {\n dbPathHash: string\n dbPath: string\n enabled: boolean\n modelId: string | null\n indexStatus: SemanticIndexStatus\n cleanupStatus: SemanticIndexCleanupStatus\n totalMessages: number\n indexedMessages: number\n lastIndexedMessageId: number | null\n chunkCount: number\n /** 建立索引时使用的 chunker 算法版本(与当前不一致 => 需重建) */\n chunkerVersion: string | null\n /** 建立索引时使用的 chunker 参数 hash(与当前不一致 => 需重建) */\n chunkerConfigHash: string | null\n error: string | null\n enabledAt: number | null\n updatedAt: number\n}\n\nexport interface EnableParams {\n dbPathHash: string\n dbPath: string\n modelId: string\n /** 当前 chunker 算法版本,随索引一起记录用于重建判断 */\n chunkerVersion: string\n /** 当前 chunker 参数 hash,随索引一起记录用于重建判断 */\n chunkerConfigHash: string\n}\n\nexport interface ProgressPatch {\n indexStatus?: SemanticIndexStatus\n totalMessages?: number\n indexedMessages?: number\n lastIndexedMessageId?: number\n chunkCount?: number\n error?: string | null\n}\n\ninterface SessionRow {\n db_path_hash: string\n db_path: string\n enabled: number\n model_id: string | null\n index_status: string\n cleanup_status: string\n total_messages: number\n indexed_messages: number\n last_indexed_message_id: number | null\n chunk_count: number\n chunker_version: string | null\n chunker_config_hash: string | null\n error: string | null\n enabled_at: number | null\n updated_at: number\n}\n\nfunction rowToState(row: SessionRow): SemanticIndexSessionState {\n return {\n dbPathHash: row.db_path_hash,\n dbPath: row.db_path,\n enabled: row.enabled === 1,\n modelId: row.model_id,\n indexStatus: row.index_status as SemanticIndexStatus,\n cleanupStatus: row.cleanup_status as SemanticIndexCleanupStatus,\n totalMessages: row.total_messages,\n indexedMessages: row.indexed_messages,\n lastIndexedMessageId: row.last_indexed_message_id,\n chunkCount: row.chunk_count,\n chunkerVersion: row.chunker_version ?? null,\n chunkerConfigHash: row.chunker_config_hash ?? null,\n error: row.error,\n enabledAt: row.enabled_at,\n updatedAt: row.updated_at,\n }\n}\n\nexport class SemanticIndexStateStore {\n private db: Database.Database\n\n constructor(dbPath: string, options?: { nativeBinding?: string }) {\n this.db = new Database(dbPath, { nativeBinding: options?.nativeBinding })\n this.db.pragma('journal_mode = WAL')\n this.db.exec(SEMANTIC_INDEX_SESSION_TABLE)\n this.ensureChunkerColumns()\n }\n\n /** 兼容旧版本表结构:缺少 chunker 身份列时补齐(identity 为空 => 后续判定需重建) */\n private ensureChunkerColumns(): void {\n const columns = this.db.prepare(`PRAGMA table_info(semantic_index_session)`).all() as { name: string }[]\n const names = new Set(columns.map((c) => c.name))\n if (!names.has('chunker_version')) {\n this.db.exec(`ALTER TABLE semantic_index_session ADD COLUMN chunker_version TEXT`)\n }\n if (!names.has('chunker_config_hash')) {\n this.db.exec(`ALTER TABLE semantic_index_session ADD COLUMN chunker_config_hash TEXT`)\n }\n }\n\n /** 启用对话索引;已存在则更新模型并清除待清理标记(覆盖重新启用/换模型场景) */\n enable(params: EnableParams): void {\n const now = Date.now()\n this.db\n .prepare(\n `INSERT INTO semantic_index_session\n (db_path_hash, db_path, enabled, model_id, index_status, cleanup_status,\n chunker_version, chunker_config_hash, enabled_at, updated_at)\n VALUES (@dbPathHash, @dbPath, 1, @modelId, 'idle', 'none',\n @chunkerVersion, @chunkerConfigHash, @now, @now)\n ON CONFLICT(db_path_hash) DO UPDATE SET\n db_path = excluded.db_path,\n enabled = 1,\n model_id = excluded.model_id,\n cleanup_status = 'none',\n chunker_version = excluded.chunker_version,\n chunker_config_hash = excluded.chunker_config_hash,\n enabled_at = COALESCE(semantic_index_session.enabled_at, excluded.enabled_at),\n updated_at = excluded.updated_at`\n )\n .run({\n dbPathHash: params.dbPathHash,\n dbPath: params.dbPath,\n modelId: params.modelId,\n chunkerVersion: params.chunkerVersion,\n chunkerConfigHash: params.chunkerConfigHash,\n now,\n })\n }\n\n /**\n * 重新激活已有状态行(stale 重建场景):仅更新 enabled 和 dbPath,保留旧身份字段,\n * 使 buildAllPending() 重启后仍能通过 isStale() 检测到需重建并入队 rebuild。\n */\n reactivate(dbPathHash: string, dbPath: string): void {\n this.db\n .prepare(\n `UPDATE semantic_index_session SET enabled = 1, db_path = ?, cleanup_status = 'none', updated_at = ? WHERE db_path_hash = ?`\n )\n .run(dbPath, Date.now(), dbPathHash)\n }\n\n /** 停用对话索引;标记待清理,保留状态与已建索引直到清理任务执行 */\n disable(dbPathHash: string): void {\n this.db\n .prepare(\n `UPDATE semantic_index_session\n SET enabled = 0, cleanup_status = 'pending', updated_at = ?\n WHERE db_path_hash = ?`\n )\n .run(Date.now(), dbPathHash)\n }\n\n updateProgress(dbPathHash: string, patch: ProgressPatch): void {\n const sets: string[] = []\n const values: Record<string, unknown> = { dbPathHash, now: Date.now() }\n const map: Record<keyof ProgressPatch, string> = {\n indexStatus: 'index_status',\n totalMessages: 'total_messages',\n indexedMessages: 'indexed_messages',\n lastIndexedMessageId: 'last_indexed_message_id',\n chunkCount: 'chunk_count',\n error: 'error',\n }\n for (const key of Object.keys(map) as (keyof ProgressPatch)[]) {\n if (patch[key] !== undefined) {\n sets.push(`${map[key]} = @${key}`)\n values[key] = patch[key]\n }\n }\n if (sets.length === 0) return\n this.db\n .prepare(\n `UPDATE semantic_index_session SET ${sets.join(', ')}, updated_at = @now WHERE db_path_hash = @dbPathHash`\n )\n .run(values)\n }\n\n setIndexStatus(dbPathHash: string, status: SemanticIndexStatus, error?: string | null): void {\n this.db\n .prepare(`UPDATE semantic_index_session SET index_status = ?, error = ?, updated_at = ? WHERE db_path_hash = ?`)\n .run(status, error ?? null, Date.now(), dbPathHash)\n }\n\n setCleanupStatus(dbPathHash: string, status: SemanticIndexCleanupStatus): void {\n this.db\n .prepare(`UPDATE semantic_index_session SET cleanup_status = ?, updated_at = ? WHERE db_path_hash = ?`)\n .run(status, Date.now(), dbPathHash)\n }\n\n /**\n * 刷新索引身份(模型 + chunker version/config hash)。\n * rebuild 以当前配置重写向量后调用,确保完成后不再被判为需重建(stale -> rebuild 闭环)。\n */\n setBuildIdentity(\n dbPathHash: string,\n identity: { modelId: string; chunkerVersion: string; chunkerConfigHash: string }\n ): void {\n this.db\n .prepare(\n `UPDATE semantic_index_session\n SET model_id = ?, chunker_version = ?, chunker_config_hash = ?, updated_at = ?\n WHERE db_path_hash = ?`\n )\n .run(identity.modelId, identity.chunkerVersion, identity.chunkerConfigHash, Date.now(), dbPathHash)\n }\n\n /** 重置进度与状态到初始(rebuild 前调用,清空断点游标,避免续跑跳过) */\n resetProgress(dbPathHash: string): void {\n this.db\n .prepare(\n `UPDATE semantic_index_session\n SET index_status = 'idle', indexed_messages = 0, chunk_count = 0,\n last_indexed_message_id = NULL, error = NULL, updated_at = ?\n WHERE db_path_hash = ?`\n )\n .run(Date.now(), dbPathHash)\n }\n\n getState(dbPathHash: string): SemanticIndexSessionState | null {\n const row = this.db.prepare(`SELECT * FROM semantic_index_session WHERE db_path_hash = ?`).get(dbPathHash) as\n | SessionRow\n | undefined\n return row ? rowToState(row) : null\n }\n\n listEnabled(): SemanticIndexSessionState[] {\n const rows = this.db\n .prepare(`SELECT * FROM semantic_index_session WHERE enabled = 1 ORDER BY enabled_at`)\n .all() as SessionRow[]\n return rows.map(rowToState)\n }\n\n listAll(): SemanticIndexSessionState[] {\n const rows = this.db.prepare(`SELECT * FROM semantic_index_session ORDER BY enabled_at`).all() as SessionRow[]\n return rows.map(rowToState)\n }\n\n listPendingCleanup(): SemanticIndexSessionState[] {\n const rows = this.db\n .prepare(`SELECT * FROM semantic_index_session WHERE cleanup_status = 'pending' ORDER BY updated_at`)\n .all() as SessionRow[]\n return rows.map(rowToState)\n }\n\n remove(dbPathHash: string): number {\n return this.db.prepare(`DELETE FROM semantic_index_session WHERE db_path_hash = ?`).run(dbPathHash).changes\n }\n\n close(): void {\n this.db.close()\n }\n}\n","/**\n * 语义索引串行 job 队列(Phase 1 simple runner)\n *\n * chunking-decision-final.md 第 15 节:build/rebuild/cleanup 任务串行处理,避免 CPU、\n * 内存、API 请求和 SQLite 写入竞争;支持按对话暂停、取消、续跑;失败保留已完成部分。\n *\n * 本队列只负责调度与停止信号管理,具体执行(读库/embedding/写入)由注入的 executor\n * 完成。队列不持久化;重启后的恢复由 service 层扫描业务状态表(权威来源)重新入队驱动。\n */\n\nimport type { StopSignal } from './runner'\n\nexport type SemanticIndexJobType = 'build' | 'rebuild' | 'cleanup'\n\nexport interface SemanticIndexJob {\n type: SemanticIndexJobType\n dbPathHash: string\n}\n\nexport interface JobContext {\n job: SemanticIndexJob\n /** executor 应将其传入 runWarmup;返回 'paused'/'cancelled' 时应停止 */\n checkStop: StopSignal\n}\n\nexport type JobExecutor = (ctx: JobContext) => Promise<void>\n\ntype StopState = null | 'paused' | 'cancelled'\n\nexport class SemanticIndexJobQueue {\n private executor: JobExecutor\n private pending: SemanticIndexJob[] = []\n private processing = false\n private currentHash: string | null = null\n private stopState: StopState = null\n private idleResolvers: (() => void)[] = []\n\n constructor(executor: JobExecutor) {\n this.executor = executor\n }\n\n /** 入队;同一对话 + 同类型的待处理任务去重 */\n enqueue(job: SemanticIndexJob): void {\n const duplicate = this.pending.some((j) => j.dbPathHash === job.dbPathHash && j.type === job.type)\n if (duplicate) return\n this.pending.push(job)\n void this.process()\n }\n\n /** 暂停指定对话:停止其正在运行的任务并移除其待处理任务(不自动续跑) */\n pause(dbPathHash: string): void {\n this.removePending(dbPathHash)\n if (this.currentHash === dbPathHash) this.stopState = 'paused'\n }\n\n /** 取消指定对话:停止其正在运行的任务并移除其待处理任务 */\n cancel(dbPathHash: string): void {\n this.removePending(dbPathHash)\n if (this.currentHash === dbPathHash) this.stopState = 'cancelled'\n }\n\n isRunning(dbPathHash: string): boolean {\n return this.currentHash === dbPathHash\n }\n\n isQueued(dbPathHash: string): boolean {\n return this.currentHash === dbPathHash || this.pending.some((j) => j.dbPathHash === dbPathHash)\n }\n\n /** 等待队列清空(含当前任务) */\n whenIdle(): Promise<void> {\n if (!this.processing && this.pending.length === 0) return Promise.resolve()\n return new Promise((resolve) => this.idleResolvers.push(resolve))\n }\n\n private removePending(dbPathHash: string): void {\n this.pending = this.pending.filter((j) => j.dbPathHash !== dbPathHash)\n }\n\n private async process(): Promise<void> {\n if (this.processing) return\n this.processing = true\n\n while (this.pending.length > 0) {\n const job = this.pending.shift()!\n this.currentHash = job.dbPathHash\n this.stopState = null\n try {\n await this.executor({ job, checkStop: () => this.stopState })\n } catch {\n // executor 内部负责把失败写入业务状态;队列继续处理后续任务\n }\n this.currentHash = null\n this.stopState = null\n }\n\n this.processing = false\n const resolvers = this.idleResolvers\n this.idleResolvers = []\n for (const resolve of resolvers) resolve()\n }\n}\n","/**\n * Parent/child chunker(纯函数)\n *\n * 实现 chunking-decision-final.md 第 7/8/9/10 节的 Hierarchical Balanced Chunking:\n * - parent:按时间 gap 粗分,超 token 估算上限再拆 sub-parent。\n * - child:parent 内按有效字符滑动,overlap 保留末尾若干条消息。\n * - 语义真空消息:不计有效字符、不进 header、不进 embedding body,但保留在范围与\n * raw_content_hash 中(证据可读性)。\n * - header:来源 + 时间范围 + 参与者(Contextual Retrieval)。\n *\n * 本模块与具体模型/维度无关;全局 chunk_id 由 warmup 层用 db_path_hash + model_id\n * 组合本模块输出的 localChunkId 得到。\n */\n\nimport { createHash } from 'crypto'\nimport { MessageType } from '@openchatlab/shared-types'\nimport {\n CHUNKER_VERSION,\n DEFAULT_CHUNKER_CONFIG,\n computeChunkerConfigHash,\n deriveParentId,\n type ChunkerConfig,\n} from './chunker-config'\nimport { clampEstimatedTokens, estimateTokens } from './tokens'\n\nexport interface ChunkMessageInput {\n id: number\n senderName: string\n content: string | null\n /** 毫秒时间戳 */\n ts: number\n /** 平台消息类型(用于识别语义真空类型) */\n type?: number\n}\n\nexport interface ChunkSource {\n /** 对话名称(群名或私聊对端名) */\n title: string\n kind: 'group' | 'private'\n}\n\nexport interface ChunkMessagesInput {\n messages: ChunkMessageInput[]\n source: ChunkSource\n config?: ChunkerConfig\n}\n\nexport interface ChildChunk {\n /** 模型无关的稳定本地 id;全局 chunk_id 由 warmup 组合 db_path_hash + model_id */\n localChunkId: string\n parentId: string\n startMessageId: number\n endMessageId: number\n startTs: number\n endTs: number\n /** 范围内消息总数(含语义真空) */\n messageCount: number\n /** 非真空消息有效字符数 */\n effectiveChars: number\n rawContentHash: string\n embeddingInputHash: string\n /** 送入 embedding 模型的最终文本(header + 非真空 body) */\n embeddingInput: string\n}\n\nexport interface ChunkResult {\n chunkerVersion: string\n chunkerConfigHash: string\n chunks: ChildChunk[]\n skippedCount: number\n parentCount: number\n}\n\n// ==================== 语义真空判定 ====================\n\n/**\n * 语义真空消息类型(canonical MessageType):媒体/占位/系统类,自身不携带可嵌入文本。\n * TEXT(0) 与 LINK/SHARE/REPLY/FORWARD 等携带文本的类型不在此集合,由内容规则进一步判定。\n */\nconst VOID_MESSAGE_TYPES = new Set<number>([\n MessageType.IMAGE,\n MessageType.VOICE,\n MessageType.VIDEO,\n MessageType.FILE,\n MessageType.EMOJI,\n MessageType.LOCATION,\n MessageType.RED_PACKET,\n MessageType.TRANSFER,\n MessageType.POKE,\n MessageType.CALL,\n MessageType.CONTACT,\n MessageType.SYSTEM,\n MessageType.RECALL,\n])\n\n/** 占位类标签(与平台导出格式匹配,保持原始语言不变) */\nconst VOID_TEXT_PATTERNS = [\n '[图片]',\n '[语音]',\n '[视频]',\n '[文件]',\n '[表情]',\n '[动画表情]',\n '[位置]',\n '[名片]',\n '[红包]',\n '[转账]',\n '[撤回消息]',\n]\n\nconst VOID_REVOKE_PATTERNS = ['撤回了一条消息']\n\n/** 短填充语集合(语义真空),变更需 bump CHUNKER_VERSION */\nconst VOID_FILLERS = new Set([\n '好',\n '好的',\n '好滴',\n '好呀',\n '嗯',\n '嗯嗯',\n '嗯呢',\n '哦',\n '噢',\n '额',\n '呃',\n '哈',\n '哈哈',\n '哈哈哈',\n '呵呵',\n '在',\n '在吗',\n '在么',\n '收到',\n '行',\n '行吧',\n '可以',\n '是',\n '是的',\n '对',\n '对的',\n 'ok',\n 'okay',\n '👍',\n '👌',\n '🙏',\n])\n\n/** 归一化用于填充语匹配:去首尾空白、去末尾标点、英文小写 */\nfunction normalizeFiller(content: string): string {\n return content\n .trim()\n .toLowerCase()\n .replace(/[。.!!??、,,~~\\s]+$/u, '')\n}\n\nexport function isSemanticVoid(message: ChunkMessageInput): boolean {\n if (message.type !== undefined && VOID_MESSAGE_TYPES.has(message.type)) return true\n\n const content = (message.content ?? '').trim()\n if (content.length === 0) return true\n\n if (VOID_TEXT_PATTERNS.includes(content)) return true\n if (VOID_REVOKE_PATTERNS.some((p) => content.includes(p))) return true\n\n return VOID_FILLERS.has(normalizeFiller(content))\n}\n\n// ==================== 文本与 token 估算 ====================\n\nfunction effectiveContentOf(message: ChunkMessageInput): string {\n return isSemanticVoid(message) ? '' : (message.content ?? '').trim()\n}\n\nfunction sumEffectiveChars(messages: ChunkMessageInput[]): number {\n let total = 0\n for (const m of messages) total += effectiveContentOf(m).length\n return total\n}\n\nfunction sha256Hex(input: string): string {\n return createHash('sha256').update(input, 'utf-8').digest('hex')\n}\n\n// ==================== header / body ====================\n\nconst EMBEDDING_HEADER_BUDGET_RATIO = 0.25\nconst SOURCE_TITLE_HEADER_SHARE = 1 / 3\nconst SENDER_NAMES_BODY_BUDGET_RATIO = 0.1\n\nfunction pad2(n: number): string {\n return String(n).padStart(2, '0')\n}\n\nfunction formatDateTime(ts: number): string {\n const d = new Date(ts)\n return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`\n}\n\nfunction formatTime(ts: number): string {\n const d = new Date(ts)\n return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`\n}\n\nfunction formatTimeRange(startTs: number, endTs: number): string {\n const start = new Date(startTs)\n const end = new Date(endTs)\n const sameDay =\n start.getFullYear() === end.getFullYear() &&\n start.getMonth() === end.getMonth() &&\n start.getDate() === end.getDate()\n return `${formatDateTime(startTs)} - ${sameDay ? formatTime(endTs) : formatDateTime(endTs)}`\n}\n\n/**\n * 正文发送者名共享总预算的两段式收缩:总占用不超预算时不截断任何名字;\n * 超预算时求最大的统一 token 上限(水位线),短名字保持原样,只收缩超出水位线的长名字。\n * 返回 null 表示预算充足、无需截断。\n */\nfunction resolveSenderNameTokenCap(nameTokenCounts: number[], totalLimit: number): number | null {\n const total = nameTokenCounts.reduce((sum, count) => sum + count, 0)\n if (total <= totalLimit) return null\n\n const sorted = [...nameTokenCounts].sort((a, b) => a - b)\n let consumed = 0\n for (let i = 0; i < sorted.length; i++) {\n const cap = Math.floor((totalLimit - consumed) / (sorted.length - i))\n if (sorted[i] > cap) return cap\n consumed += sorted[i]\n }\n // 循环走完意味着总占用未超预算,与上面的提前返回矛盾,仅为类型完整性保留\n return null\n}\n\n/**\n * 在 token/字节总护栏内组装 embedding 文本。\n * header 最多占四分之一预算,异常长的来源名或发送者名只截断派生文本,正文始终优先保留。\n */\nfunction buildEmbeddingInput(source: ChunkSource, messages: ChunkMessageInput[], maxTokens: number): string {\n const limit = Math.max(0, Math.floor(maxTokens))\n if (limit === 0) return ''\n\n const effective = messages.filter((m) => !isSemanticVoid(m))\n const startTs = messages[0].ts\n const endTs = messages[messages.length - 1].ts\n\n const headerLimit = Math.max(1, Math.floor(limit * EMBEDDING_HEADER_BUDGET_RATIO))\n const bodyFloor = Math.max(1, limit - headerLimit)\n const sourceTitleLimit = Math.max(1, Math.floor(headerLimit * SOURCE_TITLE_HEADER_SHARE))\n const senderNamesTotalLimit = Math.floor(bodyFloor * SENDER_NAMES_BODY_BUDGET_RATIO)\n // 所有发送者名共享正文总预算:预算内保持原样,超预算时只收缩长名字,\n // 避免多条异常长名称累计挤掉后半段正文,同时不影响正常长度的昵称。\n const bodySenderNameCap = resolveSenderNameTokenCap(\n effective.map((m) => estimateTokens(m.senderName)),\n senderNamesTotalLimit\n )\n const clampHeaderSenderName = (name: string) => clampEstimatedTokens(name, sourceTitleLimit)\n const clampBodySenderName = (name: string) =>\n bodySenderNameCap === null ? name : clampEstimatedTokens(name, bodySenderNameCap)\n\n const headerLines: string[] = []\n const kindLabel = source.kind === 'group' ? '群聊' : '私聊'\n headerLines.push(`[来源] ${clampEstimatedTokens(source.title, sourceTitleLimit)}(${kindLabel})`)\n headerLines.push(`[时间范围] ${formatTimeRange(startTs, endTs)}`)\n if (source.kind === 'group') {\n const participants = [...new Set(effective.map((m) => clampHeaderSenderName(m.senderName)))]\n headerLines.push(`[参与者] ${participants.join('、')}`)\n }\n const header = clampEstimatedTokens(headerLines.join('\\n'), headerLimit)\n\n const separator = '\\n\\n'\n const remaining = Math.max(0, limit - estimateTokens(header) - estimateTokens(separator))\n const body = clampEstimatedTokens(\n effective.map((m) => `${clampBodySenderName(m.senderName)}: ${(m.content ?? '').trim()}`).join('\\n'),\n remaining\n )\n const combined = body ? `${header}${separator}${body}` : header\n return clampEstimatedTokens(combined, limit)\n}\n\nfunction rawContentHashOf(messages: ChunkMessageInput[]): string {\n return sha256Hex(messages.map((m) => `${m.id}:${m.content ?? ''}`).join('\\n'))\n}\n\n// ==================== parent / child 切分 ====================\n\nfunction segmentParents(messages: ChunkMessageInput[], config: ChunkerConfig): ChunkMessageInput[][] {\n const parents: ChunkMessageInput[][] = []\n let current: ChunkMessageInput[] = []\n const gapMs = config.parentGapSeconds * 1000\n\n for (const m of messages) {\n if (current.length === 0) {\n current.push(m)\n continue\n }\n const prev = current[current.length - 1]\n const overGap = m.ts - prev.ts > gapMs\n const overTokens = estimateTokens(current.map((x) => (x.content ?? '').trim()).join('\\n')) >= config.parentMaxTokens\n if (overGap || overTokens) {\n parents.push(current)\n current = [m]\n } else {\n current.push(m)\n }\n }\n if (current.length > 0) parents.push(current)\n return parents\n}\n\nfunction buildChildDrafts(parent: ChunkMessageInput[], config: ChunkerConfig): ChunkMessageInput[][] {\n const drafts: ChunkMessageInput[][] = []\n let current: ChunkMessageInput[] = []\n let newCount = 0\n\n const closeAndSeed = () => {\n drafts.push(current)\n const seed = config.overlapMessages > 0 ? current.slice(-config.overlapMessages) : []\n current = [...seed]\n newCount = 0\n }\n\n for (const m of parent) {\n current.push(m)\n newCount++\n const effectiveChars = sumEffectiveChars(current)\n const messageCount = current.length\n const overChars = effectiveChars >= config.childTargetMaxChars\n const overTokens = estimateTokens(current.map((x) => effectiveContentOf(x)).join('\\n')) >= config.childHardMaxTokens\n // 消息数软上限:高频短消息群聊里,攒够消息数且有效字符达标即关闭,避免单 chunk 混入过多消息\n const overSoftMessages = messageCount >= config.childSoftMaxMessages && effectiveChars >= config.childTargetMinChars\n // 消息数硬上限:即使有效字符不足 min 也强制关闭,避免极端短消息无限堆积\n const overHardMessages = messageCount >= config.childHardMaxMessages\n if (overChars || overTokens || overSoftMessages || overHardMessages) closeAndSeed()\n }\n\n // flush:仅当还有新消息(避免重复上一个 child 的 overlap 尾巴)\n if (current.length > 0 && (newCount > 0 || drafts.length === 0)) {\n drafts.push(current)\n }\n return drafts\n}\n\nexport function chunkMessages(input: ChunkMessagesInput): ChunkResult {\n const config = input.config ?? DEFAULT_CHUNKER_CONFIG\n const chunkerConfigHash = computeChunkerConfigHash(config)\n\n const parents = segmentParents(input.messages, config)\n const chunks: ChildChunk[] = []\n let skippedCount = 0\n\n for (const parent of parents) {\n const parentId = deriveParentId({\n startMessageId: parent[0].id,\n endMessageId: parent[parent.length - 1].id,\n gapSeconds: config.parentGapSeconds,\n chunkerVersion: CHUNKER_VERSION,\n chunkerConfigHash,\n })\n\n for (const draft of buildChildDrafts(parent, config)) {\n const effectiveChars = sumEffectiveChars(draft)\n if (effectiveChars < config.semanticVoidSkipThreshold) {\n skippedCount++\n continue\n }\n const startMessageId = draft[0].id\n const endMessageId = draft[draft.length - 1].id\n // 单条超长消息无法在消息边界上继续切分,因此在派生的 embedding 文本上执行最终硬限制。\n const embeddingInput = buildEmbeddingInput(input.source, draft, config.childHardMaxTokens)\n chunks.push({\n localChunkId: `${parentId}#${startMessageId}-${endMessageId}`,\n parentId,\n startMessageId,\n endMessageId,\n startTs: draft[0].ts,\n endTs: draft[draft.length - 1].ts,\n messageCount: draft.length,\n effectiveChars,\n rawContentHash: rawContentHashOf(draft),\n embeddingInputHash: sha256Hex(embeddingInput),\n embeddingInput,\n })\n }\n }\n\n return { chunkerVersion: CHUNKER_VERSION, chunkerConfigHash, chunks, skippedCount, parentCount: parents.length }\n}\n","/**\n * 单对话 warmup runner\n *\n * 职责(chunking-decision-final.md 第 9/15 节):读取对话消息流 -> 全量 chunk ->\n * 逐 chunk embedding 并增量写入向量库 -> 实时更新业务状态。支持暂停、取消、失败处理\n * 和断点续跑,部分完成即可被检索。\n *\n * 设计要点:\n * - chunk 是纯函数且廉价,每次运行对全量消息重新 chunk(保证 parent 边界稳定)。\n * - 通过业务状态的 lastIndexedMessageId 游标跳过已写入 chunk,实现断点续跑。\n * - 每写入一个 chunk 即更新游标,保证崩溃后续跑不重复写入(chunk_id UNIQUE)。\n * - embedding 是瓶颈;每 chunk 前检查暂停/取消,embedding 返回后再次检查取消,\n * 避免在清理已取消索引后写入刚完成的向量。\n *\n * 依赖全部注入,便于单测(fake source/embedder + 真实内存级 SQLite store)。\n */\n\nimport type { EmbeddingProvider } from '../embedding/types'\nimport { chunkMessages, type ChunkMessageInput, type ChunkSource } from '../chunker'\nimport { CHUNKER_VERSION, STRATEGY_ID, composeChunkId, type ChunkerConfig } from '../chunker-config'\nimport type { EmbeddingIndexStore } from '../store'\nimport type { SemanticIndexStateStore } from '../session-state-store'\nimport type { ChunkInsert, ChunkRecord } from '../types'\n\n/** 对话消息来源(warmup 输入抽象,真实实现读取聊天库) */\nexport interface SemanticMessageSource {\n getSource(): ChunkSource\n countMessages(): number\n /** 按 ts, id 升序返回全部消息 */\n readAllMessages(): ChunkMessageInput[]\n}\n\n/** 停止信号:返回 null 继续,'paused' 暂停(可续跑),'cancelled' 取消 */\nexport type StopSignal = () => null | 'paused' | 'cancelled'\n\nexport interface WarmupRunnerOptions {\n dbPathHash: string\n modelId: string\n embedder: EmbeddingProvider\n store: EmbeddingIndexStore\n stateStore: SemanticIndexStateStore\n source: SemanticMessageSource\n config?: ChunkerConfig\n checkStop?: StopSignal\n}\n\nexport type WarmupStatus = 'completed' | 'paused' | 'cancelled' | 'failed'\n\nexport interface WarmupResult {\n status: WarmupStatus\n chunksWritten: number\n error?: string\n}\n\nfunction resolveDocumentBatchSize(embedder: EmbeddingProvider): number {\n const size = embedder.documentBatchSize\n return Number.isFinite(size) && size && size > 0 ? Math.floor(size) : 1\n}\n\nexport async function runWarmup(options: WarmupRunnerOptions): Promise<WarmupResult> {\n const { dbPathHash, modelId, embedder, store, stateStore, source, config, checkStop } = options\n\n let chunksWritten = 0\n try {\n const messages = source.readAllMessages()\n const total = source.countMessages()\n const savedState = stateStore.getState(dbPathHash)\n stateStore.updateProgress(dbPathHash, { indexStatus: 'running', totalMessages: total, error: null })\n\n // 消息 id -> 流位置,用于进度计数(消息按 ts 排序,id 未必单调)\n const streamIndexById = new Map<number, number>()\n messages.forEach((m, i) => streamIndexById.set(m.id, i))\n\n const resumeMessageId = savedState?.lastIndexedMessageId ?? null\n const rawResumeIndex = resumeMessageId !== null ? (streamIndexById.get(resumeMessageId) ?? -1) : -1\n\n // Detect non-append-only additions: if the cursor's new stream position doesn't match the\n // saved indexed count, older messages were backfilled before the cursor. Clear vectors and\n // re-index from scratch to avoid silently missing the backfilled messages.\n const isNonAppendOnly = rawResumeIndex >= 0 && rawResumeIndex + 1 !== (savedState?.indexedMessages ?? 0)\n if (isNonAppendOnly) {\n store.deleteByDbPathHash(dbPathHash)\n }\n const { chunks, chunkerConfigHash } = chunkMessages({ messages, source: source.getSource(), config })\n\n const chunkRanges = chunks.map((chunk) => ({\n chunk,\n startIndex: streamIndexById.get(chunk.startMessageId) ?? -1,\n endIndex: streamIndexById.get(chunk.endMessageId) ?? -1,\n }))\n\n let resumeIndex = isNonAppendOnly ? -1 : rawResumeIndex\n const hasAppendedMessages = !isNonAppendOnly && rawResumeIndex >= 0 && total > (savedState?.totalMessages ?? 0)\n if (hasAppendedMessages) {\n const cursorRange =\n chunkRanges.find((range) => range.startIndex <= rawResumeIndex && rawResumeIndex <= range.endIndex) ??\n chunkRanges.filter((range) => range.endIndex <= rawResumeIndex).sort((a, b) => b.endIndex - a.endIndex)[0]\n\n if (cursorRange) {\n const parentRanges = chunkRanges.filter((range) => range.chunk.parentId === cursorRange.chunk.parentId)\n const rewindIndex = Math.min(...parentRanges.map((range) => range.startIndex).filter((index) => index >= 0))\n const rewindMessage = messages[rewindIndex]\n if (rewindMessage) {\n store.deleteByModelFromPosition({\n dbPathHash,\n modelId,\n startTs: rewindMessage.ts,\n startMessageId: rewindMessage.id,\n })\n resumeIndex = rewindIndex - 1\n }\n }\n }\n\n // 续跑时统计已写入 chunk 数,保证 chunkCount 连续\n let storedChunkCount = store.countChunks(dbPathHash, modelId)\n if (resumeIndex < 0) storedChunkCount = 0\n\n const pendingRanges = chunkRanges.filter(({ endIndex }) => endIndex > resumeIndex)\n const batchSize = resolveDocumentBatchSize(embedder)\n for (let i = 0; i < pendingRanges.length; i += batchSize) {\n const batchRanges = pendingRanges.slice(i, i + batchSize)\n\n const stop = checkStop?.()\n if (stop) {\n stateStore.setIndexStatus(dbPathHash, stop === 'paused' ? 'paused' : 'cancelled')\n return { status: stop, chunksWritten }\n }\n\n const vectors = await embedder.embedDocuments(batchRanges.map(({ chunk }) => chunk.embeddingInput))\n if (vectors.length !== batchRanges.length) {\n throw new Error(`embedding provider returned ${vectors.length} vectors for ${batchRanges.length} inputs`)\n }\n const stopAfterEmbedding = checkStop?.()\n if (stopAfterEmbedding === 'cancelled') {\n stateStore.setIndexStatus(dbPathHash, 'cancelled')\n return { status: 'cancelled', chunksWritten }\n }\n\n // API provider 支持一次提交多个文本;本地 Qwen3 仍声明 batch=1,避免 last_token 污染。\n const indexedAt = Date.now()\n const inserts: ChunkInsert[] = batchRanges.map(({ chunk }, index) => {\n const vector = vectors[index]\n const record: ChunkRecord = {\n chunkId: composeChunkId(dbPathHash, modelId, chunk.localChunkId),\n dbPathHash,\n strategyId: STRATEGY_ID,\n modelId,\n dim: vector.length,\n parentId: chunk.parentId,\n startMessageId: chunk.startMessageId,\n endMessageId: chunk.endMessageId,\n startTs: chunk.startTs,\n endTs: chunk.endTs,\n messageCount: chunk.messageCount,\n rawContentHash: chunk.rawContentHash,\n embeddingInputHash: chunk.embeddingInputHash,\n chunkerVersion: CHUNKER_VERSION,\n chunkerConfigHash,\n indexedAt,\n status: 'indexed',\n }\n return { record, embedding: vector }\n })\n store.insertChunks(inserts)\n chunksWritten += inserts.length\n storedChunkCount += inserts.length\n\n const lastRange = batchRanges[batchRanges.length - 1]\n stateStore.updateProgress(dbPathHash, {\n indexStatus: 'running',\n indexedMessages: lastRange.endIndex + 1,\n lastIndexedMessageId: lastRange.chunk.endMessageId,\n chunkCount: storedChunkCount,\n })\n }\n\n stateStore.updateProgress(dbPathHash, { indexedMessages: total, chunkCount: storedChunkCount })\n stateStore.setIndexStatus(dbPathHash, 'completed', null)\n return { status: 'completed', chunksWritten }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n stateStore.setIndexStatus(dbPathHash, 'failed', message)\n return { status: 'failed', chunksWritten, error: message }\n }\n}\n","/** Dense semantic retrieval over the derived embedding index. */\n\nimport type { EmbeddingProvider } from '../embedding/types'\nimport type { EmbeddingIndexStore } from '../store'\nimport type { ChunkRecord } from '../types'\n\nexport interface SemanticSearchDeps {\n embedder: EmbeddingProvider\n store: EmbeddingIndexStore\n}\n\n/** Optional one-sided time range in milliseconds. */\nexport interface SemanticTimeRangeMs {\n startTs?: number\n endTs?: number\n}\n\nexport interface SemanticSearchParams {\n query: string\n dbPathHash: string\n modelId: string\n dim: number\n /** Number of dense candidates to retrieve. Defaults to 40. */\n denseTopN?: number\n /** Maximum number of hits to return. Defaults to 5. */\n finalTopK?: number\n /**\n * Optional time range in milliseconds.\n * Keeps chunks that overlap the range and enlarges the candidate pool while filtering.\n */\n timeRangeMs?: SemanticTimeRangeMs\n}\n\n/** Whether a chunk overlaps the optional time range. */\nfunction overlapsTimeRangeMs(record: ChunkRecord, filter?: SemanticTimeRangeMs): boolean {\n if (!filter) return true\n if (filter.startTs != null && record.endTs < filter.startTs) return false\n if (filter.endTs != null && record.startTs > filter.endTs) return false\n return true\n}\n\n/** Enlarges the candidate pool so narrow ranges in long chats still return in-range chunks. */\nconst TIME_FILTER_POOL_MULTIPLIER = 10\n\nexport interface SemanticSearchHit {\n chunkId: string\n score: number\n record: ChunkRecord\n /** Rank after time filtering, with zero being the best match. */\n denseRank: number\n}\n\nexport async function semanticSearch(\n deps: SemanticSearchDeps,\n params: SemanticSearchParams\n): Promise<SemanticSearchHit[]> {\n const { embedder, store } = deps\n const { query, dbPathHash, modelId, dim, finalTopK = 5, timeRangeMs } = params\n\n if (!query.trim()) return []\n\n // Enlarge the ANN pool when filtering by time so narrow ranges still have enough candidates.\n const poolFactor = timeRangeMs ? TIME_FILTER_POOL_MULTIPLIER : 1\n const denseTopN = Math.max((params.denseTopN ?? 40) * poolFactor, 40)\n\n const queryVector = await embedder.embedQuery(query)\n const dense = store.queryDense({ dbPathHash, modelId, dim, embedding: queryVector, k: denseTopN })\n\n const results: SemanticSearchHit[] = []\n for (const hit of dense) {\n if (!overlapsTimeRangeMs(hit.record, timeRangeMs)) continue\n results.push({\n chunkId: hit.chunkId,\n score: 1 / (1 + Math.max(0, hit.distance)),\n record: hit.record,\n denseRank: results.length,\n })\n if (results.length >= finalTopK) break\n }\n\n return results\n}\n","/**\n * 证据块组装\n *\n * chunking-decision-final.md 第 14 节:\n * - 语义检索后取 topK 命中 chunk,每个命中在 parent 内前后各扩展若干条消息,不跨 parent。\n * - 相邻命中可合并,但合并后超过单块 soft cap 则拆开注入。\n * - 总证据预算固定 1500 token:不够时优先减少前后扩展,再减少 chunk 数。\n *\n * 消息原文读取(聊天库连接)由注入的 MessageRangeReader 提供,保持模块平台无关、可单测。\n */\n\nimport { parseParentBounds } from '../chunker-config'\nimport { estimateTokens } from '../tokens'\n\nexport interface EvidenceMessage {\n id: number\n senderName: string\n content: string\n ts: number\n /** 发送者 id,用于昵称匿名化(U{id}) */\n senderId?: number\n /** 发送者平台 id,用于匿名化时识别 owner */\n senderPlatformId?: string\n}\n\n/** 读取聊天库父消息区间,按 ts, id 升序返回;startId/endId 为 parent 两端消息 id(ts 序,非 min/max) */\nexport interface MessageRangeReader {\n readRange(startId: number, endId: number): EvidenceMessage[]\n}\n\n/** 命中 chunk(来自 semanticSearch,已按 score 降序) */\nexport interface EvidenceHit {\n chunkId: string\n score: number\n parentId: string\n startMessageId: number\n endMessageId: number\n /** chunk 的起始时间戳(毫秒),供时间范围过滤使用 */\n startTs: number\n /** chunk 的结束时间戳(毫秒),供时间范围过滤使用 */\n endTs: number\n}\n\nexport interface EvidenceBudget {\n /** 总证据预算 token,默认 1500 */\n totalTokens?: number\n /** 单证据块 soft cap token,默认 800 */\n blockSoftCapTokens?: number\n /** 每个命中 chunk 前后各扩展消息条数,默认 3 */\n expandMessages?: number\n /** 最多取多少个命中 chunk,默认 5 */\n maxChunks?: number\n}\n\nexport interface EvidenceBlock {\n parentId: string\n startMessageId: number\n endMessageId: number\n messages: EvidenceMessage[]\n tokens: number\n /** 该块覆盖的命中 chunkId */\n chunkIds: string[]\n /** 块内最高命中分 */\n score: number\n}\n\nexport interface EvidenceResult {\n blocks: EvidenceBlock[]\n totalTokens: number\n}\n\n/** 证据块规范化文本(用于 token 估算与后续 prompt 注入) */\nexport function formatEvidenceMessages(messages: EvidenceMessage[]): string {\n return messages.map((m) => `${m.senderName}: ${m.content}`).join('\\n')\n}\n\nfunction messagesTokens(messages: EvidenceMessage[]): number {\n return estimateTokens(formatEvidenceMessages(messages))\n}\n\ninterface WorkingBlock {\n parentId: string\n parentMsgs: EvidenceMessage[]\n startIdx: number\n endIdx: number\n /** 命中核心范围(合并后取并集),收缩扩展时不得越过此范围 */\n coreStartIdx: number\n coreEndIdx: number\n chunkIds: string[]\n score: number\n}\n\nexport function assembleEvidence(\n reader: MessageRangeReader,\n hits: EvidenceHit[],\n budget: EvidenceBudget = {}\n): EvidenceResult {\n const totalTokens = budget.totalTokens ?? 1500\n const softCap = budget.blockSoftCapTokens ?? 800\n const expand = budget.expandMessages ?? 3\n const maxChunks = budget.maxChunks ?? 5\n\n const selected = hits.slice(0, maxChunks)\n const parentCache = new Map<string, EvidenceMessage[]>()\n\n // 1. 每个命中在 parent 内扩展为 working block\n const raw: WorkingBlock[] = []\n for (const hit of selected) {\n const bounds = parseParentBounds(hit.parentId)\n if (!bounds) continue\n let parentMsgs = parentCache.get(hit.parentId)\n if (!parentMsgs) {\n parentMsgs = reader.readRange(bounds.startMessageId, bounds.endMessageId)\n parentCache.set(hit.parentId, parentMsgs)\n }\n if (parentMsgs.length === 0) continue\n\n // Use message IDs for precise core boundary — ts has second granularity and is non-unique\n const coreStartIdx = parentMsgs.findIndex((m) => m.id === hit.startMessageId)\n let coreEndIdx = -1\n for (let i = parentMsgs.length - 1; i >= 0; i--) {\n if (parentMsgs[i].id === hit.endMessageId) {\n coreEndIdx = i\n break\n }\n }\n if (coreStartIdx < 0 || coreEndIdx < coreStartIdx) continue\n\n raw.push({\n parentId: hit.parentId,\n parentMsgs,\n startIdx: Math.max(0, coreStartIdx - expand),\n endIdx: Math.min(parentMsgs.length - 1, coreEndIdx + expand),\n coreStartIdx,\n coreEndIdx,\n chunkIds: [hit.chunkId],\n score: hit.score,\n })\n }\n\n // 2. 同 parent 内相邻/重叠且合并后不超 soft cap 的块合并\n const byParent = new Map<string, WorkingBlock[]>()\n for (const block of raw) {\n const list = byParent.get(block.parentId) ?? []\n list.push(block)\n byParent.set(block.parentId, list)\n }\n\n const merged: WorkingBlock[] = []\n for (const list of byParent.values()) {\n list.sort((a, b) => a.startIdx - b.startIdx)\n for (const block of list) {\n const last = merged[merged.length - 1]\n const mergeable = last && last.parentId === block.parentId && block.startIdx <= last.endIdx + 1\n if (mergeable) {\n const candStart = Math.min(last.startIdx, block.startIdx)\n const candEnd = Math.max(last.endIdx, block.endIdx)\n const candTokens = messagesTokens(block.parentMsgs.slice(candStart, candEnd + 1))\n if (candTokens <= softCap) {\n last.startIdx = candStart\n last.endIdx = candEnd\n last.coreStartIdx = Math.min(last.coreStartIdx, block.coreStartIdx)\n last.coreEndIdx = Math.max(last.coreEndIdx, block.coreEndIdx)\n last.chunkIds.push(...block.chunkIds)\n last.score = Math.max(last.score, block.score)\n continue\n }\n }\n merged.push({ ...block })\n }\n }\n\n // 3. 单块超 soft cap 时收缩扩展(不越过 core)\n for (const block of merged) shrinkToFit(block, softCap)\n\n // 4. 总预算控制:按 score 降序贪心装入;不够时收缩扩展,再不够则丢弃该块\n merged.sort((a, b) => b.score - a.score)\n const blocks: EvidenceBlock[] = []\n let used = 0\n for (const block of merged) {\n let tokens = messagesTokens(block.parentMsgs.slice(block.startIdx, block.endIdx + 1))\n if (used + tokens > totalTokens) {\n shrinkToFit(block, Math.min(softCap, totalTokens - used))\n tokens = messagesTokens(block.parentMsgs.slice(block.startIdx, block.endIdx + 1))\n if (used + tokens > totalTokens) continue\n }\n const messages = block.parentMsgs.slice(block.startIdx, block.endIdx + 1)\n blocks.push({\n parentId: block.parentId,\n startMessageId: messages[0].id,\n endMessageId: messages[messages.length - 1].id,\n messages,\n tokens,\n chunkIds: block.chunkIds,\n score: block.score,\n })\n used += tokens\n }\n\n return { blocks, totalTokens: used }\n}\n\n/** 收缩扩展使块 token 不超过 cap:从扩展更多的一侧逐条收缩,不越过 core 范围 */\nfunction shrinkToFit(block: WorkingBlock, cap: number): void {\n while (messagesTokens(block.parentMsgs.slice(block.startIdx, block.endIdx + 1)) > cap) {\n const startMargin = block.coreStartIdx - block.startIdx\n const endMargin = block.endIdx - block.coreEndIdx\n if (startMargin <= 0 && endMargin <= 0) break\n if (endMargin >= startMargin) block.endIdx--\n else block.startIdx++\n }\n}\n","/**\n * 聊天库消息来源适配器\n *\n * 把聊天库的 message/member 表桥接为 warmup runner 需要的 SemanticMessageSource。\n *\n * 时间单位转换:聊天库 message.ts 以秒存储,而 chunker 按毫秒计算时间 gap\n * (gapMs = parentGapSeconds * 1000),故此处统一乘 1000 转为毫秒。\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport type { ChunkSource } from '../chunker'\nimport type { SemanticMessageSource } from '../warmup/runner'\n\nconst SELECT_ALL_MESSAGES = `\n SELECT msg.id AS id,\n msg.ts AS ts,\n msg.type AS type,\n msg.content AS content,\n COALESCE(msg.sender_group_nickname, msg.sender_account_name, m.group_nickname, m.account_name, m.platform_id) AS senderName\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ORDER BY msg.ts ASC, msg.id ASC\n`\n\ninterface MessageRow {\n id: number\n ts: number\n type: number\n content: string | null\n senderName: string | null\n}\n\nexport function createChatDbMessageSource(db: DatabaseAdapter, source: ChunkSource): SemanticMessageSource {\n return {\n getSource: () => source,\n countMessages: () => {\n const row = db.prepare('SELECT COUNT(*) AS c FROM message').get() as { c: number } | undefined\n return row?.c ?? 0\n },\n readAllMessages: () => {\n const rows = db.prepare(SELECT_ALL_MESSAGES).all() as unknown as MessageRow[]\n return rows.map((r) => ({\n id: r.id,\n senderName: r.senderName ?? '',\n content: r.content,\n ts: r.ts * 1000,\n type: r.type,\n }))\n },\n }\n}\n","/**\n * 聊天库消息区间读取适配器\n *\n * 为证据组装提供 parent 边界消息的闭区间原文。使用 (ts, id) 复合边界:\n * - ts 范围只精确到秒,同一秒可有多条消息;若只用 ts 边界,会纳入同秒的相邻 parent 消息\n * - (ts, id) 复合条件可精确到行,保证不跨 parent 越界\n * - 同时支持回填旧消息(高 id 但低 ts)后的正确读取\n * 结果按 ts, id 升序返回(与 chunker 排序一致)。证据保留全部消息(含非文本)。\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport type { EvidenceMessage, MessageRangeReader } from '../retrieval/evidence'\n\nconst SELECT_RANGE = `\n WITH b AS (\n SELECT (SELECT ts FROM message WHERE id = ?) AS s_ts,\n (SELECT ts FROM message WHERE id = ?) AS e_ts\n )\n SELECT msg.id AS id,\n msg.ts AS ts,\n msg.content AS content,\n msg.sender_id AS senderId,\n m.platform_id AS senderPlatformId,\n COALESCE(msg.sender_group_nickname, msg.sender_account_name, m.group_nickname, m.account_name, m.platform_id) AS senderName\n FROM message msg\n JOIN member m ON msg.sender_id = m.id, b\n WHERE (msg.ts > b.s_ts OR (msg.ts = b.s_ts AND msg.id >= ?))\n AND (msg.ts < b.e_ts OR (msg.ts = b.e_ts AND msg.id <= ?))\n ORDER BY msg.ts ASC, msg.id ASC\n`\n\ninterface RangeRow {\n id: number\n ts: number\n content: string | null\n senderId: number | null\n senderPlatformId: string | null\n senderName: string | null\n}\n\nexport function createChatDbMessageRangeReader(db: DatabaseAdapter): MessageRangeReader {\n return {\n readRange(startId: number, endId: number): EvidenceMessage[] {\n const rows = db.prepare(SELECT_RANGE).all(startId, endId, startId, endId) as unknown as RangeRow[]\n return rows.map((r) => ({\n id: r.id,\n senderName: r.senderName ?? '',\n content: r.content ?? '',\n ts: r.ts * 1000,\n senderId: r.senderId ?? undefined,\n senderPlatformId: r.senderPlatformId ?? undefined,\n }))\n },\n }\n}\n","import { execFile } from 'node:child_process'\nimport fs from 'node:fs'\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { appLogger } from '../../logging/app-logger'\n\nexport const LOCAL_EMBEDDING_RUNTIME_PACKAGE = '@huggingface/transformers'\nexport const LOCAL_EMBEDDING_RUNTIME_VERSION = '4.2.0'\n\nexport type LocalEmbeddingRuntimeInstallMode = 'auto' | 'preinstalled'\nexport type LocalEmbeddingRuntimeStatus = 'idle' | 'installing' | 'ready' | 'error'\n\nexport interface LocalEmbeddingRuntimeConfig {\n baseDir: string\n installMode: LocalEmbeddingRuntimeInstallMode\n}\n\ninterface LocalEmbeddingRuntimeManifest {\n formatVersion: 1\n packageName: typeof LOCAL_EMBEDDING_RUNTIME_PACKAGE\n packageVersion: typeof LOCAL_EMBEDDING_RUNTIME_VERSION\n platform: string\n arch: string\n}\n\ninterface InstallCommand {\n command: string\n args: string[]\n cwd: string\n}\n\nexport interface LocalEmbeddingRuntimeManagerOptions extends LocalEmbeddingRuntimeConfig {\n platform?: string\n arch?: string\n npmCommand?: string\n runInstall?: (input: InstallCommand) => Promise<void>\n verifyStagedRuntime?: (runtimeDir: string) => Promise<void>\n verifyRuntime?: (runtimeDir: string) => Promise<void>\n importTransformers?: (runtimeDir: string) => Promise<typeof import('@huggingface/transformers')>\n}\n\nconst RUNTIME_MANIFEST_FILE = 'runtime.json'\n\nconst VERIFY_RUNTIME_SCRIPT = `\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nconst [, runtimeDir, transformersPackage] = process.argv\nconst runtimeRequire = createRequire(path.join(runtimeDir, 'package.json'))\nconst importFromRuntime = async (packageName) => {\n const resolved = runtimeRequire.resolve(packageName)\n return await import(pathToFileURL(resolved).href)\n}\n\nconst transformers = await importFromRuntime(transformersPackage)\nif (typeof transformers.pipeline !== 'function' || typeof transformers.env !== 'object') {\n throw new Error('Installed Transformers runtime does not expose the expected Node API.')\n}\n\nconst onnxRuntime = await importFromRuntime('onnxruntime-node')\nif (typeof onnxRuntime.listSupportedBackends !== 'function') {\n throw new Error('Installed ONNX runtime does not expose listSupportedBackends().')\n}\n`\n\nfunction executeInstall(input: InstallCommand): Promise<void> {\n return new Promise((resolve, reject) => {\n execFile(\n input.command,\n input.args,\n {\n cwd: input.cwd,\n env: { ...process.env, npm_config_update_notifier: 'false' },\n maxBuffer: 10 * 1024 * 1024,\n shell: process.platform === 'win32',\n },\n (error, _stdout, stderr) => {\n if (!error) {\n resolve()\n return\n }\n reject(new Error(stderr.trim() || error.message, { cause: error }))\n }\n )\n })\n}\n\nfunction verifyRuntimeInSubprocess(runtimeDir: string): Promise<void> {\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n ['--input-type=module', '--eval', VERIFY_RUNTIME_SCRIPT, runtimeDir, LOCAL_EMBEDDING_RUNTIME_PACKAGE],\n {\n env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },\n maxBuffer: 10 * 1024 * 1024,\n },\n (error, _stdout, stderr) => {\n if (!error) {\n resolve()\n return\n }\n reject(new Error(stderr.trim() || error.message, { cause: error }))\n }\n )\n })\n}\n\nfunction createRuntimeRequire(runtimeDir: string): NodeJS.Require {\n return createRequire(path.join(runtimeDir, 'package.json'))\n}\n\nasync function importFromRuntime(runtimeDir: string, packageName: string): Promise<Record<string, unknown>> {\n const resolved = createRuntimeRequire(runtimeDir).resolve(packageName)\n return (await import(pathToFileURL(resolved).href)) as Record<string, unknown>\n}\n\nasync function verifyInstalledRuntime(runtimeDir: string): Promise<void> {\n const transformers = await importFromRuntime(runtimeDir, LOCAL_EMBEDDING_RUNTIME_PACKAGE)\n if (typeof transformers.pipeline !== 'function' || typeof transformers.env !== 'object') {\n throw new Error('Installed Transformers runtime does not expose the expected Node API.')\n }\n\n const onnxRuntime = await importFromRuntime(runtimeDir, 'onnxruntime-node')\n if (typeof onnxRuntime.listSupportedBackends !== 'function') {\n throw new Error('Installed ONNX runtime does not expose listSupportedBackends().')\n }\n}\n\nasync function loadInstalledTransformers(runtimeDir: string): Promise<typeof import('@huggingface/transformers')> {\n return (await importFromRuntime(\n runtimeDir,\n LOCAL_EMBEDDING_RUNTIME_PACKAGE\n )) as unknown as typeof import('@huggingface/transformers')\n}\n\nexport class LocalEmbeddingRuntimeManager {\n private readonly baseDir: string\n private readonly installMode: LocalEmbeddingRuntimeInstallMode\n private readonly platform: string\n private readonly arch: string\n private readonly npmCommand: string\n private readonly runInstall: (input: InstallCommand) => Promise<void>\n private readonly verifyStagedRuntime: (runtimeDir: string) => Promise<void>\n private readonly verifyRuntime: (runtimeDir: string) => Promise<void>\n private readonly importTransformers: (runtimeDir: string) => Promise<typeof import('@huggingface/transformers')>\n\n private status: LocalEmbeddingRuntimeStatus\n private installPromise: Promise<string> | null = null\n private transformersPromise: Promise<typeof import('@huggingface/transformers')> | null = null\n private verifiedRuntimeDir: string | null = null\n\n constructor(options: LocalEmbeddingRuntimeManagerOptions) {\n this.baseDir = options.baseDir\n this.installMode = options.installMode\n this.platform = options.platform ?? process.platform\n this.arch = options.arch ?? process.arch\n this.npmCommand = options.npmCommand ?? (this.platform === 'win32' ? 'npm.cmd' : 'npm')\n this.runInstall = options.runInstall ?? executeInstall\n this.verifyStagedRuntime = options.verifyStagedRuntime ?? verifyRuntimeInSubprocess\n this.verifyRuntime = options.verifyRuntime ?? verifyInstalledRuntime\n this.importTransformers = options.importTransformers ?? loadInstalledTransformers\n this.status = 'idle'\n }\n\n getStatus(): LocalEmbeddingRuntimeStatus {\n return this.status\n }\n\n getRuntimeDir(): string {\n return path.join(this.baseDir, `transformers-${LOCAL_EMBEDDING_RUNTIME_VERSION}`, `${this.platform}-${this.arch}`)\n }\n\n async ensureInstalled(): Promise<string> {\n if (!this.installPromise) {\n this.installPromise = this.ensureInstalledOnce().finally(() => {\n this.installPromise = null\n })\n }\n return await this.installPromise\n }\n\n async loadTransformers(): Promise<typeof import('@huggingface/transformers')> {\n if (!this.transformersPromise) {\n this.transformersPromise = this.ensureInstalled()\n .then((runtimeDir) => this.importTransformers(runtimeDir))\n .catch((error) => {\n this.transformersPromise = null\n throw error\n })\n }\n return await this.transformersPromise\n }\n\n private async ensureInstalledOnce(): Promise<string> {\n const runtimeDir = this.getRuntimeDir()\n try {\n if (this.hasCompleteLayout(runtimeDir)) {\n try {\n await this.verifyOnce(runtimeDir)\n this.status = 'ready'\n return runtimeDir\n } catch (error) {\n if (this.installMode === 'preinstalled') throw error\n appLogger.warn('semantic-index', 'Existing local embedding runtime is invalid; reinstalling', {\n packageVersion: LOCAL_EMBEDDING_RUNTIME_VERSION,\n platform: this.platform,\n arch: this.arch,\n error: error instanceof Error ? error.message : String(error),\n })\n fs.rmSync(runtimeDir, { recursive: true, force: true })\n this.verifiedRuntimeDir = null\n }\n }\n\n if (this.installMode === 'preinstalled') {\n throw new Error(`Preinstalled local embedding runtime is missing or incomplete: ${runtimeDir}`)\n }\n\n this.status = 'installing'\n appLogger.info('semantic-index', 'Local embedding runtime installation started', {\n packageVersion: LOCAL_EMBEDDING_RUNTIME_VERSION,\n platform: this.platform,\n arch: this.arch,\n })\n\n await this.installAtomically(runtimeDir)\n await this.verifyOnce(runtimeDir)\n this.status = 'ready'\n appLogger.info('semantic-index', 'Local embedding runtime installation completed', {\n packageVersion: LOCAL_EMBEDDING_RUNTIME_VERSION,\n platform: this.platform,\n arch: this.arch,\n })\n return runtimeDir\n } catch (error) {\n this.status = 'error'\n appLogger.error('semantic-index', 'Local embedding runtime installation failed', error)\n throw error\n }\n }\n\n private async installAtomically(runtimeDir: string): Promise<void> {\n const parentDir = path.dirname(runtimeDir)\n fs.mkdirSync(parentDir, { recursive: true })\n const stagingDir = fs.mkdtempSync(path.join(parentDir, '.installing-'))\n\n try {\n fs.writeFileSync(\n path.join(stagingDir, 'package.json'),\n `${JSON.stringify({ private: true, type: 'module' }, null, 2)}\\n`,\n 'utf8'\n )\n await this.runInstall({\n command: this.npmCommand,\n args: [\n 'install',\n '--omit=dev',\n '--no-audit',\n '--no-fund',\n '--save-exact',\n '--loglevel=error',\n '--prefix',\n stagingDir,\n `${LOCAL_EMBEDDING_RUNTIME_PACKAGE}@${LOCAL_EMBEDDING_RUNTIME_VERSION}`,\n ],\n cwd: stagingDir,\n })\n // Keep native modules in a disposable process so Windows releases DLL handles before the directory rename.\n await this.verifyStagedRuntime(stagingDir)\n fs.writeFileSync(path.join(stagingDir, RUNTIME_MANIFEST_FILE), `${JSON.stringify(this.manifest(), null, 2)}\\n`)\n\n if (fs.existsSync(runtimeDir)) {\n if (this.hasCompleteLayout(runtimeDir)) return\n fs.rmSync(runtimeDir, { recursive: true, force: true })\n }\n try {\n fs.renameSync(stagingDir, runtimeDir)\n } catch (error) {\n // Another ChatLab process may have completed the same atomic install first.\n if (!this.hasCompleteLayout(runtimeDir)) throw error\n }\n } finally {\n fs.rmSync(stagingDir, { recursive: true, force: true })\n }\n }\n\n private async verifyOnce(runtimeDir: string): Promise<void> {\n if (this.verifiedRuntimeDir === runtimeDir) return\n await this.verifyRuntime(runtimeDir)\n this.verifiedRuntimeDir = runtimeDir\n }\n\n private hasCompleteLayout(runtimeDir: string): boolean {\n const manifestPath = path.join(runtimeDir, RUNTIME_MANIFEST_FILE)\n const packagePath = path.join(runtimeDir, 'node_modules', '@huggingface', 'transformers', 'package.json')\n if (!fs.existsSync(manifestPath) || !fs.existsSync(packagePath)) return false\n try {\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as Partial<LocalEmbeddingRuntimeManifest>\n const expected = this.manifest()\n return (\n manifest.formatVersion === expected.formatVersion &&\n manifest.packageName === expected.packageName &&\n manifest.packageVersion === expected.packageVersion &&\n manifest.platform === expected.platform &&\n manifest.arch === expected.arch\n )\n } catch {\n return false\n }\n }\n\n private manifest(): LocalEmbeddingRuntimeManifest {\n return {\n formatVersion: 1,\n packageName: LOCAL_EMBEDDING_RUNTIME_PACKAGE,\n packageVersion: LOCAL_EMBEDDING_RUNTIME_VERSION,\n platform: this.platform,\n arch: this.arch,\n }\n }\n}\n\nexport function createLocalEmbeddingRuntimeManager(\n options: LocalEmbeddingRuntimeManagerOptions\n): LocalEmbeddingRuntimeManager {\n return new LocalEmbeddingRuntimeManager(options)\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport {\n NAVIGATION_LAYOUT_SCHEMA_VERSION,\n type NavigationLayout,\n type NavigationLayoutLoadResult,\n type NavigationLayoutPrimaryItem,\n} from '@openchatlab/shared-types'\nimport { appLogger } from '../logging/app-logger'\n\nconst NAVIGATION_LAYOUT_FILE = 'navigation-layout.json'\n\nexport class NavigationLayoutValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'NavigationLayoutValidationError'\n }\n}\n\nexport interface NavigationLayoutService {\n load(): NavigationLayoutLoadResult\n save(input: unknown): NavigationLayout\n reset(): void\n}\n\nexport function createNavigationLayoutService(systemDir: string): NavigationLayoutService {\n const settingsDir = path.join(systemDir, 'settings')\n const filePath = path.join(settingsDir, NAVIGATION_LAYOUT_FILE)\n\n return {\n load() {\n if (!fs.existsSync(filePath)) return { status: 'missing', layout: null }\n try {\n const layout = normalizeNavigationLayout(JSON.parse(fs.readFileSync(filePath, 'utf-8')))\n return { status: 'saved', layout }\n } catch (error) {\n appLogger.warn('navigation-layout', 'Saved navigation layout is invalid; using plugin defaults', error)\n return { status: 'invalid', layout: null }\n }\n },\n\n save(input) {\n const layout = normalizeNavigationLayout(input)\n fs.mkdirSync(settingsDir, { recursive: true })\n const tempPath = path.join(settingsDir, `.navigation-layout.${process.pid}.${Date.now()}.tmp`)\n try {\n fs.writeFileSync(tempPath, `${JSON.stringify(layout, null, 2)}\\n`, 'utf-8')\n fs.renameSync(tempPath, filePath)\n } finally {\n if (fs.existsSync(tempPath)) fs.rmSync(tempPath, { force: true })\n }\n appLogger.info('navigation-layout', 'Navigation layout saved')\n return layout\n },\n\n reset() {\n if (fs.existsSync(filePath)) fs.rmSync(filePath)\n appLogger.info('navigation-layout', 'Navigation layout reset to plugin defaults')\n },\n }\n}\n\nexport function normalizeNavigationLayout(input: unknown): NavigationLayout {\n const record = requireRecord(input, 'layout')\n if (record.schemaVersion !== NAVIGATION_LAYOUT_SCHEMA_VERSION) {\n throw new NavigationLayoutValidationError(\n `Unsupported navigation layout schema version: ${String(record.schemaVersion)}`\n )\n }\n if (!Array.isArray(record.primary))\n throw new NavigationLayoutValidationError('Navigation layout primary must be an array')\n if (!Array.isArray(record.hiddenEntryIds)) {\n throw new NavigationLayoutValidationError('Navigation layout hiddenEntryIds must be an array')\n }\n\n const groupIds = new Set<string>()\n const visibleEntryIds = new Set<string>()\n const primary = record.primary.map((item, index) => normalizePrimaryItem(item, index, groupIds, visibleEntryIds))\n const hiddenEntryIds = normalizeUniqueIds(record.hiddenEntryIds, 'hiddenEntryIds')\n for (const entryId of hiddenEntryIds) {\n if (visibleEntryIds.has(entryId)) {\n throw new NavigationLayoutValidationError(`Navigation entry \"${entryId}\" cannot be both visible and hidden`)\n }\n }\n\n return { schemaVersion: NAVIGATION_LAYOUT_SCHEMA_VERSION, primary, hiddenEntryIds }\n}\n\nfunction normalizePrimaryItem(\n input: unknown,\n index: number,\n groupIds: Set<string>,\n visibleEntryIds: Set<string>\n): NavigationLayoutPrimaryItem {\n const item = requireRecord(input, `primary[${index}]`)\n if (item.kind === 'entry') {\n const entryId = requireId(item.entryId, `primary[${index}].entryId`)\n addUnique(visibleEntryIds, entryId, 'navigation entry')\n return { kind: 'entry', entryId }\n }\n if (item.kind === 'group') {\n const id = requireId(item.id, `primary[${index}].id`)\n addUnique(groupIds, id, 'navigation group')\n if (item.title !== undefined && (typeof item.title !== 'string' || item.title.trim().length === 0)) {\n throw new NavigationLayoutValidationError(`primary[${index}].title must be a non-empty string when provided`)\n }\n if (!Array.isArray(item.children)) {\n throw new NavigationLayoutValidationError(`primary[${index}].children must be an array`)\n }\n const children = normalizeUniqueIds(item.children, `primary[${index}].children`)\n for (const entryId of children) addUnique(visibleEntryIds, entryId, 'navigation entry')\n return {\n kind: 'group',\n id,\n ...(typeof item.title === 'string' && { title: item.title }),\n children,\n }\n }\n throw new NavigationLayoutValidationError(`primary[${index}].kind must be \"entry\" or \"group\"`)\n}\n\nfunction normalizeUniqueIds(input: unknown[], field: string): string[] {\n const ids = new Set<string>()\n return input.map((value, index) => {\n const id = requireId(value, `${field}[${index}]`)\n addUnique(ids, id, field)\n return id\n })\n}\n\nfunction requireRecord(input: unknown, field: string): Record<string, unknown> {\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n throw new NavigationLayoutValidationError(`${field} must be an object`)\n }\n return input as Record<string, unknown>\n}\n\nfunction requireId(input: unknown, field: string): string {\n if (typeof input !== 'string' || input.trim().length === 0) {\n throw new NavigationLayoutValidationError(`${field} must be a non-empty string`)\n }\n return input\n}\n\nfunction addUnique(ids: Set<string>, id: string, field: string): void {\n if (ids.has(id)) throw new NavigationLayoutValidationError(`Duplicate ${field} \"${id}\"`)\n ids.add(id)\n}\n","/**\n * Server-side merge session cache.\n *\n * Manages temporary databases for the multi-file merge workflow.\n * Each uploaded/parsed file gets a temp DB handle that persists\n * across HTTP requests until merge completes or cache is cleared.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as crypto from 'crypto'\nimport type { PathProvider, DatabaseAdapter } from '@openchatlab/core'\nimport { openBetterSqliteDatabase } from '../better-sqlite3-adapter'\nimport { TempDbReader, deleteTempDatabase, cleanupTempDatabases } from '../merger/temp-db'\n\ninterface CacheEntry {\n tempDbPath: string\n filename: string\n createdAt: number\n}\n\nexport class MergeSessionCache {\n private cache = new Map<string, CacheEntry>()\n private tempDir: string\n private nativeBinding?: string\n\n constructor(pathProvider: PathProvider, options?: { nativeBinding?: string }) {\n this.tempDir = pathProvider.getTempDir()\n this.nativeBinding = options?.nativeBinding\n if (!fs.existsSync(this.tempDir)) {\n fs.mkdirSync(this.tempDir, { recursive: true })\n }\n }\n\n generateTempDbPath(filename: string): string {\n const ts = Date.now()\n const rand = Math.random().toString(36).slice(2, 8)\n const safeName = filename.replace(/[/\\\\?%*:|\"<>]/g, '_').slice(0, 40)\n return path.join(this.tempDir, `merge_${safeName}_${ts}_${rand}.db`)\n }\n\n /** Store a parsed file's temp DB and return a handle for subsequent requests. */\n store(filename: string, tempDbPath: string): string {\n const handle = crypto.randomUUID()\n this.cache.set(handle, { tempDbPath, filename, createdAt: Date.now() })\n return handle\n }\n\n /** Open a TempDbReader for a given handle. Caller must close the reader. */\n openReader(handle: string): { reader: TempDbReader; filename: string } | null {\n const entry = this.cache.get(handle)\n if (!entry || !fs.existsSync(entry.tempDbPath)) return null\n const adapter = openBetterSqliteDatabase(entry.tempDbPath, {\n readonly: true,\n nativeBinding: this.nativeBinding,\n })\n return { reader: new TempDbReader(adapter), filename: entry.filename }\n }\n\n /** Create a writable temp DB for streaming parse. */\n createTempDatabase(filename: string): { db: DatabaseAdapter; tempDbPath: string } {\n const tempDbPath = this.generateTempDbPath(filename)\n const adapter = openBetterSqliteDatabase(tempDbPath, { nativeBinding: this.nativeBinding })\n return { db: adapter, tempDbPath }\n }\n\n /** Remove a single entry and its temp DB file. */\n delete(handle: string): void {\n const entry = this.cache.get(handle)\n if (entry) {\n deleteTempDatabase(entry.tempDbPath)\n this.cache.delete(handle)\n }\n }\n\n /** Clear all entries and delete their temp DB files. */\n clear(): void {\n for (const entry of this.cache.values()) {\n deleteTempDatabase(entry.tempDbPath)\n }\n this.cache.clear()\n }\n\n /** Cleanup orphan temp DBs from previous runs. */\n cleanupOrphans(): void {\n cleanupTempDatabases(this.tempDir)\n }\n\n /** Auto-cleanup entries older than maxAge (default 1 hour). */\n cleanupExpired(maxAgeMs = 3600_000): void {\n const now = Date.now()\n for (const [handle, entry] of this.cache) {\n if (now - entry.createdAt > maxAgeMs) {\n deleteTempDatabase(entry.tempDbPath)\n this.cache.delete(handle)\n }\n }\n }\n}\n","/**\n * Push import service — handles POST /api/v1/imports/:sessionId\n *\n * Accepts a ChatLab Format JSON payload, creates or appends to a session.\n * Dedup: platformMessageId (preferred) or deterministic fallback key.\n */\n\nimport * as fs from 'fs'\nimport { DataDirCompatibilityError } from '../data-dir-compat'\nimport {\n CHAT_DB_SCHEMA,\n generateSessionIndex,\n generateIncrementalSessionIndex,\n getSessionIndexStats,\n} from '@openchatlab/core'\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport type { DatabaseManager } from '../database-manager'\nimport { writeParseResultToDb } from '../import'\nimport { ImportInProgressError, withDataDirImportLock } from '../import/import-lock'\nimport {\n createMessageDedupState,\n generateFallbackMessageKey,\n registerMessageAndCheckDuplicate,\n type MessageDedupState,\n} from '../import/message-deduplicator'\nimport { appLogger } from '../logging/app-logger'\nimport { isValidImportSessionId } from '../import/session-id'\n\nconst SYSTEM_SENDER_ID = 'SYSTEM'\nconst SYSTEM_MEMBER_NAME = '系统消息'\n\nexport interface PushImportMessage {\n sender: string\n timestamp: number\n type: number\n accountName?: string\n groupNickname?: string\n content?: string | null\n platformMessageId?: string\n replyToMessageId?: string\n}\n\nexport interface PushImportMember {\n platformId: string\n accountName?: string\n groupNickname?: string\n avatar?: string\n roles?: Array<{ id: string }>\n}\n\nexport interface PushImportMeta {\n name: string\n platform: string\n type: string\n groupId?: string\n groupAvatar?: string\n ownerId?: string\n}\n\nexport interface PushImportPayload {\n chatlab?: { version: string; exportedAt: number; generator?: string }\n meta?: PushImportMeta\n members?: PushImportMember[]\n messages?: PushImportMessage[]\n options?: {\n metaUpdateMode?: 'patch' | 'none'\n memberUpdateMode?: 'upsert' | 'none'\n }\n}\n\nexport interface PushImportResult {\n sessionId: string\n created: boolean\n batch: { receivedCount: number; writtenCount: number; duplicateCount: number }\n session: { totalCount: number; memberCount: number; firstTimestamp: number | null; lastTimestamp: number | null }\n updates: { metaUpdated: boolean; membersAdded: number; membersUpdated: number }\n}\n\nexport type PushImportOutcome =\n | { ok: true; result: PushImportResult }\n | { ok: false; reason: 'import_in_progress' | 'invalid_payload' | 'import_failed'; message: string }\n\ninterface PushImportAnalysisResultBase {\n sessionId: string\n totalInFile: number\n newMessageCount: number\n duplicateCount: number\n}\n\nexport type PushImportAnalysisResult = PushImportAnalysisResultBase &\n ({ created: true; newMemberCount: number } | { created: false })\n\nexport type PushImportAnalysisOutcome =\n | { ok: true; result: PushImportAnalysisResult }\n | { ok: false; reason: 'invalid_payload' | 'import_failed'; message: string }\n\nexport interface PushImportExecutionDeps {\n getDbPath(sessionId: string): string\n openDatabase(sessionId: string, options: { readonly?: boolean; create?: boolean }): DatabaseAdapter\n deleteDatabase(sessionId: string): void\n}\n\nexport type PushImportAnalysisExecutionDeps = Pick<PushImportExecutionDeps, 'getDbPath' | 'openDatabase'>\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction validateOptionalString(value: unknown, field: string): string | null {\n return value !== undefined && typeof value !== 'string' ? `${field} must be a string` : null\n}\n\nfunction validatePayload(payload: PushImportPayload, isNew: boolean): string | null {\n if (!isRecord(payload)) return 'payload must be an object'\n\n const messages = payload.messages\n if (!Array.isArray(messages) || messages.length === 0) return 'messages is required and must be a non-empty array'\n\n if (payload.chatlab !== undefined) {\n if (!isRecord(payload.chatlab)) return 'chatlab must be an object'\n if (typeof payload.chatlab.version !== 'string' || payload.chatlab.version.length === 0)\n return 'chatlab.version must be a string'\n if (typeof payload.chatlab.exportedAt !== 'number' || !Number.isFinite(payload.chatlab.exportedAt))\n return 'chatlab.exportedAt must be a number'\n const generatorError = validateOptionalString(payload.chatlab.generator, 'chatlab.generator')\n if (generatorError) return generatorError\n }\n\n if (isNew) {\n if (!payload.chatlab) return 'chatlab is required for new sessions'\n if (!payload.meta) return 'meta is required for new sessions'\n }\n\n if (payload.meta !== undefined) {\n if (!isRecord(payload.meta)) return 'meta must be an object'\n const metaFields = ['name', 'platform', 'type', 'groupId', 'groupAvatar', 'ownerId'] as const\n for (const field of metaFields) {\n const error = validateOptionalString(payload.meta[field], `meta.${field}`)\n if (error) return error\n }\n if (isNew && !payload.meta.name) return 'meta.name is required'\n if (isNew && !payload.meta.platform) return 'meta.platform is required'\n if (isNew && !payload.meta.type) return 'meta.type is required'\n }\n\n if (payload.options !== undefined && !isRecord(payload.options)) return 'options must be an object'\n const { metaUpdateMode, memberUpdateMode } = payload.options ?? {}\n if (metaUpdateMode !== undefined && metaUpdateMode !== 'patch' && metaUpdateMode !== 'none') {\n return `options.metaUpdateMode must be 'patch' or 'none'`\n }\n if (memberUpdateMode !== undefined && memberUpdateMode !== 'upsert' && memberUpdateMode !== 'none') {\n return `options.memberUpdateMode must be 'upsert' or 'none'`\n }\n\n if (payload.members !== undefined && !Array.isArray(payload.members)) return 'members must be an array'\n if (payload.members) {\n for (let i = 0; i < payload.members.length; i++) {\n const member = payload.members[i]\n if (!isRecord(member)) return `members[${i}] must be an object`\n if (typeof member.platformId !== 'string' || member.platformId.length === 0)\n return `members[${i}].platformId must be a string`\n for (const field of ['accountName', 'groupNickname', 'avatar'] as const) {\n const error = validateOptionalString(member[field], `members[${i}].${field}`)\n if (error) return error\n }\n if (member.roles !== undefined) {\n if (!Array.isArray(member.roles)) return `members[${i}].roles must be an array`\n for (let j = 0; j < member.roles.length; j++) {\n const role = member.roles[j]\n if (!isRecord(role) || typeof role.id !== 'string' || role.id.length === 0)\n return `members[${i}].roles[${j}].id must be a string`\n }\n }\n }\n }\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n if (!isRecord(msg)) return `messages[${i}] must be an object`\n if (typeof msg.sender !== 'string' || msg.sender.length === 0) return `messages[${i}].sender must be a string`\n if (typeof msg.timestamp !== 'number' || !Number.isFinite(msg.timestamp) || msg.timestamp <= 0)\n return `messages[${i}].timestamp must be a positive number`\n if (typeof msg.type !== 'number' || !Number.isFinite(msg.type)) return `messages[${i}].type must be a number`\n for (const field of ['accountName', 'groupNickname'] as const) {\n const error = validateOptionalString(msg[field], `messages[${i}].${field}`)\n if (error) return error\n }\n if (msg.content !== undefined && msg.content !== null && typeof msg.content !== 'string')\n return `messages[${i}].content must be a string or null`\n if (msg.platformMessageId !== undefined && typeof msg.platformMessageId !== 'string')\n return `messages[${i}].platformMessageId must be a string`\n if (msg.replyToMessageId !== undefined && typeof msg.replyToMessageId !== 'string')\n return `messages[${i}].replyToMessageId must be a string`\n }\n\n return null\n}\n\nfunction queryStats(db: DatabaseAdapter): PushImportResult['session'] {\n const row = db.prepare('SELECT COUNT(*) as total, MIN(ts) as first, MAX(ts) as last FROM message').get() as {\n total: number\n first: number | null\n last: number | null\n }\n const memberRow = db\n .prepare(`SELECT COUNT(*) as cnt FROM member WHERE COALESCE(account_name, '') != ?`)\n .get(SYSTEM_MEMBER_NAME) as { cnt: number }\n return { totalCount: row.total, memberCount: memberRow.cnt, firstTimestamp: row.first, lastTimestamp: row.last }\n}\n\nfunction normalizeSenderAccountName(sender: string, accountName: string | undefined): string | undefined {\n return sender === SYSTEM_SENDER_ID ? SYSTEM_MEMBER_NAME : accountName\n}\n\nfunction normalizeSenderGroupNickname(sender: string, groupNickname: string | undefined): string | undefined {\n return sender === SYSTEM_SENDER_ID ? SYSTEM_MEMBER_NAME : groupNickname\n}\n\nfunction isCountedMember(platformId: string): boolean {\n return platformId !== SYSTEM_SENDER_ID\n}\n\nfunction registerPushMessageAndCheckDuplicate(message: PushImportMessage, dedupState: MessageDedupState): boolean {\n return registerMessageAndCheckDuplicate(\n {\n platformMessageId: message.platformMessageId,\n timestamp: message.timestamp,\n senderPlatformId: message.sender,\n type: message.type,\n content: message.content ?? null,\n replyToMessageId: message.replyToMessageId,\n },\n dedupState\n )\n}\n\nfunction createExistingMessageDedupState(db: DatabaseAdapter): MessageDedupState {\n const existingPmids = new Set<string>()\n const existingKeys = new Set<string>()\n const existingFallbackOnlyKeys = new Set<string>()\n const existingMessages = db\n .prepare(\n `SELECT msg.ts, m.platform_id, msg.type, msg.content, msg.reply_to_message_id, msg.platform_message_id\n FROM message msg\n JOIN member m ON msg.sender_id = m.id`\n )\n .all() as Array<{\n ts: number\n platform_id: string\n type: number\n content: string | null\n reply_to_message_id: string | null\n platform_message_id: string | null\n }>\n\n for (const message of existingMessages) {\n if (message.platform_message_id) existingPmids.add(message.platform_message_id)\n const key = generateFallbackMessageKey({\n timestamp: message.ts,\n senderPlatformId: message.platform_id,\n type: message.type,\n content: message.content,\n replyToMessageId: message.reply_to_message_id ?? undefined,\n })\n existingKeys.add(key)\n if (!message.platform_message_id) existingFallbackOnlyKeys.add(key)\n }\n\n return createMessageDedupState(existingPmids, existingKeys, existingFallbackOnlyKeys)\n}\n\nfunction analyzePushMessages(\n messages: PushImportMessage[],\n dedupState: MessageDedupState,\n sortByTimestamp: boolean\n): { newMessageCount: number; duplicateCount: number } {\n const orderedMessages = sortByTimestamp ? [...messages].sort((a, b) => a.timestamp - b.timestamp) : messages\n let newMessageCount = 0\n let duplicateCount = 0\n\n for (const message of orderedMessages) {\n if (registerPushMessageAndCheckDuplicate(message, dedupState)) {\n duplicateCount++\n continue\n }\n newMessageCount++\n }\n\n return { newMessageCount, duplicateCount }\n}\n\nfunction writeMessages(\n db: DatabaseAdapter,\n messages: PushImportMessage[],\n dedupState: MessageDedupState\n): {\n writtenCount: number\n duplicateCount: number\n membersAdded: number\n minWrittenTs: number\n} {\n const insertMsg = db.prepare(\n `INSERT INTO message (sender_id, sender_account_name, sender_group_nickname, ts, type, content, reply_to_message_id, platform_message_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n const getMemberId = db.prepare('SELECT id FROM member WHERE platform_id = ?')\n const insertMinimalMember = db.prepare(\n 'INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname) VALUES (?, ?, ?)'\n )\n\n const memberIdCache = new Map<string, number>()\n let writtenCount = 0\n let duplicateCount = 0\n let membersAdded = 0\n let minWrittenTs = Infinity\n\n const sorted = [...messages].sort((a, b) => a.timestamp - b.timestamp)\n\n db.transaction(() => {\n for (const msg of sorted) {\n if (registerPushMessageAndCheckDuplicate(msg, dedupState)) {\n duplicateCount++\n continue\n }\n\n let memberId = memberIdCache.get(msg.sender)\n if (!memberId) {\n const insertResult = insertMinimalMember.run(\n msg.sender,\n normalizeSenderAccountName(msg.sender, msg.accountName) || null,\n normalizeSenderGroupNickname(msg.sender, msg.groupNickname) || null\n )\n const row = getMemberId.get(msg.sender) as { id: number } | undefined\n if (row) {\n memberId = row.id\n memberIdCache.set(msg.sender, memberId)\n if (isCountedMember(msg.sender)) membersAdded += insertResult.changes\n }\n }\n if (!memberId) continue\n\n insertMsg.run(\n memberId,\n normalizeSenderAccountName(msg.sender, msg.accountName) || null,\n normalizeSenderGroupNickname(msg.sender, msg.groupNickname) || null,\n msg.timestamp,\n msg.type,\n msg.content ?? null,\n msg.replyToMessageId || null,\n msg.platformMessageId || null\n )\n if (msg.timestamp < minWrittenTs) minWrittenTs = msg.timestamp\n writtenCount++\n }\n })\n\n return { writtenCount, duplicateCount, membersAdded, minWrittenTs }\n}\n\nfunction fullImport(\n db: DatabaseAdapter,\n meta: PushImportMeta,\n members: PushImportMember[],\n messages: PushImportMessage[]\n): { writtenCount: number; duplicateCount: number } {\n db.exec(CHAT_DB_SCHEMA)\n\n // Auto-create minimal member entries for senders not listed in members,\n // preserving the protocol promise that unknown senders are auto-created.\n const knownIds = new Set(members.map((m) => m.platformId))\n const extraMembers: PushImportMember[] = []\n for (const msg of messages) {\n if (!knownIds.has(msg.sender)) {\n extraMembers.push({\n platformId: msg.sender,\n accountName: normalizeSenderAccountName(msg.sender, msg.accountName),\n groupNickname: normalizeSenderGroupNickname(msg.sender, msg.groupNickname),\n })\n knownIds.add(msg.sender)\n }\n }\n const allMembers = extraMembers.length > 0 ? [...members, ...extraMembers] : members\n\n // Deduplicate within the batch using the same pmid/hash logic as incremental imports.\n const dedupState = createMessageDedupState()\n let duplicateCount = 0\n const dedupedMessages: PushImportMessage[] = []\n for (const msg of messages) {\n if (registerPushMessageAndCheckDuplicate(msg, dedupState)) {\n duplicateCount++\n continue\n }\n dedupedMessages.push(msg)\n }\n\n // Delegate to the shared writer so member_name_history is tracked correctly,\n // matching the behaviour of file-based full imports.\n const stats = writeParseResultToDb(\n db,\n meta,\n allMembers.map((m) => ({\n ...m,\n accountName: normalizeSenderAccountName(m.platformId, m.accountName) ?? m.platformId,\n groupNickname: normalizeSenderGroupNickname(m.platformId, m.groupNickname),\n })),\n dedupedMessages.map((m) => ({\n senderPlatformId: m.sender,\n senderAccountName: normalizeSenderAccountName(m.sender, m.accountName) ?? m.sender,\n senderGroupNickname: normalizeSenderGroupNickname(m.sender, m.groupNickname),\n timestamp: m.timestamp,\n type: m.type,\n content: m.content ?? null,\n platformMessageId: m.platformMessageId,\n replyToMessageId: m.replyToMessageId,\n }))\n )\n\n try {\n generateSessionIndex(db)\n } catch {\n /* non-fatal */\n }\n\n return { writtenCount: stats.messageCount, duplicateCount }\n}\n\ninterface IncrementalImportStats {\n writtenCount: number\n duplicateCount: number\n metaUpdated: boolean\n membersAdded: number\n membersUpdated: number\n}\n\nfunction writeIncrementalImport(db: DatabaseAdapter, payload: PushImportPayload): IncrementalImportStats {\n const metaUpdateMode = payload.options?.metaUpdateMode ?? 'patch'\n const memberUpdateMode = payload.options?.memberUpdateMode ?? 'upsert'\n\n const dedupState = createExistingMessageDedupState(db)\n\n let metaUpdated = false\n let membersAdded = 0\n let membersUpdated = 0\n\n if (payload.meta && metaUpdateMode === 'patch') {\n const m = payload.meta\n db.prepare(\n `UPDATE meta SET name = COALESCE(NULLIF(?, ''), name), group_id = COALESCE(NULLIF(?, ''), group_id), group_avatar = COALESCE(NULLIF(?, ''), group_avatar), owner_id = COALESCE(NULLIF(?, ''), owner_id), imported_at = ?`\n ).run(m.name || '', m.groupId || '', m.groupAvatar || '', m.ownerId || '', Math.floor(Date.now() / 1000))\n metaUpdated = true\n }\n\n if (payload.members && memberUpdateMode === 'upsert') {\n const upsertMember = db.prepare(\n `INSERT INTO member (platform_id, account_name, group_nickname, avatar, roles) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(platform_id) DO UPDATE SET\n account_name = COALESCE(NULLIF(excluded.account_name, ''), account_name),\n group_nickname = COALESCE(NULLIF(excluded.group_nickname, ''), group_nickname),\n avatar = COALESCE(NULLIF(excluded.avatar, ''), avatar),\n roles = CASE WHEN excluded.roles != '[]' THEN excluded.roles ELSE roles END`\n )\n const getMemberId = db.prepare('SELECT id FROM member WHERE platform_id = ?')\n const existingMemberIds = new Set(\n (db.prepare('SELECT platform_id FROM member').all() as Array<{ platform_id: string }>).map((r) => r.platform_id)\n )\n\n db.transaction(() => {\n for (const m of payload.members!) {\n const existed = existingMemberIds.has(m.platformId)\n const accountName = normalizeSenderAccountName(m.platformId, m.accountName)\n const groupNickname = normalizeSenderGroupNickname(m.platformId, m.groupNickname)\n upsertMember.run(\n m.platformId,\n accountName || null,\n groupNickname || null,\n m.avatar || null,\n m.roles ? JSON.stringify(m.roles) : '[]'\n )\n if (!existed) {\n if (isCountedMember(m.platformId)) membersAdded++\n const row = getMemberId.get(m.platformId) as { id: number } | undefined\n if (row) existingMemberIds.add(m.platformId)\n } else if (isCountedMember(m.platformId)) {\n membersUpdated++\n }\n }\n })\n }\n\n // Capture existing max timestamp before writing to detect backfill batches.\n const preWriteMaxTs =\n (db.prepare('SELECT MAX(ts) as max_ts FROM message').get() as { max_ts: number | null })?.max_ts ?? 0\n\n const {\n writtenCount,\n duplicateCount,\n membersAdded: autoCreatedMembersAdded,\n minWrittenTs,\n } = writeMessages(db, payload.messages!, dedupState)\n\n if (writtenCount > 0) {\n try {\n const sessionGapThreshold = getSessionIndexStats(db).gapThreshold\n // Use minWrittenTs (not payload min) to avoid false-positive backfill detection\n // when overlap duplicates in the batch have older timestamps than written rows.\n if (minWrittenTs < preWriteMaxTs) {\n generateSessionIndex(db, sessionGapThreshold)\n } else {\n generateIncrementalSessionIndex(db, sessionGapThreshold)\n }\n } catch {\n /* non-fatal */\n }\n }\n\n if (!metaUpdated) {\n db.prepare('UPDATE meta SET imported_at = ?').run(Math.floor(Date.now() / 1000))\n }\n\n return {\n writtenCount,\n duplicateCount,\n metaUpdated,\n membersAdded: membersAdded + autoCreatedMembersAdded,\n membersUpdated,\n }\n}\n\nfunction incrementalImport(db: DatabaseAdapter, payload: PushImportPayload): IncrementalImportStats {\n return db.transaction(() => writeIncrementalImport(db, payload))\n}\n\nfunction countNewSessionMembers(payload: PushImportPayload): number {\n const memberIds = new Set<string>()\n for (const member of payload.members ?? []) {\n if (isCountedMember(member.platformId)) memberIds.add(member.platformId)\n }\n for (const message of payload.messages ?? []) {\n if (isCountedMember(message.sender)) memberIds.add(message.sender)\n }\n return memberIds.size\n}\n\n/**\n * 只读分析 JSON Push 请求。新会话按首次导入的批内去重顺序计算;已有会话则加载\n * 当前消息去重键,并按增量写入的时间顺序计算,确保 dry-run 与正式写入共享同一语义。\n */\nexport async function executeAnalyzePushImport(\n deps: PushImportAnalysisExecutionDeps,\n sessionId: string,\n payload: PushImportPayload\n): Promise<PushImportAnalysisOutcome> {\n if (!isValidImportSessionId(sessionId)) {\n return { ok: false, reason: 'invalid_payload', message: 'sessionId contains invalid characters' }\n }\n\n const isNew = !fs.existsSync(deps.getDbPath(sessionId))\n const validationError = validatePayload(payload, isNew)\n if (validationError) {\n return { ok: false, reason: 'invalid_payload', message: validationError }\n }\n\n try {\n let analysis: { newMessageCount: number; duplicateCount: number }\n\n if (isNew) {\n analysis = analyzePushMessages(payload.messages!, createMessageDedupState(), false)\n } else {\n const db = deps.openDatabase(sessionId, { readonly: true })\n try {\n analysis = analyzePushMessages(payload.messages!, createExistingMessageDedupState(db), true)\n } finally {\n db.close()\n }\n }\n\n const baseResult: PushImportAnalysisResultBase = {\n sessionId,\n totalInFile: payload.messages!.length,\n ...analysis,\n }\n const result: PushImportAnalysisResult = isNew\n ? { ...baseResult, created: true, newMemberCount: countNewSessionMembers(payload) }\n : { ...baseResult, created: false }\n appLogger.info('push-import', 'Push import analysis completed', result)\n return { ok: true, result }\n } catch (err: unknown) {\n if (err instanceof DataDirCompatibilityError) throw err\n appLogger.error('push-import', 'Push import analysis failed', err)\n return { ok: false, reason: 'import_failed', message: err instanceof Error ? err.message : String(err) }\n }\n}\n\nexport async function pushImport(\n dbManager: DatabaseManager,\n sessionId: string,\n payload: PushImportPayload\n): Promise<PushImportOutcome> {\n try {\n return await withDataDirImportLock(dbManager.getUserDataDir(), async () => {\n const outcome = await executePushImportUnlocked(\n {\n getDbPath: (id) => dbManager.getDbPath(id),\n openDatabase: (id, options) => dbManager.openRawSessionDatabase(id, options),\n deleteDatabase: (id) => {\n dbManager.deleteSessionDatabaseFiles(id)\n },\n },\n sessionId,\n payload\n )\n if (!outcome.ok) return outcome\n\n try {\n dbManager.raiseCurrentChatDbCompatibilityGate()\n } catch (error) {\n if (outcome.result.created) dbManager.deleteSessionDatabaseFiles(sessionId)\n throw error\n }\n return outcome\n })\n } catch (error) {\n if (error instanceof ImportInProgressError) {\n return {\n ok: false,\n reason: 'import_in_progress',\n message: 'Another import is already in progress',\n }\n }\n throw error\n }\n}\n\n/**\n * 执行已加锁的 Push Import 写库流程。调用方必须负责数据目录导入锁和兼容门禁,\n * 这样 CLI/Internal 可使用 DatabaseManager,Desktop 则可安全地在 worker 中执行写库。\n */\nexport async function executePushImportUnlocked(\n deps: PushImportExecutionDeps,\n sessionId: string,\n payload: PushImportPayload\n): Promise<PushImportOutcome> {\n if (!isValidImportSessionId(sessionId)) {\n return { ok: false, reason: 'invalid_payload', message: 'sessionId contains invalid characters' }\n }\n\n const dbPath = deps.getDbPath(sessionId)\n const isNew = !fs.existsSync(dbPath)\n\n const validationError = validatePayload(payload, isNew)\n if (validationError) {\n return { ok: false, reason: 'invalid_payload', message: validationError }\n }\n\n try {\n if (isNew) {\n const db = deps.openDatabase(sessionId, { create: true })\n try {\n const { writtenCount, duplicateCount } = fullImport(db, payload.meta!, payload.members ?? [], payload.messages!)\n const session = queryStats(db)\n // Every non-system member in a new database was inserted by this request,\n // including senders that were omitted from the submitted members array.\n const membersAdded = session.memberCount\n const outcome: PushImportOutcome = {\n ok: true,\n result: {\n sessionId,\n created: true,\n batch: { receivedCount: payload.messages!.length, writtenCount, duplicateCount },\n session,\n updates: { metaUpdated: true, membersAdded, membersUpdated: 0 },\n },\n }\n appLogger.info('push-import', 'Push import completed', outcome.result)\n return outcome\n } finally {\n db.close()\n }\n }\n\n const db = deps.openDatabase(sessionId, { readonly: false })\n try {\n const { writtenCount, duplicateCount, metaUpdated, membersAdded, membersUpdated } = incrementalImport(db, payload)\n const session = queryStats(db)\n const outcome: PushImportOutcome = {\n ok: true,\n result: {\n sessionId,\n created: false,\n batch: { receivedCount: payload.messages!.length, writtenCount, duplicateCount },\n session,\n updates: { metaUpdated, membersAdded, membersUpdated },\n },\n }\n appLogger.info('push-import', 'Push import completed', outcome.result)\n return outcome\n } finally {\n db.close()\n }\n } catch (err: unknown) {\n // Let DataDirCompatibilityError propagate so the Fastify error handler\n // maps it to 409 DATA_DIR_INCOMPATIBLE (consistent with other routes).\n if (err instanceof DataDirCompatibilityError) throw err\n\n if (isNew) {\n try {\n deps.deleteDatabase(sessionId)\n } catch {\n /* cleanup best-effort */\n }\n }\n appLogger.error('push-import', 'Push import failed', err)\n return { ok: false, reason: 'import_failed', message: err instanceof Error ? err.message : String(err) }\n }\n}\n","import { createHash } from 'node:crypto'\n\nexport const DEFAULT_IMPORT_IDEMPOTENCY_TTL_MS = 60 * 60 * 1000\n\ninterface ImportIdempotencyEntry<T> {\n bodyHash: string\n status: 'pending' | 'success'\n response?: T\n timestamp: number\n}\n\nexport type ImportIdempotencyStartResult<T> =\n | { status: 'started' }\n | { status: 'conflict' }\n | { status: 'pending' }\n | { status: 'success'; response: T }\n\nexport function hashImportBody(body: unknown): string {\n return createHash('sha256')\n .update(JSON.stringify(body ?? ''))\n .digest('hex')\n}\n\n/**\n * 进程内的请求级幂等缓存。调用方先 start,再在业务完成后 success/fail,\n * 确保 Desktop、CLI 和 Internal Server 使用同一套冲突与重试语义。\n */\nexport class ImportIdempotencyCache<T> {\n private entries = new Map<string, ImportIdempotencyEntry<T>>()\n\n constructor(private ttlMs = DEFAULT_IMPORT_IDEMPOTENCY_TTL_MS) {}\n\n start(key: string, bodyHash: string, now = Date.now()): ImportIdempotencyStartResult<T> {\n this.prune(now)\n const entry = this.entries.get(key)\n if (!entry) {\n this.entries.set(key, { bodyHash, status: 'pending', timestamp: now })\n return { status: 'started' }\n }\n if (entry.bodyHash !== bodyHash) return { status: 'conflict' }\n if (entry.status === 'pending') return { status: 'pending' }\n return { status: 'success', response: entry.response as T }\n }\n\n success(key: string, response: T): void {\n const entry = this.entries.get(key)\n if (!entry) return\n entry.status = 'success'\n entry.response = response\n }\n\n fail(key: string): void {\n this.entries.delete(key)\n }\n\n private prune(now: number): void {\n for (const [key, entry] of this.entries) {\n if (now - entry.timestamp > this.ttlMs) this.entries.delete(key)\n }\n }\n}\n"],"mappings":"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AY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MACpF;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AAAA,MAAY;AA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","names":["options","options","options","path","options","result","options","fns","chain","options","options","Assembler","options","path","Assembler","options","options","require_stream_chain","result","options","fns","fs","buffer","options","fs","openPromise","path","options","resolve","err","options","fs","path","fs","path","feature","parser","fs","fs","path","options","parser","fallbackAfterNativeFailure","resolve","streamJson","pickModule","streamValuesModule","streamChain","basename","options","resolve","pipeline","fs","path","extractNameFromFilePath","basename","feature","options","parser","module_","import_stream_json","import_Pick","import_StreamValues","import_stream_chain","fs","path","import_stream_json","import_Pick","import_StreamValues","import_stream_chain","fs","path","os","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","chain","streamChain","resolve","pipeline","options","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","chain","streamChain","feature","extractJsonObject","extractNameFromFilePath","basename","options","resolve","pipeline","parser_","feature","module_","feature","parser_","fs","path","readline","feature","convertMessageType","options","parser_","module_","import_stream_json","import_Pick","import_StreamValues","import_stream_chain","fs","path","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","chain","streamChain","extractNameFromFilePath","basename","feature","convertMessageType","options","resolve","pipeline","parser_","feature","module_","feature","parser_","feature","parser_","module_","feature","parser_","import_stream_json","import_Pick","import_StreamValues","import_stream_chain","fs","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","chain","streamChain","feature","parseTimestamp","options","resolve","pipeline","parser_","module_","import_stream_chain","import_stream_json","import_Pick","import_StreamValues","fs","chain","streamChain","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","feature","resolve","pipeline","options","parser_","module_","import_stream_json","fs","parser","streamJson","feature","options","Assembler","parser_","module_","fs","readline","detectMessageType","basename","feature","options","parser_","module_","fs","readline","extractNameFromFileName","detectMessageType","getBasename","feature","options","parser_","module_","fs","path","feature","extractNameFromFilePath","basename","SYSTEM_MESSAGE_PATTERNS","isSystemMessage","detectMessageType","options","parser_","module_","import_stream_chain","import_stream_json","import_Pick","import_StreamValues","fs","path","chain","streamChain","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","feature","relative","readManifest","detectMessageType","options","pipeline","parser_","module_","fs","options","extractNameFromFileName","buildTimestamp","detectMessageType","resolve","basename","getBasename","hours","minutes","feature","parseLine","options","parser_","module_","createReadStream","extname","createInterface","path","createReadStream","createInterface","extname","actual","isRecord","feature","options","parser","feature","fs","path","LogLevel","fs","SYSTEM_SENDER_ID","resolve","feature","db","generateSessionIndex","options","options","trackError","meta","options","decision","result","analysis","options","resolve","options","fs","path","options","fs","path","options","import_stream_chain","import_stream_json","import_Pick","import_StreamValues","fs","path","chain","streamChain","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","pipeline","fs","path","randomUUID","options","resolver","randomUUID","fs","path","renameSession","updateSessionOwnerId","options","renameSession","updateSessionOwnerId","deleteMember","deleteMembers","getMemberNameHistory","getMembersPaginated","mergeMembers","updateMemberAliases","getMembersPaginated","updateMemberAliases","mergeMembers","deleteMember","deleteMembers","getMemberNameHistory","options","fs","path","path","fs","isRecord","fs","path","path","options","fs","existsSync","existsSync","options","resolve","options","fs","path","path","options","fs","existsSync","Worker","existsSync","normalizeWorkerEntryUrl","createWorker","Worker","options","resolve","createAbortError","requirePathProvider","createIdleTaskState","options","fs","existsSync","Worker","existsSync","options","resolve","createWorker","Worker","requirePathProvider","createIdleTaskState","options","fs","fs","requirePathProvider","createIdleTaskState","options","emptyCoverage","fs","fs","path","options","path","createHash","Buffer","options","resolve","options","Database","options","Database","resolve","createHash","sha256Hex","createHash","options","options","resolveApiKey","path","fs","createRequire","path","resolve","createRequire","path","options","fs","fs","path","fs","path","options","fs","SYSTEM_SENDER_ID","SYSTEM_MEMBER_NAME","isRecord","validateOptionalString","incrementalImport","options","db","incrementalImport","createHash"]}