chatlab-cli 0.26.3 → 0.27.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.
- package/README.md +10 -4
- package/dist/{chat-command-D37MMEJ7.mjs → chat-command-B7POB3C3.mjs} +6 -5
- package/dist/{chat-command-D37MMEJ7.mjs.map → chat-command-B7POB3C3.mjs.map} +1 -1
- package/dist/{chunk-K5SGXTVJ.mjs → chunk-3FUF3PEY.mjs} +21 -19
- package/dist/{chunk-K5SGXTVJ.mjs.map → chunk-3FUF3PEY.mjs.map} +1 -1
- package/dist/{chunk-742TAKEP.mjs → chunk-3JNVGW62.mjs} +2 -2
- package/dist/{chunk-VSOWNKPA.mjs → chunk-GFHSSCCR.mjs} +11 -1
- package/dist/{chunk-YPTJVTVJ.mjs → chunk-I7BHOZPX.mjs} +13159 -19038
- package/dist/chunk-I7BHOZPX.mjs.map +1 -0
- package/dist/chunk-N3G43SCQ.mjs +7337 -0
- package/dist/chunk-N3G43SCQ.mjs.map +1 -0
- package/dist/{chunk-GNGZHBKM.mjs → chunk-RJ4D4QWK.mjs} +42 -7
- package/dist/chunk-RJ4D4QWK.mjs.map +1 -0
- package/dist/{chunk-VCN64OD6.mjs → chunk-SAE2H2LN.mjs} +7 -5
- package/dist/{chunk-VCN64OD6.mjs.map → chunk-SAE2H2LN.mjs.map} +1 -1
- package/dist/{chunk-QMXNJRTT.mjs → chunk-SIVERPF5.mjs} +280 -104
- package/dist/chunk-SIVERPF5.mjs.map +1 -0
- package/dist/{chunk-RMITK4IC.mjs → chunk-WQMKKUSD.mjs} +468 -118
- package/dist/chunk-WQMKKUSD.mjs.map +1 -0
- package/dist/{chunk-D5MWER7N.mjs → chunk-YUFYTDRH.mjs} +6 -4
- package/dist/{chunk-D5MWER7N.mjs.map → chunk-YUFYTDRH.mjs.map} +1 -1
- package/dist/cli.mjs +5 -4
- package/dist/{http-6F6TYWCI.mjs → http-A54ZBBAY.mjs} +10 -9
- package/dist/{import-VYPOMSSA.mjs → import-EA3TB3S2.mjs} +6 -5
- package/dist/index.mjs +10 -9
- package/dist/{mcp-AR7L5FA3.mjs → mcp-DJDOWHH3.mjs} +11 -9
- package/dist/{mcp-AR7L5FA3.mjs.map → mcp-DJDOWHH3.mjs.map} +1 -1
- package/dist/onnxruntime_binding-6Q6HXASN.node +0 -0
- package/dist/onnxruntime_binding-EKZT2NRK.node +0 -0
- package/dist/onnxruntime_binding-P6S7V3CI.node +0 -0
- package/dist/onnxruntime_binding-PJNNIIUO.node +0 -0
- package/dist/onnxruntime_binding-UN6SPTQK.node +0 -0
- package/dist/semantic-index-worker.mjs +34 -0
- package/dist/semantic-index-worker.mjs.map +1 -0
- package/dist/{service-KZTHBDQ5.mjs → service-KGL3RAIA.mjs} +2 -2
- package/dist/{src-6S7JPKHX.mjs → src-EKPMJRG6.mjs} +65 -109
- package/dist/{stream-import-KZFC3XT2.mjs → stream-import-FQFNRQ2B.mjs} +5 -4
- package/dist/transformers.node-PUMHAODN.mjs +42889 -0
- package/dist/transformers.node-PUMHAODN.mjs.map +1 -0
- package/dist/{update-checker-3N7HQEP2.mjs → update-checker-LGMTEKMT.mjs} +2 -2
- package/dist/worker-runtime-UH2OBO3T.mjs +71 -0
- package/dist/worker-runtime-UH2OBO3T.mjs.map +1 -0
- package/dist-web/assets/{DebugToolsPanel-DzZmSzIU.css → DebugToolsPanel-DFw6_G5V.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-NL1sTF6i.js +110 -0
- package/dist-web/assets/{MessageExportModal-CF6yaor7.js → MessageExportModal-B60rvfCh.js} +1 -1
- package/dist-web/assets/{electron-DKVw61Ww.js → electron-BRUg9LnY.js} +1 -1
- package/dist-web/assets/{fetch-CUw-PUYP.js → fetch-BIpjJHxj.js} +1 -1
- package/dist-web/assets/{fetch-BMdgfBVS.js → fetch-BRsMdFcn.js} +1 -1
- package/dist-web/assets/{fetch-CPeSfqsU.js → fetch-C-m3IysH.js} +1 -1
- package/dist-web/assets/{fetch-CpYSD089.js → fetch-Cma-mZjd.js} +1 -1
- package/dist-web/assets/{fetch-D2jNBqs8.js → fetch-DE8gj5Ke.js} +1 -1
- package/dist-web/assets/{fetch-DqEMAea3.js → fetch-DLAp7h55.js} +1 -1
- package/dist-web/assets/{fetch-BvVfWR-P.js → fetch-DNSaxRnL.js} +1 -1
- package/dist-web/assets/{fetch-BC8jyJ_U.js → fetch-DOEbyLSD.js} +1 -1
- package/dist-web/assets/{fetch-BQlEQsqi.js → fetch-DSN-QYZE.js} +1 -1
- package/dist-web/assets/{fetch-Bh6HC-IR.js → fetch-woAa3H2L.js} +1 -1
- package/dist-web/assets/index-B3mDjury.js +11 -0
- package/dist-web/assets/{index-Qh0XLt3R.js → index-B9I-Y3fR.js} +1 -1
- package/dist-web/assets/{index-DqybGdJH.css → index-BX3tqfLz.css} +1 -1
- package/dist-web/assets/index-GAU7LI1r.js +173 -0
- package/dist-web/assets/index-I3SWZjnW.css +1 -0
- package/dist-web/assets/index-YmzTgkb2.css +1 -0
- package/dist-web/assets/index-iwqJDVMJ.js +2 -0
- package/dist-web/assets/index-o6veAyTB.js +64 -0
- package/dist-web/assets/{index-BHQnAj6l.js → index-r_zHR2t9.js} +1 -1
- package/dist-web/assets/{web-Bx2TiHDk.js → web-C2UtGskd.js} +1 -1
- package/dist-web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-GNGZHBKM.mjs.map +0 -1
- package/dist/chunk-QMXNJRTT.mjs.map +0 -1
- package/dist/chunk-RMITK4IC.mjs.map +0 -1
- package/dist/chunk-YPTJVTVJ.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BTdqmm0y.js +0 -107
- package/dist-web/assets/index-BwzuCu5R.js +0 -2
- package/dist-web/assets/index-CG746E15.js +0 -64
- package/dist-web/assets/index-CXoQ_U22.js +0 -173
- package/dist-web/assets/index-HgIuP1Td.css +0 -1
- package/dist-web/assets/index-R82N2_0b.css +0 -1
- package/dist-web/assets/index-wPAmuQZY.js +0 -11
- /package/dist/{chunk-742TAKEP.mjs.map → chunk-3JNVGW62.mjs.map} +0 -0
- /package/dist/{chunk-VSOWNKPA.mjs.map → chunk-GFHSSCCR.mjs.map} +0 -0
- /package/dist/{http-6F6TYWCI.mjs.map → http-A54ZBBAY.mjs.map} +0 -0
- /package/dist/{import-VYPOMSSA.mjs.map → import-EA3TB3S2.mjs.map} +0 -0
- /package/dist/{service-KZTHBDQ5.mjs.map → service-KGL3RAIA.mjs.map} +0 -0
- /package/dist/{src-6S7JPKHX.mjs.map → src-EKPMJRG6.mjs.map} +0 -0
- /package/dist/{stream-import-KZFC3XT2.mjs.map → stream-import-FQFNRQ2B.mjs.map} +0 -0
- /package/dist/{update-checker-3N7HQEP2.mjs.map → update-checker-LGMTEKMT.mjs.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/stream-json/utils/Utf8Stream.js","../../../node_modules/stream-json/Parser.js","../../../node_modules/stream-json/utils/emit.js","../../../node_modules/stream-json/index.js","../../../node_modules/stream-json/filters/FilterBase.js","../../../node_modules/stream-chain/index.js","../../../node_modules/stream-json/utils/withParser.js","../../../node_modules/stream-json/filters/Pick.js","../../../node_modules/stream-json/Assembler.js","../../../node_modules/stream-json/streamers/StreamBase.js","../../../node_modules/stream-json/streamers/StreamValues.js","../../../packages/node-runtime/src/import/write-parse-result.ts","../../../packages/node-runtime/src/import/perf-logger.ts","../../../packages/parser/src/index.ts","../../../packages/parser/src/sniffer.ts","../../../packages/parser/src/formats/chatlab.ts","../../../packages/parser/src/utils.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/formats/shuakami-qq-exporter-chunked.ts","../../../packages/parser/src/formats/weflow.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/formats/whatsapp-native-txt.ts","../../../packages/parser/src/formats/qq-native-txt.ts","../../../packages/parser/src/formats/instagram-native.ts","../../../packages/parser/src/formats/line-native-txt.ts","../../../packages/parser/src/formats/index.ts","../../../packages/node-runtime/src/import/streaming-importer.ts","../../../packages/node-runtime/src/import/incremental-importer.ts","../../../packages/node-runtime/src/node-path-provider.ts","../../../packages/node-runtime/src/data-dir-switch.ts","../../../packages/node-runtime/src/jieba-nlp-provider.ts","../../../packages/node-runtime/src/cache/analytics-cache.ts","../../../packages/node-runtime/src/preferences.ts","../../../packages/node-runtime/src/merger/index.ts","../../../packages/node-runtime/src/semantic-index/index.ts","../../../packages/node-runtime/src/semantic-index/worker-client.ts","../../../packages/node-runtime/src/semantic-index/worker-thread-transport.ts","../../../packages/node-runtime/src/semantic-index/embedding/index.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","/**\n * Shared data writing logic for importing parsed chat data into SQLite.\n *\n * Extracted from electron/main/database/core.ts importData().\n * Used by both Electron and Server import pipelines.\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport type { ParsedMember, ParsedMessage } from '@openchatlab/shared-types'\n\nexport interface ImportMeta {\n name: string\n platform: string\n type: string\n groupId?: string | null\n groupAvatar?: string | null\n ownerId?: string | null\n}\n\nexport interface WriteParseResultStats {\n messageCount: number\n memberCount: number\n skippedCount: number\n}\n\n/**\n * Write parsed chat data into a database that already has the schema initialized.\n *\n * Handles:\n * - Meta record insertion (with schema_version and imported_at)\n * - Member insertion with dedup (INSERT OR IGNORE)\n * - Messages sorted by timestamp, inserted with sender_id FK\n * - member_name_history tracking (account_name & group_nickname changes over time)\n * - Final member name update to latest seen values\n *\n * @param db - Database with CHAT_DB_SCHEMA already applied\n * @param meta - Chat session metadata\n * @param members - Parsed member list\n * @param messages - Parsed message list (will be sorted by timestamp internally)\n */\nexport function writeParseResultToDb(\n db: DatabaseAdapter,\n meta: ImportMeta,\n members: readonly ParsedMember[],\n messages: readonly ParsedMessage[]\n): WriteParseResultStats {\n let messageCount = 0\n let skippedCount = 0\n\n db.transaction(() => {\n db.prepare(\n `INSERT INTO meta (name, platform, type, imported_at, group_id, group_avatar, owner_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)`\n ).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\n const insertMember = db.prepare(\n `INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar, roles) VALUES (?, ?, ?, ?, ?)`\n )\n const getMemberId = db.prepare(`SELECT id FROM member WHERE platform_id = ?`)\n\n const memberIdMap = new Map<string, number>()\n\n for (const member of members) {\n insertMember.run(\n member.platformId,\n member.accountName || null,\n member.groupNickname || null,\n member.avatar || null,\n member.roles ? JSON.stringify(member.roles) : '[]'\n )\n const row = getMemberId.get(member.platformId) as { id: number }\n memberIdMap.set(member.platformId, row.id)\n }\n\n const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp)\n\n const accountNameTracker = new Map<string, { currentName: string; lastSeenTs: number }>()\n const groupNicknameTracker = new Map<string, { currentName: string; lastSeenTs: number }>()\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 const insertNameHistory = db.prepare(\n `INSERT INTO member_name_history (member_id, name_type, name, start_ts, end_ts) VALUES (?, ?, ?, ?, ?)`\n )\n const updateMemberAccountName = db.prepare(`UPDATE member SET account_name = ? WHERE platform_id = ?`)\n const updateMemberGroupNickname = db.prepare(`UPDATE member SET group_nickname = ? WHERE platform_id = ?`)\n const updateNameHistoryEndTs = db.prepare(\n `UPDATE member_name_history SET end_ts = ? WHERE member_id = ? AND name_type = ? AND end_ts IS NULL`\n )\n\n for (const msg of sortedMessages) {\n const senderId = memberIdMap.get(msg.senderPlatformId)\n if (senderId === undefined) {\n skippedCount++\n continue\n }\n\n insertMessage.run(\n senderId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\n msg.timestamp,\n msg.type,\n msg.content,\n msg.replyToMessageId || null,\n msg.platformMessageId || null\n )\n messageCount++\n\n const accountName = msg.senderAccountName\n if (accountName) {\n const tracker = accountNameTracker.get(msg.senderPlatformId)\n if (!tracker) {\n accountNameTracker.set(msg.senderPlatformId, {\n currentName: accountName,\n lastSeenTs: msg.timestamp,\n })\n insertNameHistory.run(senderId, 'account_name', accountName, msg.timestamp, null)\n } else if (tracker.currentName !== accountName) {\n updateNameHistoryEndTs.run(msg.timestamp, senderId, 'account_name')\n insertNameHistory.run(senderId, 'account_name', accountName, msg.timestamp, null)\n tracker.currentName = accountName\n tracker.lastSeenTs = msg.timestamp\n } else {\n tracker.lastSeenTs = msg.timestamp\n }\n }\n\n const groupNickname = msg.senderGroupNickname\n if (groupNickname) {\n const tracker = groupNicknameTracker.get(msg.senderPlatformId)\n if (!tracker) {\n groupNicknameTracker.set(msg.senderPlatformId, {\n currentName: groupNickname,\n lastSeenTs: msg.timestamp,\n })\n insertNameHistory.run(senderId, 'group_nickname', groupNickname, msg.timestamp, null)\n } else if (tracker.currentName !== groupNickname) {\n updateNameHistoryEndTs.run(msg.timestamp, senderId, 'group_nickname')\n insertNameHistory.run(senderId, 'group_nickname', groupNickname, msg.timestamp, null)\n tracker.currentName = groupNickname\n tracker.lastSeenTs = msg.timestamp\n } else {\n tracker.lastSeenTs = msg.timestamp\n }\n }\n }\n\n for (const [platformId, tracker] of accountNameTracker.entries()) {\n updateMemberAccountName.run(tracker.currentName, platformId)\n }\n for (const [platformId, tracker] of groupNicknameTracker.entries()) {\n updateMemberGroupNickname.run(tracker.currentName, platformId)\n }\n })\n\n return { messageCount, memberCount: members.length, skippedCount }\n}\n","/**\n * Import performance logger.\n *\n * Extracted from electron/main/worker/core/perfLogger.ts.\n * Records real-time performance metrics during import operations.\n *\n * Caller provides the log directory; this module has no path assumptions.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\n\nexport enum LogLevel {\n ERROR = 'ERROR',\n INFO = 'INFO',\n}\n\nlet lastLogTime = Date.now()\nlet lastMessageCount = 0\nlet currentLogFile: string | null = null\nlet errorCount = 0\n\nexport function initPerfLog(sessionId: string, logDir: string): void {\n try {\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true })\n }\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 // Ignore initialization failure\n }\n}\n\nexport function logPerf(event: string, messagesProcessed: number, batchSize?: number): void {\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\n let memory = 0\n try {\n const used = process.memoryUsage()\n memory = Math.round(used.heapUsed / 1024 / 1024)\n } catch {\n // Ignore\n }\n\n const logLine =\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 if (currentLogFile) {\n try {\n fs.appendFileSync(currentLogFile, logLine, 'utf-8')\n } catch {\n // Ignore write failure\n }\n }\n\n lastLogTime = now\n lastMessageCount = messagesProcessed\n}\n\nexport function logPerfDetail(detail: string): void {\n if (currentLogFile) {\n try {\n fs.appendFileSync(currentLogFile, ` ${detail}\\n`, 'utf-8')\n } catch {\n // Ignore\n }\n }\n}\n\nexport function resetPerfLog(): void {\n lastLogTime = Date.now()\n lastMessageCount = 0\n currentLogFile = null\n errorCount = 0\n}\n\nexport function getCurrentLogFile(): string | null {\n return currentLogFile\n}\n\nfunction writeLogLine(level: LogLevel, message: string): void {\n if (!currentLogFile) return\n\n const logLine = `[${new Date().toISOString()}] [${level}] ${message}\\n`\n try {\n fs.appendFileSync(currentLogFile, logLine, 'utf-8')\n } catch {\n // Ignore write failure\n }\n}\n\nexport function logError(message: string, error?: Error): void {\n errorCount++\n const errorDetail = error ? `: ${error.message}` : ''\n writeLogLine(LogLevel.ERROR, `${message}${errorDetail}`)\n}\n\nexport function logInfo(message: string): void {\n writeLogLine(LogLevel.INFO, message)\n}\n\nexport function getErrorCount(): number {\n return errorCount\n}\n\nexport function logSummary(totalMessages: number, totalMembers: number): void {\n if (!currentLogFile) return\n\n const summary = `\n=== Import Summary ===\nEnd time: ${new Date().toISOString()}\nTotal messages: ${totalMessages.toLocaleString()}\nTotal members: ${totalMembers.toLocaleString()}\nErrors: ${errorCount}\n`\n try {\n fs.appendFileSync(currentLogFile, summary, 'utf-8')\n } catch {\n // Ignore\n }\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\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 const all = sniffer.getSupportedFormats()\n return all.find((f) => f.id === formatId) ?? null\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 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() && entry.name.endsWith('.json')) {\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// ==================== 回调模式 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 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 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 module = this.formats.find((m) => m.feature.id === formatId)\n return module?.parser || 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 // 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","/**\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 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: '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 和 members 信息\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 }\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 // 解析 members(如果在文件开头能找到)\n const members: ParsedMember[] = []\n try {\n const membersMatch = headContent.match(/\"members\"\\s*:\\s*\\[([\\s\\S]*?)\\]/)\n if (membersMatch) {\n const membersJson = JSON.parse(`[${membersMatch[1]}]`) as ChatLabMember[]\n for (const m of membersJson) {\n members.push({\n platformId: m.platformId,\n accountName: m.accountName,\n groupNickname: m.groupNickname,\n avatar: m.avatar,\n roles: m.roles,\n })\n }\n }\n } catch {\n // members 可能太大,稍后从消息中收集\n }\n\n // 收集成员和消息\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 const pipeline = chain([readStream, parser(), pick({ filter: /^messages\\.\\d+$/ }), streamValues()])\n\n // 用于收集批次的临时数组\n const batchCollector: ParsedMessage[] = []\n\n pipeline.on('data', ({ value }: { value: ChatLabMessage }) => {\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\nexport const parser_: Parser = {\n feature,\n parse: parseChatLab,\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 * 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 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 }\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}\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: 'chatlab-jsonl',\n name: 'ChatLab JSONL',\n platform: KNOWN_PLATFORMS.UNKNOWN,\n priority: 51, // 低优先级,让其他格式先匹配\n extensions: ['.jsonl'],\n signatures: {\n // 第一行必须是 header 类型,包含 chatlab 信息\n head: [/^\\s*\\{.*\"_type\"\\s*:\\s*\"header\".*\"chatlab\"/],\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 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 }\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 avatar: parsed.avatar,\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 // 达到批次大小,发送进度\n if (messageBatch.length >= batchSize) {\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed.toLocaleString()} 条消息...`\n )\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 yield { type: 'members', data: members }\n } else if (memberMap.size > 0) {\n yield { type: 'members', data: Array.from(memberMap.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 : 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 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: 'shuakami-qq-exporter',\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 isSystemMessage?: boolean\n isRecalled?: 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}\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 * 计算 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.match(/\"uid\"\\s*:/g)\n return uidMatches ? uidMatches.length : 0\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* parseV4(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', `开始解析 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 // 根据 senders 数量判断聊天类型\n const sendersCount = countSenders(headContent)\n const chatType = sendersCount > 2 ? ChatType.GROUP : sendersCount > 0 ? ChatType.PRIVATE : ChatType.GROUP\n\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) {\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 // 消息类型\n const type = value.isSystemMessage\n ? MessageType.SYSTEM\n : convertMessageType(value.messageType, value.content, value.isRecalled)\n\n // 文本内容\n let textContent = value.content?.text || ''\n if (value.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\nexport const parser_: Parser = {\n feature,\n parse: parseV4,\n}\n\nimport { qqPreprocessor } from './shuakami-qq-preprocessor'\nexport const preprocessor = qqPreprocessor\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n preprocessor: qqPreprocessor,\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 } 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 return path.join(os.tmpdir(), 'chatlab')\n}\n\n/**\n * 确保目录存在\n */\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\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 * QQ JSON 消息的精简结构\n */\ninterface SlimQQMessage {\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 }\n recalled?: boolean\n isRecalled?: boolean\n system?: boolean\n isSystemMessage?: boolean\n rawMessage?: {\n sendNickName?: string\n sendMemberName?: string\n senderUin?: string\n senderUid?: string\n }\n}\n\n/**\n * 精简 QQ JSON 消息对象\n */\nfunction slimMessage(msg: Record<string, unknown>): SlimQQMessage {\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: SlimQQMessage['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 const slimMsg: SlimQQMessage = {\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) slimMsg.recalled = msg.recalled as boolean\n if (msg.system) slimMsg.system = msg.system as boolean\n\n // V4 新格式字段\n if (msg.messageId) slimMsg.messageId = msg.messageId as string\n if (msg.messageType !== undefined) slimMsg.messageType = msg.messageType as number\n if (msg.isRecalled) slimMsg.isRecalled = msg.isRecalled as boolean\n if (msg.isSystemMessage) slimMsg.isSystemMessage = msg.isSystemMessage as boolean\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 * 预处理 QQ JSON 文件\n */\nasync function preprocessQQJson(inputPath: string, onProgress?: (progress: ParseProgress) => void): 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 return new Promise((resolve, reject) => {\n const headChunks: string[] = []\n let headSize = 0\n const maxHeadSize = 100000\n\n const headStream = fs.createReadStream(inputPath, { encoding: 'utf-8' })\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 headStream.on('data', (chunk: string | Buffer) => {\n const str = typeof chunk === 'string' ? chunk : chunk.toString('utf-8')\n if (headSize < maxHeadSize) {\n headChunks.push(str)\n headSize += str.length\n } else {\n headStream.destroy()\n }\n })\n\n headStream.on('close', () => {\n const headContent = headChunks.join('')\n\n try {\n const chatInfoMatch = headContent.match(/\"chatInfo\"\\s*:\\s*(\\{[^}]+\\})/)\n if (chatInfoMatch) {\n chatInfo = JSON.parse(chatInfoMatch[1])\n }\n } catch {\n // 使用默认值\n }\n\n try {\n const metadataMatch = headContent.match(/\"metadata\"\\s*:\\s*(\\{[^}]+\\})/)\n if (metadataMatch) {\n metadata = JSON.parse(metadataMatch[1])\n }\n } catch {\n // 忽略\n }\n\n try {\n const statisticsMatch = headContent.match(/\"statistics\"\\s*:\\s*(\\{[\\s\\S]*?\\})\\s*,\\s*\"messages\"/)\n if (statisticsMatch) {\n statistics = JSON.parse(statisticsMatch[1])\n }\n } catch {\n // 解析失败时忽略\n }\n\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 headStream.on('error', reject)\n })\n}\n\n/**\n * 清理临时文件\n */\nfunction cleanupTempFile(filePath: string): void {\n try {\n if (fs.existsSync(filePath) && filePath.includes(getTempDir())) {\n fs.unlinkSync(filePath)\n }\n } catch {\n // 忽略清理错误\n }\n}\n\n/**\n * QQ Chat Exporter 预处理器\n */\nexport const qqPreprocessor: Preprocessor = {\n needsPreprocess(_filePath: string, fileSize: number): boolean {\n return fileSize > PREPROCESS_THRESHOLD\n },\n\n preprocess: preprocessQQJson,\n\n cleanup: cleanupTempFile,\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 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: 'shuakami-qq-exporter-chunked',\n name: 'shuakami/qq-chat-exporter (chunked)',\n platform: KNOWN_PLATFORMS.QQ,\n priority: 5, // 比单文件版本优先级更高\n extensions: ['.json'],\n signatures: {\n // 使用 head 签名 \"format\": \"chunked-jsonl\" 来区分。\n head: [/\"format\"\\s*:\\s*\"chunked-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') {\n onLog?.('error', `不支持的格式: ${manifest.metadata.format}`)\n yield { type: 'error', data: new Error(`不支持的格式: ${manifest.metadata.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 // 发送 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 ownerId: manifest.chatInfo.selfUin || manifest.chatInfo.selfUid,\n }\n yield { type: 'meta', data: meta }\n onLog?.('info', `Meta 信息: name=${meta.name}, type=${meta.type}, ownerId=${meta.ownerId}`)\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 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: '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\n// 导出解析函数供其他格式复用(如 ycccccccy-echotrace)\nexport { parseWeFlow }\n\nexport const parser_: Parser = {\n feature,\n parse: parseWeFlow,\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 格式预处理器\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 type { FormatFeature, FormatModule, Parser } from '../types'\nimport { parseWeFlow } from './weflow'\nimport { weflowPreprocessor } from './weflow-preprocessor'\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: 'ycccccccy-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: parseWeFlow,\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 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: 'tyrrrz-discord-exporter',\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 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: '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","/**\n * Telegram 单聊天导出 JSON 格式解析器\n * 适配 Telegram Desktop (Windows) 的「导出聊天记录」→ 单个聊天导出\n *\n * 格式特征:\n * - 单个 JSON 文件只包含一个聊天\n * - 顶层直接是聊天对象:{ name, type, id, messages }\n * - 没有 about / personal_information / chats 等外层包装\n * - 消息结构与全量导出一致(date_unixtime, from_id, text_entities 等)\n * - 支持 personal_chat / bot_chat / private_group / public_channel 等类型\n *\n * 导入流程(直接导入,无需聊天选择器):\n * 1. 用户选择单聊天 JSON 文件 → 格式识别\n * 2. parser 直接读取并解析该文件\n */\n\nimport * as fs from 'fs'\nimport streamChain from 'stream-chain'\nimport streamJson from 'stream-json'\nimport streamValuesModule from 'stream-json/streamers/StreamValues.js'\n\nimport { KNOWN_PLATFORMS, ChatType } from '@openchatlab/shared-types'\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 { streamValues } = streamValuesModule\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: 'telegram-native-single',\n name: 'Telegram 单聊天导出 (JSON)',\n platform: KNOWN_PLATFORMS.TELEGRAM,\n priority: 23,\n extensions: ['.json'],\n signatures: {\n // Telegram 单聊天导出特征:文件以 \"name\" 作为第一个 JSON 键\n // 这与全量导出(以 \"about\" 开头)有效区分,避免嵌套字段误匹配\n head: [/^\\s*\\{\\s*\\r?\\n\\s*\"name\"\\s*:/],\n requiredFields: ['messages'],\n fieldPatterns: {\n // Telegram 特有的聊天类型值,精确区分\n telegramChatType:\n /\"type\"\\s*:\\s*\"(personal_chat|bot_chat|private_group|private_supergroup|public_group|public_supergroup|public_channel|private_channel|saved_messages)\"/,\n },\n },\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseTelegramSingle(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, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n onLog?.('info', `开始解析 Telegram 单聊天 JSON 文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 流式读取整个文件(顶层即为聊天对象)\n const chatData = await new Promise<TelegramChat | null>((resolve, reject) => {\n const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })\n\n const pipeline = chain([readStream, parser(), streamValues()])\n\n let found = false\n\n pipeline.on('data', ({ value }: { value: TelegramChat }) => {\n if (!found) {\n found = true\n resolve(value)\n }\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', '无法解析 Telegram 单聊天文件')\n yield { type: 'error', data: new Error('无法解析 Telegram 单聊天文件') }\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 0,\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: parseTelegramSingle,\n}\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n}\n\nexport default module_\n","/**\n * WhatsApp 官方导出 TXT 格式解析器\n * 适配 WhatsApp 聊天导出功能\n *\n * 格式特征:\n * - 文件头:消息和通话已进行端到端加密 / Messages and calls are end-to-end encrypted\n * - 消息格式(V1,无方括号):YYYY/MM/DD HH:MM - 昵称: 内容\n * - 消息格式(V2,方括号):[时间戳] 昵称: 内容\n * 时间戳由弹性解析器处理,自动适配各地区变体:\n * - 日期顺序:YYYY/MM/DD | DD/MM/YYYY | M/D/YY\n * - AM/PM 标记:24h 无标记 | AM/PM | 上午/下午 | 오전/오후 | 午前/午後\n * - 分隔符:逗号 / 空格 / 特殊空格(U+2009, U+202F)\n * - 系统消息:时间戳 + 系统内容(无冒号分隔)\n * - 媒体占位:<省略影音内容> / 圖片已略去 等\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 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\n/**\n * 从文件名提取聊天名称\n * 例如:与开心每一天的 WhatsApp 聊天.txt → 开心每一天\n * 例如:与gaoberry37的 WhatsApp 聊天.txt → gaoberry37\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath)\n // 简体中文:与xxx的 WhatsApp 聊天.txt\n const matchZhCn = basename.match(/^与(.+?)的\\s*WhatsApp\\s*聊天\\.txt$/i)\n if (matchZhCn) return matchZhCn[1].trim()\n // 繁体中文:與xxx的WhatsApp對話.txt\n const matchZhTw = basename.match(/^與(.+?)的\\s*WhatsApp\\s*對話\\.txt$/i)\n if (matchZhTw) return matchZhTw[1].trim()\n // 兜底:移除扩展名\n return basename.replace(/\\.txt$/i, '') || '未知聊天'\n}\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: 'whatsapp-native-txt',\n name: 'WhatsApp 官方导出 (TXT)',\n platform: KNOWN_PLATFORMS.WHATSAPP,\n priority: 25,\n extensions: ['.txt'],\n signatures: {\n // WhatsApp 导出文件的特征(中文/英文)\n // 注意:仅保留 WhatsApp 独有的特征,避免误匹配其他 TXT 格式(如 LINE)\n head: [\n /消息和通话已进行端到端加密/, // 简体中文加密提示(WhatsApp 独有)\n /訊息與通話已受端對端加密保護/, // 繁体中文加密提示(WhatsApp 独有)\n /Messages and calls are end-to-end encrypted/i, // 英文加密提示(WhatsApp 独有)\n /你发送给自己的消息已进行端到端加密/, // 简体中文自己对话提示(WhatsApp 独有)\n /\\d{1,4}\\/\\d{1,2}\\/\\d{2,4},?\\s+\\d{1,2}:\\d{2} - /, // 消息行格式特征(无方括号,含 \" - \" 分隔符,WhatsApp 独有)\n /\\[\\d{1,4}\\/\\d{1,2}\\/\\d{2,4}[\\s,].*\\d{1,2}:\\d{2}:\\d{2}.*\\] /, // 消息行格式特征(方括号 + 含日期和时间的时间戳,兼容各地区变体)\n ],\n // 文件名特征:与xxx的 WhatsApp 聊天.txt\n filename: [/^与.+的\\s*WhatsApp\\s*聊天\\.txt$/i, /^與.+的\\s*WhatsApp\\s*對話\\.txt$/i, /WhatsApp/i],\n },\n}\n\n// ==================== 辅助函数:清理不可见字符 ====================\n\n/**\n * 清理行首/行尾的不可见 Unicode 字符\n * WhatsApp 导出文件中可能包含 BOM、Left-to-Right Mark (U+200E) 等\n */\nfunction cleanLine(line: string): string {\n // 移除常见的不可见字符:BOM、LTR Mark、RTL Mark、零宽字符等\n return line.replace(/^(?:\\uFEFF|\\u200E|\\u200F|\\u200B|\\u200C|\\u200D|\\u2060)+/, '').trim()\n}\n\n// ==================== 消息头正则 ====================\n\n// 格式1(无方括号):日期/日期/日期[,] 时:分 - 内容\n// 兼容各地区变体:YYYY/MM/DD HH:MM | DD/MM/YYYY, HH:MM | M/D/YY, HH:MM\nconst MESSAGE_LINE_REGEX_V1 = /^(\\d{1,4}\\/\\d{1,2}\\/\\d{2,4},?\\s+\\d{1,2}:\\d{2}) - (.+)$/\n\n// 格式2(方括号格式):宽松捕获 [时间戳] 后的内容\n// 时间戳的地区变体由 parseFlexibleV2Timestamp() 弹性解析器处理\n// 已知变体包括但不限于:中文 上午/下午、英文 AM/PM、韩文 오전/오후、各种日期顺序\nconst MESSAGE_LINE_REGEX_V2 = /^\\[([^\\]]+)\\] (.+)$/\n\n// 从消息内容中分离昵称和实际内容\n// 格式:昵称: 内容(冒号后可能是空格、U+200E LTR Mark 或两者组合)\nconst SENDER_CONTENT_REGEX = /^(.+?):[\\s\\u200E]+(.*)$/\n\n// ==================== 系统消息识别 ====================\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 /此訊息已刪除/,\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]\n\nfunction isSystemMessage(content: string): boolean {\n return SYSTEM_MESSAGE_PATTERNS.some((pattern) => pattern.test(content))\n}\n\n// ==================== 消息类型判断 ====================\n\nfunction detectMessageType(content: string): MessageType {\n const trimmed = content.trim()\n\n // 媒体消息(简体/繁体多种变体)\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 if (trimmed.includes('<已附加:') || trimmed.includes('<附件:') || trimmed.includes('<已附加:'))\n return MessageType.FILE\n\n // 贴图/贴纸(繁体中文导出标记)\n if (trimmed === '貼圖已忽略' || trimmed === '貼圖已略去') return MessageType.EMOJI\n\n // 删除消息(简体/繁体多种变体)\n if (trimmed === '这条消息已删除' || trimmed.startsWith('此訊息已刪除') || trimmed.startsWith('你已刪除此訊息'))\n return MessageType.RECALL\n\n // 系统消息\n if (isSystemMessage(trimmed)) return MessageType.SYSTEM\n\n return MessageType.TEXT\n}\n\n// ==================== 时间解析 ====================\n\n// 多语言 AM/PM 标记映射(值为 true 表示 PM,false 表示 AM)\nconst AMPM_MARKERS: [RegExp, boolean][] = [\n [/\\bPM\\b/i, true],\n [/\\bP\\.M\\.\\b/i, true],\n [/下午/, true],\n [/午後/, true],\n [/오후/, true],\n [/\\bAM\\b/i, false],\n [/\\bA\\.M\\.\\b/i, false],\n [/上午/, false],\n [/午前/, false],\n [/오전/, false],\n]\n\n/**\n * 弹性解析 V2(方括号)时间戳\n * 自动处理各地区变体:日期顺序、AM/PM 多语言标记、特殊空格等\n * @returns 秒级时间戳,解析失败返回 null\n */\nfunction parseFlexibleV2Timestamp(raw: string): number | null {\n // 1. 规范化特殊空格和不可见字符\n let str = raw.replace(/(?:\\u2009|\\u202F|\\uFEFF|\\u200E|\\u200F|\\u200B|\\u200C|\\u200D|\\u2060)/g, ' ').trim()\n\n // 2. 提取并移除 AM/PM 标记(任何语言)\n let isPM: boolean | null = null\n for (const [pattern, pm] of AMPM_MARKERS) {\n if (pattern.test(str)) {\n isPM = pm\n str = str.replace(pattern, '').trim()\n break\n }\n }\n\n // 3. 移除逗号,规范化连续空格\n str = str.replace(/,/g, ' ').replace(/\\s+/g, ' ').trim()\n\n // 4. 提取日期部分(含 /)和时间部分(含 :)\n const match = str.match(/^(\\d{1,4}\\/\\d{1,2}\\/\\d{2,4})\\s+(\\d{1,2}:\\d{2}(?::\\d{2})?)$/)\n if (!match) return null\n\n const [, datePart, timePart] = match\n const dateParts = datePart.split('/').map((s) => parseInt(s, 10))\n const timeParts = timePart.split(':').map((s) => parseInt(s, 10))\n\n // 5. 推断日期顺序\n let year: number, month: number, day: number\n if (dateParts[0] > 31) {\n // 第一段 > 31 → 一定是年份 → YYYY/MM/DD\n year = dateParts[0]\n month = dateParts[1]\n day = dateParts[2]\n } else if (dateParts[2] > 31) {\n // 第三段 > 31 → 一定是年份 → DD/MM/YYYY\n day = dateParts[0]\n month = dateParts[1]\n year = dateParts[2]\n } else {\n // 全部 ≤ 31 → M/D/YY(2 位年份,00-99 → 2000-2099)\n month = dateParts[0]\n day = dateParts[1]\n year = 2000 + dateParts[2]\n }\n\n // 6. 解析时间\n let hour = timeParts[0]\n const minute = timeParts[1]\n const second = timeParts[2] ?? 0\n\n if (isPM === true && hour !== 12) hour += 12\n if (isPM === false && hour === 12) hour = 0\n\n // 7. 构造日期并校验\n const date = new Date(year, month - 1, day, hour, minute, second)\n const ts = Math.floor(date.getTime() / 1000)\n return isNaN(ts) ? null : ts\n}\n\n/**\n * 解析 V1(无方括号)时间格式\n * 支持:YYYY/M/D H:MM | DD/MM/YYYY, HH:MM | M/D/YY, HH:MM\n */\nfunction parseV1Timestamp(timeStr: string): number {\n // 移除逗号,规范化空格\n const str = timeStr.replace(/,/g, '').replace(/\\s+/g, ' ').trim()\n const match = str.match(/^(\\d{1,4})\\/(\\d{1,2})\\/(\\d{2,4})\\s+(\\d{1,2}):(\\d{2})$/)\n if (!match) {\n const normalized = timeStr.replace(/,/g, '').replace(/\\//g, '-').replace(/\\s+/, 'T') + ':00'\n return Math.floor(new Date(normalized).getTime() / 1000)\n }\n\n const parts = [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)]\n const hour = parseInt(match[4], 10)\n const minute = parseInt(match[5], 10)\n\n let year: number, month: number, day: number\n if (parts[0] > 31) {\n year = parts[0]\n month = parts[1]\n day = parts[2]\n } else if (parts[2] > 31) {\n day = parts[0]\n month = parts[1]\n year = parts[2]\n } else {\n month = parts[0]\n day = parts[1]\n year = 2000 + parts[2]\n }\n\n const date = new Date(year, month - 1, day, hour, minute, 0)\n const ts = Math.floor(date.getTime() / 1000)\n return isNaN(ts) ? 0 : ts\n}\n\n// ==================== 成员信息 ====================\n\ninterface MemberInfo {\n platformId: string\n nickname: string\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseWhatsApp(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 skippedLines = 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', `开始解析 WhatsApp TXT 文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 收集数据\n const chatName = extractNameFromFilePath(filePath)\n const memberMap = new Map<string, MemberInfo>()\n const messages: ParsedMessage[] = []\n\n // 当前正在解析的消息(可能跨多行)\n let currentMessage: {\n timestamp: number\n sender: string | null // null 表示系统消息\n contentLines: string[]\n } | null = null\n\n // 保存当前消息\n const saveCurrentMessage = () => {\n if (currentMessage) {\n const content = currentMessage.contentLines.join('\\n').trim()\n const type = detectMessageType(content)\n\n // 系统消息使用特殊 ID 和统一名称\n const senderPlatformId = currentMessage.sender || 'system'\n const senderName = currentMessage.sender || '系统消息'\n\n messages.push({\n senderPlatformId,\n senderAccountName: senderName,\n timestamp: currentMessage.timestamp,\n type,\n content: content || null,\n })\n\n // 更新成员信息(跳过系统消息)\n if (currentMessage.sender) {\n memberMap.set(senderPlatformId, {\n platformId: senderPlatformId,\n nickname: senderName,\n })\n }\n\n messagesProcessed++\n }\n }\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 fileStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n for await (const line of rl) {\n // 清理行首不可见字符\n const cleanedLine = cleanLine(line)\n\n // 尝试匹配消息行:优先 V1(严格),再 V2(宽松 + 弹性时间解析)\n const v1Match = cleanedLine.match(MESSAGE_LINE_REGEX_V1)\n if (v1Match) {\n saveCurrentMessage()\n const restContent = v1Match[2]\n const senderMatch = restContent.match(SENDER_CONTENT_REGEX)\n if (senderMatch && !isSystemMessage(restContent)) {\n currentMessage = {\n timestamp: parseV1Timestamp(v1Match[1]),\n sender: senderMatch[1].trim(),\n contentLines: [senderMatch[2]],\n }\n } else {\n currentMessage = {\n timestamp: parseV1Timestamp(v1Match[1]),\n sender: null,\n contentLines: [restContent],\n }\n }\n if (messagesProcessed % 500 === 0) {\n onProgress?.(\n createProgress('parsing', bytesRead, totalBytes, messagesProcessed, `已处理 ${messagesProcessed} 条消息...`)\n )\n }\n continue\n }\n\n const v2Match = cleanedLine.match(MESSAGE_LINE_REGEX_V2)\n if (v2Match) {\n const timestamp = parseFlexibleV2Timestamp(v2Match[1])\n if (timestamp !== null) {\n // 弹性解析成功 → 新消息\n saveCurrentMessage()\n const restContent = v2Match[2]\n const senderMatch = restContent.match(SENDER_CONTENT_REGEX)\n if (senderMatch && !isSystemMessage(restContent)) {\n currentMessage = {\n timestamp,\n sender: senderMatch[1].trim(),\n contentLines: [senderMatch[2]],\n }\n } else {\n currentMessage = {\n timestamp,\n sender: null,\n contentLines: [restContent],\n }\n }\n if (messagesProcessed % 500 === 0) {\n onProgress?.(\n createProgress('parsing', bytesRead, totalBytes, messagesProcessed, `已处理 ${messagesProcessed} 条消息...`)\n )\n }\n continue\n }\n // 弹性解析失败 → 方括号内容不是有效时间戳,降级为续行\n }\n\n // 非消息行:可能是多行消息的延续\n if (currentMessage && cleanedLine) {\n currentMessage.contentLines.push(cleanedLine)\n } else if (cleanedLine) {\n // 无法解析的非空行\n skippedLines++\n }\n }\n\n // 保存最后一条消息\n saveCurrentMessage()\n\n // 确定聊天类型:根据参与者数量判断\n // - 排除系统成员后,2 人或更少:私聊\n // - 超过 2 人:群聊\n const hasSystemMember = memberMap.has('system')\n const realMemberCount = hasSystemMember ? memberMap.size - 1 : memberMap.size\n\n const chatType = realMemberCount > 2 ? ChatType.GROUP : ChatType.PRIVATE\n\n // 发送 meta\n const meta: ParsedMeta = {\n name: chatName,\n platform: KNOWN_PLATFORMS.WHATSAPP,\n type: chatType,\n }\n yield { type: 'meta', data: meta }\n\n // 发送成员\n const members: ParsedMember[] = Array.from(memberMap.values()).map((m) => ({\n platformId: m.platformId,\n accountName: m.nickname,\n }))\n yield { type: 'members', data: members }\n\n // 分批发送消息\n for (let i = 0; i < messages.length; i += batchSize) {\n const batch = messages.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 typeCounts = new Map<MessageType, number>()\n for (const msg of messages) {\n typeCounts.set(msg.type, (typeCounts.get(msg.type) || 0) + 1)\n }\n\n // 记录解析摘要\n onLog?.('info', `解析完成: ${messagesProcessed} 条消息, ${memberMap.size} 个成员, 类型: ${chatType}`)\n onLog?.(\n 'info',\n `消息类型统计: ${Array.from(typeCounts.entries())\n .map(([type, count]) => `${type}=${count}`)\n .join(', ')}`\n )\n if (skippedLines > 0) {\n onLog?.('info', `跳过 ${skippedLines} 行无法解析的内容`)\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: parseWhatsApp,\n}\n\n// ==================== 导出格式模块 ====================\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n // TXT 格式不需要预处理器\n}\n\nexport default module_\n","/**\n * QQ 官方导出 TXT 格式解析器\n * 适配 QQ 群聊旧版导出功能\n *\n * 格式特征:\n * - 文件头:消息记录(此消息记录为文本格式,不支持重新导入)\n * - 群名:消息对象:xxx\n * - 消息格式:时间 昵称(QQ号) 或 时间 昵称<邮箱>\n * - 内容在消息头下一行,可能跨多行\n *\n * 字段映射:\n * - name: platformId(用户ID)\n * - nickname: 群昵称\n * - senderName: 群昵称(用于昵称历史追踪)\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 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\n/**\n * 从文件名提取群名\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath)\n const name = basename.replace(/\\.txt$/i, '')\n return name || '未知群聊'\n}\n\n// ==================== 特征定义 ====================\n\nexport const feature: FormatFeature = {\n id: 'qq-native-txt',\n name: 'QQ 官方导出 (TXT)',\n platform: KNOWN_PLATFORMS.QQ,\n priority: 30,\n extensions: ['.txt'],\n signatures: {\n // 支持群聊导出和多人聊天(讨论组)导出\n head: [/消息记录(此消息记录为文本格式/, /消息对象:/, /多人聊天/],\n },\n}\n\n// ==================== 消息头正则 ====================\n\n// 群聊格式:2019-07-16 18:15:05 地瓜(23333233)\n// 邮箱格式:2019-07-16 18:15:11 土豆<example@xx.com>\n// 讨论组格式:2017-08-29 20:28:30 番茄(没有 ID,只有昵称)\nconst MESSAGE_HEADER_REGEX = /^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) (.+?)(?:\\(([^)]+)\\)|<([^>]+)>)?$/\n\n// 匹配群名:消息对象:xxx\nconst GROUP_NAME_REGEX = /^消息对象:(.+)$/\n\n// ==================== 消息类型判断 ====================\n\nfunction detectMessageType(content: string): MessageType {\n const trimmed = content.trim()\n\n // 基础消息类型\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\n // 交互消息类型\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('通话时长')) return MessageType.CALL\n if (trimmed === '[分享]' || trimmed === '[音乐]' || trimmed === '[小程序]') return MessageType.SHARE\n if (trimmed.startsWith('[回复]')) return MessageType.REPLY\n if (trimmed === '[转发]' || trimmed === '[聊天记录]') return MessageType.FORWARD\n\n // 系统消息类型\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\n // 其他方括号包裹的特殊消息\n if (trimmed.startsWith('[') && trimmed.endsWith(']')) return MessageType.OTHER\n\n return MessageType.TEXT\n}\n\n// ==================== 时间解析 ====================\n\n/**\n * 解析本地时间字符串为秒级时间戳\n * @param timeStr 格式:2019-07-16 18:15:05\n */\nfunction parseLocalTime(timeStr: string): number {\n // 直接用 Date 解析,会按本地时区处理\n const date = new Date(timeStr.replace(' ', 'T'))\n return Math.floor(date.getTime() / 1000)\n}\n\n// ==================== 昵称清理 ====================\n\n/**\n * 清理昵称中的前缀污染\n * 例如:【管理员】张三 -> 张三\n */\nfunction cleanNickname(nickname: string): string {\n // 移除开头的【xxx】前缀(可能有多个)\n return nickname.replace(/^(【[^】]*】\\s*)+/, '').trim()\n}\n\n// ==================== 成员信息 ====================\n\ninterface MemberInfo {\n platformId: string\n nickname: string // 群昵称\n}\n\n// 用于记录用户最近的有效昵称(昵称 != ID 的情况)\nconst lastValidNickname = new Map<string, string>()\n\n// ==================== 解析器实现 ====================\n\nasync function* parseTxt(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 skippedLines = 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', `开始解析 QQ TXT 文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 收集数据\n let groupName = '未知群聊'\n const memberMap = new Map<string, MemberInfo>()\n const messages: ParsedMessage[] = []\n\n // 当前正在解析的消息\n let currentMessage: {\n timestamp: number\n platformId: string\n nickname: string\n contentLines: string[]\n } | null = null\n\n // 保存当前消息\n const saveCurrentMessage = () => {\n if (currentMessage) {\n const content = currentMessage.contentLines.join('\\n').trim()\n const type = detectMessageType(content)\n\n messages.push({\n senderPlatformId: currentMessage.platformId,\n senderAccountName: currentMessage.nickname, // QQ TXT 格式只有一个昵称,作为账号名称追踪历史\n // 不设置 senderGroupNickname,避免同一昵称被重复追踪\n timestamp: currentMessage.timestamp,\n type,\n content: content || null,\n })\n\n // 更新成员信息(保留最新昵称)\n memberMap.set(currentMessage.platformId, {\n platformId: currentMessage.platformId,\n nickname: currentMessage.nickname,\n })\n\n messagesProcessed++\n }\n }\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 fileStream.on('data', (chunk: string | Buffer) => {\n bytesRead += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length\n })\n\n for await (const line of rl) {\n // 检查群名\n const groupMatch = line.match(GROUP_NAME_REGEX)\n if (groupMatch) {\n groupName = groupMatch[1].trim()\n continue\n }\n\n // 检查消息头\n const headerMatch = line.match(MESSAGE_HEADER_REGEX)\n if (headerMatch) {\n // 保存前一条消息\n saveCurrentMessage()\n\n const timeStr = headerMatch[1]\n const rawNickname = headerMatch[2].trim()\n let nickname = cleanNickname(rawNickname) // 清理前缀污染\n // platformId: (id) 或 <email>,如果没有则使用昵称(讨论组格式)\n const platformId = headerMatch[3] || headerMatch[4] || nickname\n\n // 如果昵称和 ID 相同,可能是系统故障,使用之前记录的昵称\n if (nickname === platformId && headerMatch[3]) {\n // 只有当确实有 ID 时才检查昵称覆盖\n const previousNickname = lastValidNickname.get(platformId)\n if (previousNickname) {\n nickname = previousNickname\n }\n // 如果没有之前的记录,保持使用 ID 作为昵称\n } else if (headerMatch[3] || headerMatch[4]) {\n // 记录有效昵称(有 ID 且昵称 != ID)\n lastValidNickname.set(platformId, nickname)\n }\n\n currentMessage = {\n timestamp: parseLocalTime(timeStr),\n platformId,\n nickname,\n contentLines: [],\n }\n\n // 更新进度\n if (messagesProcessed % 1000 === 0) {\n const progress = createProgress(\n 'parsing',\n bytesRead,\n totalBytes,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n onProgress?.(progress)\n }\n\n continue\n }\n\n // 内容行(追加到当前消息)\n if (currentMessage) {\n // 跳过分隔线\n if (line.startsWith('=====')) continue\n // 跳过文件头信息\n if (line.startsWith('消息记录') || line.startsWith('消息分组')) continue\n\n currentMessage.contentLines.push(line)\n } else {\n // 没有当前消息时,检查是否是需要跳过的行\n const trimmed = line.trim()\n if (\n trimmed &&\n !trimmed.startsWith('=====') &&\n !trimmed.startsWith('消息记录') &&\n !trimmed.startsWith('消息分组')\n ) {\n // 这是一个无法解析的非空行\n skippedLines++\n }\n }\n }\n\n // 保存最后一条消息\n saveCurrentMessage()\n\n // 发送 meta(如果群名仍是默认值,使用文件名作为后备)\n const meta: ParsedMeta = {\n name: groupName === '未知群聊' ? extractNameFromFilePath(filePath) : groupName,\n platform: KNOWN_PLATFORMS.QQ,\n type: ChatType.GROUP,\n }\n yield { type: 'meta', data: meta }\n\n // 发送成员(QQ TXT 格式只有一个昵称,只设置 accountName 避免重复追踪)\n const members: ParsedMember[] = Array.from(memberMap.values()).map((m) => ({\n platformId: m.platformId,\n accountName: m.nickname, // QQ TXT 格式只有昵称,作为账号名称\n // 不设置 groupNickname,避免同一昵称被重复追踪\n }))\n yield { type: 'members', data: members }\n\n // 分批发送消息\n for (let i = 0; i < messages.length; i += batchSize) {\n const batch = messages.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 (skippedLines > 0) {\n onLog?.('info', `跳过 ${skippedLines} 行无法解析的内容`)\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: parseTxt,\n}\n\n// ==================== 导出格式模块 ====================\n\nconst module_: FormatModule = {\n feature,\n parser: parser_,\n // TXT 格式不需要预处理器\n}\n\nexport default module_\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 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: '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\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 new TextDecoder('utf-8').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 */\nfunction isSystemMessage(content: string): boolean {\n const systemPatterns = [\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 return systemPatterns.some((p) => content.includes(p))\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 reversedMessages = [...data.messages].reverse()\n const messageBatch: ParsedMessage[] = []\n\n for (const msg of reversedMessages) {\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 / reversedMessages.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 * LINE 官方导出 TXT 格式解析器\n * 支持私聊和群聊,支持多语言导出(EN / ZH-CN / ZH-TW / JA)\n *\n * 格式特征:\n * - 头部格式(私聊和群聊相同):\n * Line 1: [LINE] {name}的聊天记录 / Chat history with/in {name} / ...\n * Line 2: 保存日期: YYYY/MM/DD HH:MM / Saved on: ...\n * Line 3: (空行)\n * - 日期行:YYYY/MM/DD(星期)或 Day, MM/DD/YYYY\n * - 消息格式:TIME\\t{sender}\\t{content}(Tab 分隔)\n * - 系统消息:TIME\\t\\t{content}(双 Tab,无发送者)\n * - 时间格式:HH:MM / 上午|下午HH:MM / 午前|午後HH:MM / HH:MMam|pm\n * - 多行消息:用双引号包裹\n *\n * 私聊 vs 群聊区分:\n * - EN: \"Chat history with {name}\" (私聊) vs \"Chat history in {name}\" (群聊)\n * - JA: \"{name}とのトーク履歴\" (私聊) vs \"{name}のトーク履歴\" (群聊)\n * - ZH-CN: \"与{name}的聊天记录\" (私聊) vs \"{name}的聊天记录\" (群聊)\n * - ZH-TW: \"與{name}的聊天記錄\" (私聊) vs \"{name}的聊天記錄\" (群聊)\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types'\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: 'line-native-txt',\n name: 'LINE 官方导出 TXT',\n platform: KNOWN_PLATFORMS.LINE,\n priority: 35,\n extensions: ['.txt'],\n signatures: {\n head: [\n // 头部标识(多语言)\n /^\\[LINE\\] /m,\n /^(?:\\[LINE\\] )?Chat history (?:with|in) /m,\n // Tab 分隔的消息格式(支持多种时间格式)\n /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?)\\t[^\\t\\n]+\\t/m,\n // 空格分隔的消息格式(部分 LINE 导出)\n /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?) [^\\s]+ /m,\n // LINE 独有的日期行格式:YYYY.MM.DD DayOfWeek(英文星期全称)\n /^\\d{4}\\.\\d{2}\\.\\d{2}\\s+(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)/m,\n // LINE 日文/中文日期行格式:YYYY/M/D(曜日)\n /^\\d{4}\\/\\d{1,2}\\/\\d{1,2}[((][月火水木金土日]/m,\n // LINE 中文日期行格式:YYYY/M/D周X\n /^\\d{4}\\/\\d{1,2}\\/\\d{1,2}周/m,\n ],\n // 文件名特征:[LINE] 出现在文件名中\n filename: [/\\[LINE\\]/i],\n },\n}\n\n// ==================== 辅助函数 ====================\n\n/**\n * 从文件名提取聊天名称\n */\nfunction extractNameFromFilePath(filePath: string): string {\n const basename = path.basename(filePath, '.txt')\n // 移除 [LINE] 前缀\n const name = basename.replace(/^\\[LINE\\]\\s*/i, '').trim()\n return name || '未知聊天'\n}\n\n/**\n * 从头部提取聊天名称和类型\n * 支持:英文、日文、简体中文、繁体中文\n */\nfunction extractNameFromHeader(header: string): { name: string; isGroup: boolean } | null {\n // ===== 英文 =====\n // 私聊:Chat history with {name}\n const enPrivateMatch = header.match(/^(?:\\[LINE\\] )?Chat history with (.+)$/m)\n if (enPrivateMatch) return { name: enPrivateMatch[1].trim(), isGroup: false }\n // 群聊:Chat history in {name}\n const enGroupMatch = header.match(/^(?:\\[LINE\\] )?Chat history in (.+)$/m)\n if (enGroupMatch) return { name: enGroupMatch[1].trim(), isGroup: true }\n\n // ===== 日文 =====\n // 私聊:{name}とのトーク履歴\n const jaPrivateMatch = header.match(/^\\[LINE\\] (.+)とのトーク履歴/)\n if (jaPrivateMatch) return { name: jaPrivateMatch[1].trim(), isGroup: false }\n // 群聊:{name}のトーク履歴\n const jaGroupMatch = header.match(/^\\[LINE\\] (.+)のトーク履歴/)\n if (jaGroupMatch) return { name: jaGroupMatch[1].trim(), isGroup: true }\n\n // ===== 简体中文 =====\n // 私聊:与{name}的聊天记录\n const zhCnPrivateMatch = header.match(/^\\[LINE\\] 与(.+)的聊天记录/)\n if (zhCnPrivateMatch) return { name: zhCnPrivateMatch[1].trim(), isGroup: false }\n // 群聊:{name}的聊天记录\n const zhCnGroupMatch = header.match(/^\\[LINE\\] (.+)的聊天记录/)\n if (zhCnGroupMatch) return { name: zhCnGroupMatch[1].trim(), isGroup: true }\n\n // ===== 繁体中文 =====\n // 私聊:與{name}的聊天記錄\n const zhTwPrivateMatch = header.match(/^\\[LINE\\] 與(.+)的聊天記錄/)\n if (zhTwPrivateMatch) return { name: zhTwPrivateMatch[1].trim(), isGroup: false }\n // 群聊:{name}的聊天記錄\n const zhTwGroupMatch = header.match(/^\\[LINE\\] (.+)的聊天記錄/)\n if (zhTwGroupMatch) return { name: zhTwGroupMatch[1].trim(), isGroup: true }\n\n return null\n}\n\n/**\n * 日期行正则模式\n */\nconst DATE_PATTERNS = [\n // 2025.12.10 Wednesday\n /^(\\d{4})\\.(\\d{2})\\.(\\d{2})\\s+(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)?/,\n // 2026/1/30周五 or 2026/1/30(金)\n /^(\\d{4})\\/(\\d{1,2})\\/(\\d{1,2})/,\n // Fri, 1/30/2026\n /^[A-Za-z]+,\\s*(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/,\n]\n\n/**\n * 尝试解析日期行\n */\nfunction parseDateLine(line: string): Date | null {\n for (const pattern of DATE_PATTERNS) {\n const match = line.match(pattern)\n if (match) {\n // 根据不同格式提取年月日\n if (pattern === DATE_PATTERNS[0] || pattern === DATE_PATTERNS[1]) {\n // YYYY.MM.DD or YYYY/M/D\n const year = parseInt(match[1])\n const month = parseInt(match[2]) - 1\n const day = parseInt(match[3])\n return new Date(year, month, day)\n } else if (pattern === DATE_PATTERNS[2]) {\n // M/D/YYYY\n const month = parseInt(match[1]) - 1\n const day = parseInt(match[2])\n const year = parseInt(match[3])\n return new Date(year, month, day)\n }\n }\n }\n return null\n}\n\n/**\n * 消息行正则模式\n * 时间格式:HH:MM / HH:MMam|pm / 上午|下午|午前|午後HH:MM\n */\n// 私聊/群聊(有发送者):TIME\\t{name}\\t{content}\nconst PRIVATE_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?)\\t([^\\t]+)\\t(.*)$/\n// 群聊:HH:MM {name} {content} (已废弃,实际都用 Tab 分隔)\nconst GROUP_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?) ([^\\s]+) (.*)$/\n// 系统消息:双 Tab(无发送者),如「下午07:04\\t\\tXXX已加入群組」\nconst SYSTEM_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\\d{1,2}:\\d{2}(?:[AaPp][Mm])?)\\t\\t(.+)$/\n\n/**\n * 特殊消息类型映射(多语言:EN / ZH-CN / ZH-TW / JA)\n */\nconst SPECIAL_MESSAGE_TYPES: Record<string, MessageType> = {\n // 图片 / Photo\n '[Photo]': MessageType.IMAGE, // EN\n '[照片]': MessageType.IMAGE, // ZH-CN / ZH-TW\n '[写真]': MessageType.IMAGE, // JA\n Photos: MessageType.IMAGE, // EN (fallback)\n\n // 语音 / Voice\n '[Voice message]': MessageType.VOICE, // EN\n '[语音信息]': MessageType.VOICE, // ZH-CN\n '[語音訊息]': MessageType.VOICE, // ZH-TW\n '[ボイスメッセージ]': MessageType.VOICE, // JA\n Audio: MessageType.VOICE, // EN (fallback)\n\n // 视频 / Video\n '[Video]': MessageType.VIDEO, // EN\n '[视频]': MessageType.VIDEO, // ZH-CN\n '[影片]': MessageType.VIDEO, // ZH-TW\n '[動画]': MessageType.VIDEO, // JA\n Videos: MessageType.VIDEO, // EN (fallback)\n\n // 文件 / File\n '[File]': MessageType.FILE, // EN\n '[文件]': MessageType.FILE, // ZH-CN\n '[檔案]': MessageType.FILE, // ZH-TW\n '[ファイル]': MessageType.FILE, // JA\n\n // 贴纸 / Sticker\n '[Sticker]': MessageType.EMOJI, // EN\n '[贴图]': MessageType.EMOJI, // ZH-CN\n '[貼圖]': MessageType.EMOJI, // ZH-TW\n '[スタンプ]': MessageType.EMOJI, // JA\n Stickers: MessageType.EMOJI, // EN (fallback)\n\n // 位置 / Location\n '[Location]': MessageType.LOCATION, // EN\n '[位置]': MessageType.LOCATION, // ZH-CN / ZH-TW\n '[位置情報]': MessageType.LOCATION, // JA\n\n // 记事本 / Notes\n '[Notes]': MessageType.TEXT, // EN\n '[记事本]': MessageType.TEXT, // ZH-CN\n '[記事本]': MessageType.TEXT, // ZH-TW\n '[ノート]': MessageType.TEXT, // JA\n}\n\n/**\n * 检测消息类型\n */\nfunction detectMessageType(content: string): MessageType {\n // 检查特殊消息类型\n for (const [pattern, type] of Object.entries(SPECIAL_MESSAGE_TYPES)) {\n if (content === pattern || content.startsWith(pattern)) {\n return type\n }\n }\n\n // 检查 [null] 开头的位置消息\n if (content.startsWith('[null]') && content.includes('maps.google.com')) {\n return MessageType.LOCATION\n }\n\n // 检查系统消息(多语言:EN / ZH-CN / ZH-TW / JA)\n if (\n // --- 加入群组 / Join group ---\n content.includes(' joined the group') || // EN\n content.includes('已加入该群') || // ZH-CN\n content.includes('已加入群組') || // ZH-TW\n content.includes('がグループに参加しました') || // JA\n // --- 拉人进群 / Added to group ---\n content.includes(' added ') || // EN\n content.includes(' to the group') || // EN\n content.includes('已将') || // ZH-CN\n content.includes('添加至群') || // ZH-CN\n content.includes('添加到群') || // ZH-CN (另一格式)\n content.includes('已新增') || // ZH-TW\n content.includes('至群組') || // ZH-TW\n content.includes('をグループに追加しました') || // JA\n // --- 退出群组 / Left group ---\n content.includes(' left the group') || // EN\n content.includes('已退群') || // ZH-CN\n content.includes('已離開群組') || // ZH-TW\n content.includes('がグループを退会しました') || // JA\n // --- 设定公告 / Announcement ---\n content.includes('made an announcement') || // EN\n content.includes('发布了通告') || // ZH-CN\n content.includes('已設定公告') || // ZH-TW\n content.includes('がアナウンスしました') || // JA\n // --- 收回讯息 / Unsent message ---\n content.includes('unsent a message') || // EN\n content === 'Message unsent.' || // EN\n content.includes('撤回了一条消息') || // ZH-CN\n content.includes('已收回訊息') || // ZH-TW\n content.includes('送信を取り消しました') || // JA\n // --- 其他 / Others ---\n content.startsWith('Auto-reply') // EN 自动回复\n ) {\n return MessageType.SYSTEM\n }\n\n // 检查链接\n if (content.match(/^https?:\\/\\//)) {\n return MessageType.LINK\n }\n\n return MessageType.TEXT\n}\n\n// ==================== 解析器实现 ====================\n\nasync function* parseLINE(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, '')\n yield { type: 'progress', data: initialProgress }\n onProgress?.(initialProgress)\n\n onLog?.('info', `开始解析 LINE 导出文件,大小: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`)\n\n // 读取整个文件(LINE 导出通常不大)\n const content = fs.readFileSync(filePath, 'utf-8')\n // 处理 Windows 换行符 (\\r\\n)\n const lines = content.split('\\n').map((line) => line.replace(/\\r$/, ''))\n\n // 解析状态\n let currentDate: Date | null = null\n let chatName = extractNameFromFilePath(filePath)\n let isPrivateChat = false\n let useTabSeparator = false\n const memberMap = new Map<string, ParsedMember>()\n const messages: ParsedMessage[] = []\n let lastMessage: ParsedMessage | null = null\n let lineIndex = 0\n\n // 检测是否有头部\n if (lines.length > 0) {\n const firstLine = lines[0].trim()\n onLog?.('debug', `LINE 第一行: \"${firstLine}\"`)\n const headerResult = extractNameFromHeader(firstLine)\n if (headerResult) {\n chatName = headerResult.name\n isPrivateChat = !headerResult.isGroup\n useTabSeparator = true // 两种头部格式都使用 Tab 分隔\n lineIndex = 3 // 跳过头部(标题、保存时间、空行)\n onLog?.('debug', `LINE 检测到头部,名称: ${headerResult.name}, 群聊: ${headerResult.isGroup}`)\n }\n }\n\n // 如果没有检测到头部,检查第一条消息的格式\n if (!isPrivateChat && lines.length > 0) {\n for (const line of lines) {\n if (PRIVATE_MSG_PATTERN.test(line)) {\n useTabSeparator = true\n onLog?.('debug', `LINE 检测到 Tab 分隔格式`)\n break\n }\n if (GROUP_MSG_PATTERN.test(line)) {\n useTabSeparator = false\n onLog?.('debug', `LINE 检测到空格分隔格式`)\n break\n }\n }\n }\n\n onLog?.('debug', `LINE 解析配置: useTabSeparator=${useTabSeparator}, lineIndex=${lineIndex}`)\n\n // 解析消息\n let debugLogCount = 0\n for (let i = lineIndex; i < lines.length; i++) {\n const line = lines[i]\n\n // 尝试解析日期行\n const dateResult = parseDateLine(line)\n if (dateResult) {\n currentDate = dateResult\n if (debugLogCount < 5) {\n onLog?.('debug', `LINE 日期行[${i}]: ${line} -> ${dateResult.toISOString()}`)\n }\n continue\n }\n\n // 尝试解析消息行\n const msgPattern = useTabSeparator ? PRIVATE_MSG_PATTERN : GROUP_MSG_PATTERN\n const msgMatch = line.match(msgPattern)\n\n // 调试前几行\n if (debugLogCount < 5 && line.trim()) {\n onLog?.('debug', `LINE 行[${i}]: \"${line.substring(0, 50)}...\" match=${!!msgMatch}`)\n debugLogCount++\n }\n\n if (msgMatch) {\n const [, timeStr, sender, contentRaw] = msgMatch\n let content = contentRaw.trim()\n\n // 处理 LINE 导出的多行消息格式(用双引号包裹)\n let isQuotedMultiline = false\n if (content.startsWith('\"')) {\n content = content.substring(1) // 移除开头的引号\n isQuotedMultiline = !content.endsWith('\"') // 单行带引号则直接处理\n if (content.endsWith('\"')) {\n content = content.substring(0, content.length - 1) // 移除结尾引号\n }\n }\n\n // 解析时间(支持中文上午/下午、日文午前/午後、英文am/pm)\n let hours = 0\n let minutes = 0\n\n const prefix = timeStr.match(/^(上午|下午|午前|午後)/)?.[1]\n const cleanTime = timeStr.replace(/^(上午|下午|午前|午後)/, '')\n const partsMatch = cleanTime.match(/^(\\d{1,2}):(\\d{2})([AaPp][Mm])?$/i)\n\n if (partsMatch) {\n hours = parseInt(partsMatch[1])\n minutes = parseInt(partsMatch[2])\n const suffix = partsMatch[3]?.toLowerCase()\n\n // 英文后缀\n if (suffix === 'pm' && hours < 12) hours += 12\n if (suffix === 'am' && hours === 12) hours = 0\n\n // 中文/日文前缀 (下午/午後 = PM, 上午/午前 = AM)\n if ((prefix === '下午' || prefix === '午後') && hours < 12) hours += 12\n if ((prefix === '上午' || prefix === '午前') && hours === 12) hours = 0\n } else {\n // Fallback\n const parts = timeStr.split(':').map(Number)\n hours = parts[0]\n minutes = parts[1]\n }\n\n let timestamp: number\n\n if (currentDate) {\n const msgDate = new Date(currentDate)\n msgDate.setHours(hours, minutes, 0, 0)\n timestamp = Math.floor(msgDate.getTime() / 1000)\n } else {\n // 如果没有日期,使用当前日期\n const now = new Date()\n now.setHours(hours, minutes, 0, 0)\n timestamp = Math.floor(now.getTime() / 1000)\n }\n\n // 检测消息类型\n const msgType = detectMessageType(content)\n\n // 更新成员信息\n if (!memberMap.has(sender)) {\n memberMap.set(sender, {\n platformId: sender,\n accountName: sender,\n })\n }\n\n // 创建消息\n lastMessage = {\n senderPlatformId: sender,\n senderAccountName: sender,\n timestamp,\n type: msgType,\n content: content || null,\n _isQuotedMultiline: isQuotedMultiline, // 临时标记,用于追加多行内容时处理结尾引号\n } as ParsedMessage & { _isQuotedMultiline?: boolean }\n messages.push(lastMessage)\n messagesProcessed++\n\n // 更新进度\n if (messagesProcessed % 1000 === 0) {\n const progress = createProgress(\n 'parsing',\n i,\n lines.length,\n messagesProcessed,\n `已处理 ${messagesProcessed} 条消息...`\n )\n onProgress?.(progress)\n }\n } else {\n // 尝试解析系统消息(双 Tab)\n const systemMatch = line.match(SYSTEM_MSG_PATTERN)\n if (systemMatch) {\n const [, timeStr, contentRaw] = systemMatch\n const content = contentRaw.trim()\n\n // 解析时间(支持中文、日文、英文)\n let hours = 0\n let minutes = 0\n\n const prefix = timeStr.match(/^(上午|下午|午前|午後)/)?.[1]\n const cleanTime = timeStr.replace(/^(上午|下午|午前|午後)/, '')\n const partsMatch = cleanTime.match(/^(\\d{1,2}):(\\d{2})([AaPp][Mm])?$/i)\n\n if (partsMatch) {\n hours = parseInt(partsMatch[1])\n minutes = parseInt(partsMatch[2])\n const suffix = partsMatch[3]?.toLowerCase()\n\n if (suffix === 'pm' && hours < 12) hours += 12\n if (suffix === 'am' && hours === 12) hours = 0\n\n if ((prefix === '下午' || prefix === '午後') && hours < 12) hours += 12\n if ((prefix === '上午' || prefix === '午前') && hours === 12) hours = 0\n } else {\n const parts = timeStr.split(':').map(Number)\n hours = parts[0]\n minutes = parts[1]\n }\n\n let timestamp: number\n if (currentDate) {\n const msgDate = new Date(currentDate)\n msgDate.setHours(hours, minutes, 0, 0)\n timestamp = Math.floor(msgDate.getTime() / 1000)\n } else {\n const now = new Date()\n now.setHours(hours, minutes, 0, 0)\n timestamp = Math.floor(now.getTime() / 1000)\n }\n\n // 创建系统消息\n lastMessage = {\n senderPlatformId: 'system',\n senderAccountName: '系統',\n timestamp,\n type: MessageType.SYSTEM,\n content: content || null,\n }\n messages.push(lastMessage)\n messagesProcessed++\n } else if (line.trim() && lastMessage) {\n // 非消息行,追加到上一条消息(多行内容)\n let appendLine = line\n const quotedMsg = lastMessage as ParsedMessage & { _isQuotedMultiline?: boolean }\n\n // 检查是否为带引号多行消息的最后一行(以 \" 结尾)\n if (quotedMsg._isQuotedMultiline && appendLine.endsWith('\"')) {\n appendLine = appendLine.substring(0, appendLine.length - 1) // 移除结尾引号\n delete quotedMsg._isQuotedMultiline // 清除临时标记\n }\n\n if (lastMessage.content) {\n lastMessage.content += '\\n' + appendLine\n } else {\n lastMessage.content = appendLine\n }\n }\n }\n }\n\n // 根据成员数判断聊天类型\n const memberCount = memberMap.size\n const chatType = memberCount <= 2 ? ChatType.PRIVATE : ChatType.GROUP\n\n // 发送 meta\n const meta: ParsedMeta = {\n name: chatName,\n platform: KNOWN_PLATFORMS.LINE,\n type: chatType,\n }\n yield { type: 'meta', data: meta }\n\n // 发送成员\n const members = Array.from(memberMap.values())\n yield { type: 'members', data: members }\n\n // 分批发送消息\n for (let i = 0; i < messages.length; i += batchSize) {\n const batch = messages.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} 条消息, ${memberCount} 个成员`)\n\n yield {\n type: 'done',\n data: { messageCount: messagesProcessed, memberCount },\n }\n}\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","/**\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 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 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 instagramNative,\n whatsappNativeTxt,\n qqNativeTxt,\n lineNativeTxt,\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, nickname history tracking, and FTS indexing.\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 { CHAT_DB_INDEXES } 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 type ParsedMeta,\n type FormatFeature,\n type ParseProgress,\n} from '@openchatlab/parser'\nimport * as fs from 'fs'\nimport { buildFtsIndex } from '../fts'\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 messagesSkipped: number\n skipReasons: SkipReasons\n}\n\nexport interface StreamImportResult {\n success: boolean\n sessionId?: 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}\n\n// ==================== Core streaming import ====================\n\nconst BATCH_COMMIT_SIZE = 50000\nconst CHECKPOINT_INTERVAL = 200000\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\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 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 }\n\n const candidates = detectAllFormats(filePath)\n if (candidates.length === 0) {\n return { success: false, error: 'error.unrecognized_format' }\n }\n\n if (candidates.length > 1) {\n return streamImportWithFallback(filePath, deps, candidates, formatOptions, externalSessionId)\n }\n\n return streamImportSingle(filePath, deps, candidates[0], formatOptions, externalSessionId)\n}\n\nasync function streamImportWithFallback(\n filePath: string,\n deps: StreamImportDeps,\n candidates: FormatFeature[],\n formatOptions?: Record<string, unknown>,\n externalSessionId?: string\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)\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): Promise<StreamImportResult> {\n const { onProgress, logger } = deps\n const genId = deps.generateSessionId ?? defaultGenerateSessionId\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 preprocessor = getPreprocessor(filePath)\n\n if (preprocessor && needsPreprocess(filePath)) {\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 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 }\n }\n\n const db = deps.openDatabase(sessionId)\n\n const insertMeta = db.prepare(\n `INSERT INTO meta (name, platform, type, imported_at, 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, roles) VALUES (?, ?, ?, ?, ?)`\n )\n const getMemberId = db.prepare(`SELECT id FROM member WHERE platform_id = ?`)\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 const insertNameHistory = db.prepare(\n `INSERT INTO member_name_history (member_id, name_type, name, start_ts, end_ts) VALUES (?, ?, ?, ?, ?)`\n )\n const updateMemberAccountName = db.prepare(`UPDATE member SET account_name = ? WHERE platform_id = ?`)\n const updateMemberGroupNickname = db.prepare(`UPDATE member SET group_nickname = ? WHERE platform_id = ?`)\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 messageCountInBatch = 0\n let totalMessageCount = 0\n let lastCheckpointCount = 0\n let inTransaction = false\n\n const beginTransaction = () => {\n if (!inTransaction) {\n db.exec('BEGIN TRANSACTION')\n inTransaction = true\n }\n }\n\n const doCheckpoint = () => {\n try {\n db.pragma('wal_checkpoint(TRUNCATE)')\n } catch {\n /* ignore */\n }\n }\n\n const commitAndBeginNew = () => {\n if (inTransaction) {\n db.exec('COMMIT')\n inTransaction = false\n logger?.perf('Commit transaction', totalMessageCount, BATCH_COMMIT_SIZE)\n\n if (totalMessageCount - lastCheckpointCount >= CHECKPOINT_INTERVAL) {\n doCheckpoint()\n logger?.perf('WAL checkpoint', totalMessageCount)\n lastCheckpointCount = totalMessageCount\n }\n\n onProgress({\n stage: 'importing',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n }\n beginTransaction()\n }\n\n beginTransaction()\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\n logger?.info('Starting streamParseFile...')\n\n try {\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 callbackStats.onMetaCalls++\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 },\n\n onMembers: (members: ParsedMember[]) => {\n callbackStats.onMembersCalls++\n callbackStats.totalMembersReceived += members.length\n logger?.info(`Received member batch: ${members.length} members`)\n for (const member of members) {\n insertMember.run(\n member.platformId,\n member.accountName || null,\n member.groupNickname || null,\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 },\n\n onMessageBatch: (messages: ParsedMessage[]) => {\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 let memberLookupTime = 0\n let memberInsertTime = 0\n let messageInsertTime = 0\n let nicknameTrackTime = 0\n let memberLookupCount = 0\n let memberInsertCount = 0\n let nicknameChangeCount = 0\n\n for (const msg of messages) {\n if (!msg.senderPlatformId) {\n callbackStats.skippedNoSenderId++\n continue\n }\n if (!msg.senderAccountName) {\n callbackStats.skippedNoAccountName++\n continue\n }\n if (msg.timestamp === undefined || msg.timestamp === null || isNaN(msg.timestamp)) {\n callbackStats.skippedInvalidTimestamp++\n continue\n }\n if (msg.type === undefined || msg.type === null) {\n callbackStats.skippedNoType++\n continue\n }\n\n let t0 = Date.now()\n if (!memberIdMap.has(msg.senderPlatformId)) {\n insertMember.run(\n msg.senderPlatformId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\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 memberInsertCount++\n memberInsertTime += Date.now() - t0\n } else {\n memberLookupCount++\n memberLookupTime += Date.now() - t0\n }\n\n const senderId = memberIdMap.get(msg.senderPlatformId)\n if (senderId === undefined) continue\n\n let safeContent: string | null = null\n if (msg.content != null) {\n safeContent = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)\n }\n\n t0 = Date.now()\n insertMessage.run(\n senderId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\n msg.timestamp,\n msg.type,\n safeContent,\n msg.replyToMessageId || null,\n msg.platformMessageId || null\n )\n messageInsertTime += Date.now() - t0\n messageCountInBatch++\n totalMessageCount++\n\n t0 = Date.now()\n trackNickname(accountNameTracker, msg.senderPlatformId, msg.senderAccountName, msg.timestamp)\n trackNickname(groupNicknameTracker, msg.senderPlatformId, msg.senderGroupNickname, msg.timestamp)\n nicknameTrackTime += Date.now() - t0\n // nicknameChangeCount is approximate but sufficient for logging\n nicknameChangeCount += accountNameTracker.get(msg.senderPlatformId)?.history.length === 1 ? 0 : 0\n\n if (messageCountInBatch >= BATCH_COMMIT_SIZE) {\n const detail =\n `[Detail] Member lookup: ${memberLookupTime}ms (${memberLookupCount} times) | ` +\n `Member insert: ${memberInsertTime}ms (${memberInsertCount} times) | ` +\n `Message insert: ${messageInsertTime}ms | ` +\n `Nickname tracking: ${nicknameTrackTime}ms (${nicknameChangeCount} changes)`\n logger?.perfDetail(detail)\n commitAndBeginNew()\n messageCountInBatch = 0\n memberLookupTime = 0\n memberInsertTime = 0\n messageInsertTime = 0\n nicknameTrackTime = 0\n memberLookupCount = 0\n memberInsertCount = 0\n nicknameChangeCount = 0\n }\n }\n },\n },\n formatFeature.id\n )\n\n if (inTransaction) {\n db.exec('COMMIT')\n inTransaction = false\n }\n\n // Flush nickname history in batch\n onProgress({\n stage: 'importing',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n logger?.perf('Writing nickname history', totalMessageCount)\n\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 logger?.perf(`Nickname history written (${historyCount} entries)`, totalMessageCount)\n\n // Create indexes (deferred for performance)\n onProgress({\n stage: 'importing',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n logger?.perf('Creating indexes', totalMessageCount)\n db.exec(CHAT_DB_INDEXES)\n logger?.perf('Indexes created', totalMessageCount)\n\n // Build FTS index\n try {\n buildFtsIndex(db)\n logger?.perf('FTS index built', totalMessageCount)\n } catch (ftsError) {\n logger?.error('FTS index build failed (non-fatal)', ftsError instanceof Error ? ftsError : undefined)\n }\n\n // Final WAL checkpoint\n onProgress({\n stage: 'importing',\n bytesRead: 0,\n totalBytes: 0,\n messagesProcessed: totalMessageCount,\n percentage: 100,\n message: '',\n })\n doCheckpoint()\n logger?.perf('WAL checkpoint done', totalMessageCount)\n\n // Post-import hook (e.g. overview cache)\n try {\n await deps.postImportHook?.(db, sessionId)\n if (deps.postImportHook) logger?.perf('Post-import hook done', totalMessageCount)\n } catch {\n /* non-fatal */\n }\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\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 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 }\n\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 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 }\n\n if (importError) {\n return { success: false, error: importError, diagnostics }\n }\n return { success: true, sessionId, diagnostics }\n}\n\n// ==================== Dry-run analysis ====================\n\nexport interface AnalyzeNewImportResult {\n totalMessages: number\n totalMembers: number\n meta: { name: string; platform: string; type: string } | null\n error?: string\n}\n\nexport async function analyzeNewImport(\n filePath: string,\n onProgress: ImportProgressCallback\n): Promise<AnalyzeNewImportResult> {\n const formatFeature = detectFormat(filePath)\n if (!formatFeature) {\n return { totalMessages: 0, totalMembers: 0, meta: null, error: 'error.unrecognized_format' }\n }\n\n let meta: { name: string; platform: string; type: string } | null = null\n const memberSet = new Set<string>()\n let totalMessages = 0\n\n await streamParseFile(filePath, {\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 },\n })\n\n return { totalMessages, 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 (sender_platform_id, sender_account_name, sender_group_nickname, timestamp, type, content)\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.senderPlatformId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\n msg.timestamp,\n msg.type,\n msg.content || 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","/**\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 } from '@openchatlab/core'\nimport { generateMessageKey } from '@openchatlab/core'\nimport { streamParseFile, detectFormat, type ParseProgress } from '@openchatlab/parser'\nimport { insertFtsEntries, hasFtsTable } from '../fts'\nimport type { ImportProgressCallback } from './streaming-importer'\n\n// ==================== Public interfaces ====================\n\nexport interface ImportOptions {\n metaUpdateMode?: 'patch' | 'none'\n memberUpdateMode?: 'upsert' | 'none'\n}\n\nexport interface IncrementalAnalyzeResult {\n newMessageCount: number\n duplicateCount: number\n totalInFile: number\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 hook after incremental import (e.g. update overview cache). */\n postImportHook?: (db: DatabaseAdapter, sessionId: string) => void | Promise<void>\n}\n\n// ==================== Internal helpers ====================\n\nfunction loadExistingDedup(db: DatabaseAdapter): {\n existingPlatformMsgIds: Set<string>\n existingKeys: Set<string>\n} {\n const existingPlatformMsgIds = new Set<string>()\n const existingKeys = new Set<string>()\n\n const pmidRows = db\n .prepare('SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL')\n .all() as Array<{ platform_message_id: string }>\n for (const row of pmidRows) {\n existingPlatformMsgIds.add(row.platform_message_id)\n }\n\n const hashRows = db\n .prepare(\n `SELECT ts, m.platform_id as sender_platform_id, content\n FROM message msg\n JOIN member m ON msg.sender_id = m.id`\n )\n .all() as Array<{ ts: number; sender_platform_id: string; content: string | null }>\n for (const row of hashRows) {\n existingKeys.add(generateMessageKey(row.ts, row.sender_platform_id, row.content))\n }\n\n return { existingPlatformMsgIds, existingKeys }\n}\n\nfunction isDuplicate(\n msg: { platformMessageId?: string; timestamp: number; senderPlatformId: string; content: string | null },\n existingPlatformMsgIds: Set<string>,\n existingKeys: Set<string>\n): boolean {\n if (msg.platformMessageId) {\n if (existingPlatformMsgIds.has(msg.platformMessageId)) return true\n existingPlatformMsgIds.add(msg.platformMessageId)\n return false\n }\n const key = generateMessageKey(msg.timestamp, msg.senderPlatformId, msg.content)\n if (existingKeys.has(key)) return true\n existingKeys.add(key)\n return false\n}\n\nfunction normalizeTimestamp(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): Promise<IncrementalAnalyzeResult> {\n const formatFeature = 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 const { existingPlatformMsgIds, existingKeys } = loadExistingDedup(db)\n db.close()\n\n let totalInFile = 0\n let newMessageCount = 0\n let duplicateCount = 0\n\n await streamParseFile(filePath, {\n onMeta: () => {},\n onMembers: () => {},\n onProgress: (progress: ParseProgress) => {\n deps.onProgress(progress)\n },\n onMessageBatch: (batch) => {\n for (const msg of batch) {\n totalInFile++\n const timestamp = normalizeTimestamp(msg.timestamp)\n if (timestamp === null) continue\n\n if (isDuplicate({ ...msg, timestamp }, existingPlatformMsgIds, existingKeys)) {\n duplicateCount++\n } else {\n newMessageCount++\n }\n }\n },\n })\n\n return { newMessageCount, duplicateCount, totalInFile }\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 = 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\n try {\n const { existingPlatformMsgIds, existingKeys } = loadExistingDedup(db)\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, 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 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 db.exec('BEGIN TRANSACTION')\n\n let newMessageCount = 0\n let duplicateCount = 0\n let processedCount = 0\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 const newFtsEntries: Array<{ id: number; content: string | null }> = []\n\n await streamParseFile(filePath, {\n onMeta: (meta) => {\n if (metaUpdateMode === 'none') return\n updateMeta.run(\n meta.name || '',\n meta.groupId || '',\n meta.groupAvatar || '',\n 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 existed = memberIdMap.has(m.platformId)\n upsertMember.run(\n m.platformId,\n m.accountName || null,\n m.groupNickname || null,\n m.avatar || null,\n m.roles ? JSON.stringify(m.roles) : '[]'\n )\n if (!existed) {\n const row = getMemberId.get(m.platformId) as { id: number } | undefined\n if (row) memberIdMap.set(m.platformId, row.id)\n membersAdded++\n } else {\n membersUpdated++\n }\n }\n },\n onProgress: (progress: ParseProgress) => {\n deps.onProgress(progress)\n },\n onMessageBatch: (batch) => {\n for (const msg of batch) {\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 = normalizeTimestamp(msg.timestamp)\n if (timestamp === null) {\n trackError(processedCount, 'INVALID_TIMESTAMP', `timestamp value: ${msg.timestamp}`)\n continue\n }\n\n if (isDuplicate({ ...msg, timestamp }, existingPlatformMsgIds, existingKeys)) {\n duplicateCount++\n continue\n }\n\n let memberId = memberIdMap.get(msg.senderPlatformId)\n if (!memberId) {\n insertMemberMinimal.run(\n msg.senderPlatformId,\n msg.senderAccountName || null,\n msg.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 const msgResult = insertMessage.run(\n memberId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\n timestamp,\n msg.type,\n msg.content || null,\n msg.replyToMessageId || null,\n msg.platformMessageId || null\n )\n\n newFtsEntries.push({\n id: Number((msgResult as any).lastInsertRowid ?? 0),\n content: msg.content || null,\n })\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\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 FTS update\n if (newFtsEntries.length > 0 && hasFtsTable(db)) {\n try {\n insertFtsEntries(db, newFtsEntries)\n } catch {\n /* FTS failure is 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.prepare('SELECT COUNT(*) as count FROM member').get() 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","/**\n * PathProvider 的 Node.js 独立实现\n *\n * 为 CLI / npm 服务版提供路径管理,不依赖 Electron。\n *\n * 目录分为两类:\n * - 系统数据:固定在 ~/.chatlab/,存放配置、日志、缓存、AI 数据等\n * - 用户数据:可配置位置,存放聊天记录数据库等核心资产\n *\n * 用户数据目录(userDataDir)解析优先级:\n * 1. 构造函数传入的 userDataDir 参数\n * 2. CHATLAB_DATA_DIR 环境变量\n * 3. ~/.chatlab/config.toml → [data] user_data_dir\n * 4. 平台默认路径\n */\n\nimport * as fs from 'fs'\nimport * as os from 'os'\nimport * as path from 'path'\nimport type { PathProvider } from '@openchatlab/core'\nimport { writeConfigField, loadConfig } from '@openchatlab/config'\nimport { migrateFromElectronIfNeeded } from './migrations/electron-data-migration'\nimport { applyPendingNodeDataDirMigration } from './data-dir-switch'\n\nconst SYSTEM_DIR = path.join(os.homedir(), '.chatlab')\n\n/**\n * Set after resolveUserDataDir() detects Electron installation but cannot find databases.\n * CLI should check this and block execution with instructions.\n */\nlet _pendingElectronDataWarning = false\n\nexport function hasPendingElectronDataWarning(): boolean {\n return _pendingElectronDataWarning\n}\n\nexport function applyPendingNodeDataDirMigrationIfNeeded(): { success: boolean; skipped?: boolean; error?: string } {\n if (process.env.CHATLAB_DATA_DIR) return { success: true, skipped: true }\n return applyPendingNodeDataDirMigration(SYSTEM_DIR)\n}\n\nexport class NodePathProvider implements PathProvider {\n private systemDir: string\n private userDataDir: string\n\n constructor(userDataDir?: string) {\n this.systemDir = SYSTEM_DIR\n this.userDataDir = userDataDir || resolveUserDataDir()\n }\n\n getSystemDir(): string {\n return this.systemDir\n }\n\n getUserDataDir(): string {\n return this.userDataDir\n }\n\n getDatabaseDir(): string {\n return path.join(this.userDataDir, 'databases')\n }\n\n getVectorDir(): string {\n return path.join(this.userDataDir, 'vector')\n }\n\n getAiDataDir(): string {\n return path.join(this.systemDir, 'ai')\n }\n\n getSettingsDir(): string {\n return path.join(this.systemDir, 'settings')\n }\n\n getCacheDir(): string {\n return path.join(this.systemDir, 'cache')\n }\n\n getTempDir(): string {\n return path.join(this.systemDir, 'temp')\n }\n\n getLogsDir(): string {\n return path.join(this.systemDir, 'logs')\n }\n\n getDownloadsDir(): string {\n return path.join(os.homedir(), 'Downloads')\n }\n\n /**\n * 确保系统目录和用户数据目录都存在\n */\n ensureAllDirs(): void {\n const dirs = [\n this.systemDir,\n this.userDataDir,\n this.getDatabaseDir(),\n this.getVectorDir(),\n this.getAiDataDir(),\n this.getSettingsDir(),\n this.getCacheDir(),\n this.getTempDir(),\n this.getLogsDir(),\n ]\n for (const dir of dirs) {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n }\n\n const markerPath = path.join(this.userDataDir, '.chatlab')\n if (!fs.existsSync(markerPath)) {\n fs.writeFileSync(markerPath, 'ChatLab Data Directory', 'utf-8')\n }\n }\n}\n\nfunction resolveUserDataDir(): string {\n const envDir = process.env.CHATLAB_DATA_DIR\n if (envDir) {\n return expandHome(envDir)\n }\n\n const config = loadConfig()\n if (config.data.user_data_dir) {\n return expandHome(config.data.user_data_dir)\n }\n\n // First run: check for existing Electron desktop data before using default\n const result = migrateFromElectronIfNeeded(SYSTEM_DIR)\n if (result.migrated && result.userDataDir) {\n return result.userDataDir\n }\n\n const defaultDir = getDefaultNodeUserDataDir()\n\n if (result.electronDetected) {\n // Electron was used but we couldn't find databases — likely a custom data directory.\n // Don't persist default to config.toml so migration retries on next startup.\n _pendingElectronDataWarning = true\n return defaultDir\n }\n\n writeConfigField('data', 'user_data_dir', defaultDir)\n return defaultDir\n}\n\nexport function getDefaultNodeUserDataDir(): string {\n return path.join(os.homedir(), '.chatlab', 'data')\n}\n\nfunction expandHome(filePath: string): string {\n if (filePath.startsWith('~/') || filePath === '~') {\n return path.join(os.homedir(), filePath.slice(1))\n }\n return filePath\n}\n","import * as fs from 'fs'\nimport * as path from 'path'\nimport { writeConfigField } from '@openchatlab/config'\n\nconst CHATLAB_MARKER_FILE = '.chatlab'\nconst USER_DATA_REQUIRED_DIRS = ['databases']\nconst PENDING_MIGRATION_FILE = 'data-dir-migration.json'\n\nconst DANGEROUS_PATHS = [\n 'C:\\\\Windows',\n 'C:\\\\Program Files',\n 'C:\\\\Program Files (x86)',\n 'C:\\\\ProgramData',\n '/usr',\n '/etc',\n '/bin',\n '/sbin',\n '/lib',\n '/var',\n '/boot',\n '/root',\n '/System',\n '/Library',\n]\n\nexport interface CopyStats {\n copied: number\n skipped: number\n errors: string[]\n}\n\nexport interface PendingDataDirMigration {\n from: string\n to: string\n migrate: boolean\n deleteSourceOnSuccess: boolean\n createdAt: string\n}\n\nexport interface RunPendingDataDirMigrationDeps {\n copyDirMerge?: typeof copyDirMerge\n ensureDir?: (dirPath: string) => void\n writeUserDataDir: (dir: string) => void\n clearPendingMigration: () => void\n markPendingDeleteDir?: (dir: string) => void\n log?: (message: string) => void\n}\n\nexport interface RunPendingDataDirMigrationResult {\n success: boolean\n from: string\n to: string\n copied: number\n skipped: number\n errors: string[]\n}\n\nexport interface DataDirSwitchResult {\n success: boolean\n error?: string\n from?: string\n to?: string\n requiresRelaunch?: boolean\n}\n\nexport interface ApplyPendingNodeDataDirMigrationDeps {\n writeConfigField?: typeof writeConfigField\n removeDir?: (dir: string) => void\n}\n\nfunction normalizePathForCompare(input: string): string {\n const resolved = path.resolve(input)\n const normalized = path.normalize(resolved)\n return process.platform === 'win32' ? normalized.toLowerCase() : normalized\n}\n\nfunction isSubPath(parent: string, child: string): boolean {\n const parentPath = normalizePathForCompare(parent)\n const childPath = normalizePathForCompare(child)\n\n if (parentPath === childPath) return false\n return childPath.startsWith(`${parentPath}${path.sep}`)\n}\n\nfunction isPathSafe(targetPath: string): boolean {\n const normalizedTarget = targetPath.toLowerCase().replace(/\\//g, '\\\\')\n\n for (const dangerous of DANGEROUS_PATHS) {\n const normalizedDangerous = dangerous.toLowerCase().replace(/\\//g, '\\\\')\n if (normalizedTarget.startsWith(normalizedDangerous)) {\n return false\n }\n }\n\n return true\n}\n\nfunction ensureDir(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true })\n }\n}\n\nfunction hasChatLabUserDataStructure(entries: string[]): boolean {\n return entries.includes(CHATLAB_MARKER_FILE) && USER_DATA_REQUIRED_DIRS.every((dir) => entries.includes(dir))\n}\n\nfunction hasChatLabDatabaseFiles(dirPath: string, entries?: string[]): boolean {\n const dirEntries = entries ?? fs.readdirSync(dirPath)\n if (!dirEntries.includes('databases')) return false\n\n const dbDir = path.join(dirPath, 'databases')\n try {\n return fs.existsSync(dbDir) && fs.readdirSync(dbDir).some((file) => file.endsWith('.db'))\n } catch {\n return false\n }\n}\n\nexport function isExistingUserDataDir(dirPath: string): boolean {\n if (!fs.existsSync(dirPath)) return false\n\n try {\n const entries = fs.readdirSync(dirPath)\n return hasChatLabUserDataStructure(entries) || hasChatLabDatabaseFiles(dirPath, entries)\n } catch {\n return false\n }\n}\n\nexport function isUserDataDirSafeToUse(dirPath: string): boolean {\n if (!fs.existsSync(dirPath)) return true\n\n try {\n const entries = fs.readdirSync(dirPath)\n if (entries.length === 0) return true\n return hasChatLabUserDataStructure(entries) || hasChatLabDatabaseFiles(dirPath, entries)\n } catch {\n return false\n }\n}\n\nexport function isDirectoryEmptyOrMissing(dirPath: string): boolean {\n if (!fs.existsSync(dirPath)) return true\n\n try {\n return fs.readdirSync(dirPath).length === 0\n } catch {\n return false\n }\n}\n\nexport function copyDirMerge(\n src: string,\n dest: string,\n mkdir: (dirPath: string) => void = ensureDir,\n stats: CopyStats = { copied: 0, skipped: 0, errors: [] }\n): CopyStats {\n if (!fs.existsSync(src)) return stats\n\n try {\n mkdir(dest)\n const entries = fs.readdirSync(src, { withFileTypes: true })\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name)\n const destPath = path.join(dest, entry.name)\n\n try {\n if (entry.isDirectory()) {\n copyDirMerge(srcPath, destPath, mkdir, stats)\n } else if (!fs.existsSync(destPath)) {\n fs.copyFileSync(srcPath, destPath)\n stats.copied++\n } else {\n stats.skipped++\n }\n } catch (error) {\n stats.errors.push(`复制失败: ${srcPath} -> ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n } catch (error) {\n stats.errors.push(`读取目录失败: ${src} -> ${error instanceof Error ? error.message : String(error)}`)\n }\n\n return stats\n}\n\nexport function createPendingDataDirMigration(input: {\n from: string\n to: string\n migrate: boolean\n targetWasEmpty: boolean\n}): PendingDataDirMigration {\n return {\n from: input.from,\n to: input.to,\n migrate: input.migrate,\n deleteSourceOnSuccess: input.migrate && input.targetWasEmpty,\n createdAt: new Date().toISOString(),\n }\n}\n\nexport function runPendingDataDirMigration(\n pending: PendingDataDirMigration,\n deps: RunPendingDataDirMigrationDeps\n): RunPendingDataDirMigrationResult {\n const copy = deps.copyDirMerge ?? copyDirMerge\n const mkdir = deps.ensureDir ?? ensureDir\n\n let stats: CopyStats = { copied: 0, skipped: 0, errors: [] }\n if (pending.migrate && path.resolve(pending.from) !== path.resolve(pending.to)) {\n if (!fs.existsSync(pending.from)) {\n return {\n success: false,\n from: pending.from,\n to: pending.to,\n copied: 0,\n skipped: 0,\n errors: [`源数据目录不存在: ${pending.from}`],\n }\n }\n\n stats = copy(pending.from, pending.to, mkdir)\n deps.log?.(\n `数据目录迁移完成: 从 ${pending.from} 到 ${pending.to},复制 ${stats.copied} 项,跳过 ${stats.skipped} 项,错误 ${stats.errors.length} 项`\n )\n if (stats.errors.length > 0) {\n return {\n success: false,\n from: pending.from,\n to: pending.to,\n copied: stats.copied,\n skipped: stats.skipped,\n errors: stats.errors,\n }\n }\n } else {\n mkdir(pending.to)\n }\n\n deps.writeUserDataDir(pending.to)\n deps.clearPendingMigration()\n\n if (pending.deleteSourceOnSuccess && path.resolve(pending.from) !== path.resolve(pending.to)) {\n deps.markPendingDeleteDir?.(pending.from)\n }\n\n return {\n success: true,\n from: pending.from,\n to: pending.to,\n copied: stats.copied,\n skipped: stats.skipped,\n errors: [],\n }\n}\n\nfunction getPendingMigrationPath(systemDir: string): string {\n return path.join(systemDir, 'settings', PENDING_MIGRATION_FILE)\n}\n\nexport function getPendingNodeDataDirMigration(systemDir: string): PendingDataDirMigration | null {\n const filePath = getPendingMigrationPath(systemDir)\n if (!fs.existsSync(filePath)) return null\n\n try {\n const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as PendingDataDirMigration\n if (!parsed.from || !parsed.to || typeof parsed.migrate !== 'boolean') return null\n return parsed\n } catch {\n return null\n }\n}\n\nexport function clearPendingNodeDataDirMigration(systemDir: string): void {\n const filePath = getPendingMigrationPath(systemDir)\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath)\n }\n}\n\n// 只删除确认仍是 ChatLab 用户数据目录的旧路径,避免误删用户选择的普通目录。\nexport function deleteOldUserDataDirIfSafe(\n dirPath: string,\n currentDir: string,\n removeDir?: (dir: string) => void\n): boolean {\n if (path.resolve(dirPath) === path.resolve(currentDir)) return false\n if (!isPathSafe(dirPath)) return false\n if (!fs.existsSync(dirPath)) return false\n if (!isExistingUserDataDir(dirPath)) return false\n\n const remove = removeDir ?? ((dir: string) => fs.rmSync(dir, { recursive: true, force: true }))\n remove(dirPath)\n return true\n}\n\nfunction writePendingNodeDataDirMigration(systemDir: string, pending: PendingDataDirMigration): void {\n const filePath = getPendingMigrationPath(systemDir)\n ensureDir(path.dirname(filePath))\n fs.writeFileSync(filePath, JSON.stringify(pending, null, 2), 'utf-8')\n}\n\nexport function createNodeDataDirSwitch(input: {\n systemDir: string\n currentDir: string\n targetDir: string | null\n defaultDir?: string\n migrate?: boolean\n envDataDir?: string\n}): DataDirSwitchResult {\n if (input.envDataDir) {\n return { success: false, error: 'CHATLAB_DATA_DIR is set, data directory cannot be changed from Web UI' }\n }\n\n const newDir = (input.targetDir?.trim() || input.defaultDir || '').trim()\n if (!newDir) return { success: false, error: 'Data directory is required' }\n if (!path.isAbsolute(newDir)) return { success: false, error: 'Data directory must be an absolute path' }\n if (!isPathSafe(newDir)) return { success: false, error: 'System directories cannot be used as data directory' }\n\n const migrate = input.migrate !== false\n if (migrate && path.resolve(input.currentDir) !== path.resolve(newDir) && isSubPath(input.currentDir, newDir)) {\n return { success: false, error: 'Target directory cannot be inside current data directory' }\n }\n\n if (!isUserDataDirSafeToUse(newDir)) {\n return { success: false, error: 'Target directory is not empty and is not a ChatLab data directory' }\n }\n\n if (path.resolve(input.currentDir) === path.resolve(newDir)) {\n clearPendingNodeDataDirMigration(input.systemDir)\n return { success: true, from: input.currentDir, to: newDir, requiresRelaunch: false }\n }\n\n const pending = createPendingDataDirMigration({\n from: input.currentDir,\n to: newDir,\n migrate,\n targetWasEmpty: isDirectoryEmptyOrMissing(newDir),\n })\n writePendingNodeDataDirMigration(input.systemDir, pending)\n\n return { success: true, from: input.currentDir, to: newDir, requiresRelaunch: true }\n}\n\nexport function applyPendingNodeDataDirMigration(\n systemDir: string,\n deps: ApplyPendingNodeDataDirMigrationDeps = {}\n): {\n success: boolean\n skipped?: boolean\n error?: string\n} {\n const pending = getPendingNodeDataDirMigration(systemDir)\n if (!pending) return { success: true, skipped: true }\n const writeConfig = deps.writeConfigField ?? writeConfigField\n\n const result = runPendingDataDirMigration(pending, {\n writeUserDataDir(dir) {\n writeConfig('data', 'user_data_dir', dir)\n writeConfig('data', 'electron_migration_done', true)\n },\n clearPendingMigration() {\n clearPendingNodeDataDirMigration(systemDir)\n },\n markPendingDeleteDir(dir) {\n deleteOldUserDataDirIfSafe(dir, pending.to, deps.removeDir)\n },\n })\n\n if (!result.success) {\n return { success: false, error: result.errors.join('; ') || 'Data directory migration failed' }\n }\n\n return { success: true }\n}\n","/**\n * JiebaNlpProvider — NlpProvider 实现(基于 @node-rs/jieba)\n *\n * 提供给 @openchatlab/core 的 getLanguagePreferenceAnalysis 使用。\n * 仅在 Node.js 环境中可用(Electron worker / CLI server)。\n *\n * 使用 nlp/segmenter 的共享 getJieba() 实例,自动加载磁盘词库。\n */\n\nimport type { NlpProvider, PosTagResult } from '@openchatlab/core'\nimport { MEANINGFUL_POS_TAGS, isStopword } from '@openchatlab/core'\nimport { getJieba } from './nlp/segmenter'\nimport type { DictType } from '@openchatlab/core'\n\nexport function createJiebaNlpProvider(dictType: DictType = 'default'): NlpProvider {\n return {\n tag(text: string): PosTagResult[] {\n return getJieba(dictType).tag(text)\n },\n isStopword,\n meaningfulPosTags: MEANINGFUL_POS_TAGS,\n }\n}\n","/**\n * Analytics result cache (platform-agnostic).\n *\n * Caches expensive analytics / NLP computations (word frequency, catchphrase,\n * activity stats, ...) as JSON under {queryCacheDir}/{sessionId}.cache.json,\n * reusing the generic session-cache infrastructure.\n *\n * Each entry carries a `v` fingerprint derived from the session DB file state.\n * Any write to the chat DB (import, incremental import, member merge/rename,\n * owner change, summary generation) changes the file and thus the fingerprint,\n * forcing a recompute. This makes the cache self-validating even across\n * processes — e.g. when `chatlab start` is serving while a separate\n * `chatlab import` mutates the same database file.\n */\n\nimport * as fs from 'fs'\nimport { getCache, setCache } from './session-cache'\n\ninterface VersionedEntry<T> {\n v: string\n data: T\n}\n\n/**\n * Derive a cheap version fingerprint from the session DB file state.\n *\n * Combines mtime + size of the main DB file and its `-wal` sidecar so that\n * WAL-only writes (before a checkpoint) are detected too. Missing files\n * contribute a placeholder; a fully missing DB yields a stable placeholder\n * version, which is acceptable because the caller only computes once the DB\n * actually exists.\n */\nexport function getDbFileVersion(dbPath: string): string {\n const parts: string[] = []\n for (const p of [dbPath, `${dbPath}-wal`]) {\n try {\n const st = fs.statSync(p)\n parts.push(`${Math.floor(st.mtimeMs)}:${st.size}`)\n } catch {\n parts.push('-')\n }\n }\n return parts.join('|')\n}\n\n/**\n * Cache-first analytics read with version validation.\n *\n * Returns the cached value when the stored fingerprint matches `version`;\n * otherwise runs `compute()`, persists the result tagged with the current\n * version (overwriting any stale entry under the same key), and returns it.\n */\nexport function getOrComputeAnalysisCache<T>(\n sessionId: string,\n key: string,\n queryCacheDir: string,\n version: string,\n compute: () => T\n): T {\n const cached = getCache<VersionedEntry<T>>(sessionId, key, queryCacheDir)\n if (cached && cached.v === version) {\n return cached.data\n }\n const data = compute()\n setCache<VersionedEntry<T>>(sessionId, key, { v: version, data }, queryCacheDir)\n return data\n}\n","/**\n * PreferencesManager — manages ~/.chatlab/preferences.json\n *\n * Stores complex user preferences that cannot fit into config.toml\n * (arrays, nested objects) and need cross-client consistency.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { DESENSITIZE_RULES_SCHEMA_VERSION } from './ai/preprocessor'\nimport type {\n Preferences,\n AIGlobalSettings,\n AIPreprocessConfig,\n WordFilterScheme,\n KeywordTemplate,\n ContextCompressionSettings,\n DesensitizeRule,\n FilterHistoryItem,\n} from '@openchatlab/shared-types'\n\nexport type {\n Preferences,\n AIGlobalSettings,\n AIPreprocessConfig,\n WordFilterScheme,\n KeywordTemplate,\n ContextCompressionSettings,\n DesensitizeRule,\n FilterHistoryItem,\n}\n\nconst DEFAULTS: Preferences = {\n pinnedSessionIds: [],\n aiPreprocessConfig: {\n dataCleaning: true,\n mergeConsecutive: true,\n mergeWindowSeconds: 180,\n blacklistKeywords: [],\n denoise: true,\n desensitize: true,\n desensitizeRulesSchemaVersion: DESENSITIZE_RULES_SCHEMA_VERSION,\n desensitizeBuiltinRuleOverrides: {},\n desensitizeRules: [],\n anonymizeNames: false,\n },\n aiGlobalSettings: {\n maxMessagesPerRequest: 1000,\n exportFormat: 'markdown',\n sqlExportFormat: 'csv',\n enableAutoSkill: true,\n chartAutoMode: 'suggest',\n searchContextBefore: 2,\n searchContextAfter: 2,\n contextCompression: {\n enabled: true,\n tokenThresholdPercent: 75,\n bufferSizePercent: 20,\n maxToolResultPercent: 50,\n },\n },\n customKeywordTemplates: [],\n deletedPresetTemplateIds: [],\n wordFilter: {\n schemes: [],\n defaultSchemeId: null,\n sessionSchemeOverrides: {},\n },\n filterHistory: [],\n thinkingLevels: {},\n ownerProfilesByPlatform: {},\n ownerPromptDismissedSessionIds: [],\n}\n\nexport class PreferencesManager {\n private filePath: string\n private cache: Preferences | null = null\n\n constructor(systemDir: string) {\n this.filePath = path.join(systemDir, 'preferences.json')\n }\n\n load(): Preferences {\n if (this.cache) return this.cache\n\n try {\n if (fs.existsSync(this.filePath)) {\n const raw = fs.readFileSync(this.filePath, 'utf-8')\n const parsed = JSON.parse(raw) as Partial<Preferences>\n const migrated = this.migrateLegacyDesensitizeRules(parsed, raw)\n this.cache = this.mergeDefaults(migrated.preferences)\n if (migrated.changed) {\n this.writePreferences(this.cache)\n }\n return this.cache\n }\n } catch (err) {\n console.warn('[Preferences] Failed to load preferences.json:', err)\n }\n\n this.cache = { ...DEFAULTS }\n return this.cache\n }\n\n save(partial: Partial<Preferences>): { success: boolean; error?: string } {\n try {\n const current = this.load()\n const merged = this.deepMerge(\n current as unknown as Record<string, unknown>,\n partial as unknown as Record<string, unknown>\n )\n if (Object.prototype.hasOwnProperty.call(partial.aiPreprocessConfig ?? {}, 'desensitizeBuiltinRuleOverrides')) {\n const mergedAiPreprocessConfig = merged.aiPreprocessConfig as Record<string, unknown>\n mergedAiPreprocessConfig.desensitizeBuiltinRuleOverrides =\n partial.aiPreprocessConfig?.desensitizeBuiltinRuleOverrides\n }\n this.cache = this.mergeDefaults(merged as unknown as Partial<Preferences>)\n\n this.writePreferences(this.cache)\n return { success: true }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n console.error('[Preferences] Failed to save:', msg)\n return { success: false, error: msg }\n }\n }\n\n getFilePath(): string {\n return this.filePath\n }\n\n invalidateCache(): void {\n this.cache = null\n }\n\n private mergeDefaults(partial: Partial<Preferences>): Preferences {\n return {\n pinnedSessionIds: partial.pinnedSessionIds ?? DEFAULTS.pinnedSessionIds,\n aiPreprocessConfig: partial.aiPreprocessConfig\n ? this.normalizeAiPreprocessConfig(partial.aiPreprocessConfig)\n : { ...DEFAULTS.aiPreprocessConfig },\n aiGlobalSettings: partial.aiGlobalSettings\n ? {\n ...DEFAULTS.aiGlobalSettings,\n ...partial.aiGlobalSettings,\n contextCompression: {\n ...DEFAULTS.aiGlobalSettings.contextCompression,\n ...(partial.aiGlobalSettings.contextCompression ?? {}),\n },\n }\n : { ...DEFAULTS.aiGlobalSettings },\n customKeywordTemplates: partial.customKeywordTemplates ?? DEFAULTS.customKeywordTemplates,\n deletedPresetTemplateIds: partial.deletedPresetTemplateIds ?? DEFAULTS.deletedPresetTemplateIds,\n wordFilter: partial.wordFilter ? { ...DEFAULTS.wordFilter, ...partial.wordFilter } : { ...DEFAULTS.wordFilter },\n filterHistory: partial.filterHistory ?? DEFAULTS.filterHistory,\n thinkingLevels: partial.thinkingLevels ?? DEFAULTS.thinkingLevels,\n ownerProfilesByPlatform: partial.ownerProfilesByPlatform ?? DEFAULTS.ownerProfilesByPlatform,\n ownerPromptDismissedSessionIds: partial.ownerPromptDismissedSessionIds ?? DEFAULTS.ownerPromptDismissedSessionIds,\n }\n }\n\n private normalizeAiPreprocessConfig(partial: Partial<AIPreprocessConfig> | undefined): AIPreprocessConfig {\n const defaults = DEFAULTS.aiPreprocessConfig\n const rules = Array.isArray(partial?.desensitizeRules) ? partial.desensitizeRules : []\n const customRules = rules.filter((rule) => !rule.builtin)\n return {\n dataCleaning: partial?.dataCleaning ?? defaults.dataCleaning,\n mergeConsecutive: partial?.mergeConsecutive ?? defaults.mergeConsecutive,\n mergeWindowSeconds: partial?.mergeWindowSeconds ?? defaults.mergeWindowSeconds,\n blacklistKeywords: partial?.blacklistKeywords ?? defaults.blacklistKeywords,\n denoise: partial?.denoise ?? defaults.denoise,\n desensitize: partial?.desensitize ?? defaults.desensitize,\n desensitizeRulesSchemaVersion: DESENSITIZE_RULES_SCHEMA_VERSION,\n desensitizeBuiltinRuleOverrides: this.normalizeBooleanMap(partial?.desensitizeBuiltinRuleOverrides),\n desensitizeRules: customRules,\n anonymizeNames: partial?.anonymizeNames ?? defaults.anonymizeNames,\n }\n }\n\n private normalizeBooleanMap(value: unknown): Record<string, boolean> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return {}\n const result: Record<string, boolean> = {}\n for (const [key, flag] of Object.entries(value as Record<string, unknown>)) {\n if (typeof flag === 'boolean') result[key] = flag\n }\n return result\n }\n\n private migrateLegacyDesensitizeRules(\n partial: Partial<Preferences>,\n rawContent: string\n ): { preferences: Partial<Preferences>; changed: boolean } {\n const rules = partial.aiPreprocessConfig?.desensitizeRules\n if (!Array.isArray(rules) || !rules.some((rule) => rule.builtin)) {\n return { preferences: partial, changed: false }\n }\n\n this.backupLegacyPreferences(rawContent)\n\n const legacyBuiltinRuleOverrides = rules\n .filter((rule) => rule.builtin && typeof rule.enabled === 'boolean')\n .reduce<Record<string, boolean>>((overrides, rule) => {\n overrides[rule.id] = rule.enabled\n return overrides\n }, {})\n const existingOverrides = this.normalizeBooleanMap(partial.aiPreprocessConfig?.desensitizeBuiltinRuleOverrides)\n\n return {\n preferences: {\n ...partial,\n aiPreprocessConfig: {\n ...partial.aiPreprocessConfig,\n desensitizeRulesSchemaVersion: DESENSITIZE_RULES_SCHEMA_VERSION,\n desensitizeBuiltinRuleOverrides: {\n ...legacyBuiltinRuleOverrides,\n ...existingOverrides,\n },\n desensitizeRules: rules.filter((rule) => !rule.builtin),\n },\n } as Partial<Preferences>,\n changed: true,\n }\n }\n\n private backupLegacyPreferences(rawContent: string): void {\n const backupDir = path.join(path.dirname(this.filePath), 'backups')\n if (!fs.existsSync(backupDir)) {\n fs.mkdirSync(backupDir, { recursive: true })\n }\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-')\n const backupPath = path.join(backupDir, `preferences-pre-desensitize-groups-${timestamp}.json`)\n fs.writeFileSync(backupPath, rawContent, 'utf-8')\n console.info(`[Preferences] Backed up legacy desensitize rules to ${backupPath}`)\n }\n\n private writePreferences(preferences: Preferences): void {\n const dir = path.dirname(this.filePath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n fs.writeFileSync(this.filePath, JSON.stringify(preferences, null, 2), 'utf-8')\n }\n\n /**\n * Deep merge: arrays are replaced (not concatenated), objects are merged.\n */\n private deepMerge(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown> {\n const result = { ...base }\n for (const [key, value] of Object.entries(override)) {\n if (value === undefined) continue\n if (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n typeof base[key] === 'object' &&\n base[key] !== null &&\n !Array.isArray(base[key])\n ) {\n result[key] = this.deepMerge(base[key] as Record<string, unknown>, value as Record<string, unknown>)\n } else {\n result[key] = value\n }\n }\n return result\n }\n}\n","/**\n * Merger orchestration — abstract data-source reading, conflict checking,\n * merge assembly, and ChatLab output formatting.\n *\n * Decoupled from Electron-specific TempDbReader via MergerDataSource interface.\n */\n\nimport {\n getCollidingPlatformIds,\n normalizePlatformId,\n detectConflictsInMessages,\n mergeMergerMembers,\n generateMessageKey,\n type MergerMember,\n type MergerMessage,\n type ConflictCheckResult,\n type MergedMember,\n type MergedMessage,\n} from '@openchatlab/core'\n\n// ==================== Data source abstraction ====================\n\nexport interface MergerSourceMeta {\n name: string\n platform: string\n type: string\n groupId?: string\n groupAvatar?: string\n}\n\n/**\n * Abstract data source for merger input.\n * In Electron, this wraps TempDbReader. Other platforms can implement\n * their own (e.g. reading from IndexedDB, REST API, etc.).\n */\nexport interface MergerDataSource {\n getMeta(): MergerSourceMeta | null\n getMembers(): MergerMember[]\n getMessageCount(): number\n streamMessages(batchSize: number, callback: (messages: MergerMessage[]) => void): void\n}\n\n// ==================== Output types ====================\n\nexport interface MergeSourceInfo {\n filename: string\n platform: string\n messageCount: number\n}\n\nexport interface ChatLabHeader {\n version: string\n exportedAt: number\n generator: string\n description: string\n}\n\nexport interface ChatLabMeta {\n name: string\n platform: string\n type: string\n sources: MergeSourceInfo[]\n groupId?: string\n groupAvatar?: string\n}\n\nexport interface ChatLabOutput {\n chatlab: ChatLabHeader\n meta: ChatLabMeta\n members: MergedMember[]\n messages: MergedMessage[]\n}\n\n// ==================== Conflict checking ====================\n\nexport function checkConflictsFromSources(\n dataSources: Array<{ source: MergerDataSource; filename: string }>\n): ConflictCheckResult {\n const allMessages: Array<{ msg: MergerMessage; source: string; platform: string }> = []\n\n for (const { source, filename } of dataSources) {\n const meta = source.getMeta()\n const platform = meta?.platform || 'unknown'\n source.streamMessages(10000, (messages) => {\n for (const msg of messages) {\n allMessages.push({ msg, source: filename, platform })\n }\n })\n }\n\n return detectConflictsInMessages(allMessages)\n}\n\n// ==================== Merge orchestration ====================\n\nexport interface MergeOrchestrationResult {\n success: true\n chatLabData: ChatLabOutput\n}\n\n/**\n * Build a merged ChatLabOutput from multiple data sources.\n * Pure orchestration: reads data sources, calls core algorithms,\n * assembles the output. Does NOT write to disk or import to DB.\n */\nexport function buildMergedOutput(\n dataSources: Array<{ source: MergerDataSource; filename: string }>,\n outputName: string\n): MergeOrchestrationResult {\n const metas = dataSources.map(({ source, filename }) => ({\n meta: source.getMeta(),\n members: source.getMembers(),\n filename,\n source,\n }))\n\n const collidingIds = getCollidingPlatformIds(\n metas.map(({ meta, members }) => ({\n platform: meta?.platform || 'unknown',\n members: members.map((m) => ({ platformId: m.platformId })),\n }))\n )\n\n const memberMap = mergeMergerMembers(\n metas.map(({ meta, members }) => ({\n platform: meta?.platform || 'unknown',\n members,\n })),\n collidingIds\n )\n\n // Streaming dedup: process batches to avoid loading all into memory\n const seenKeys = new Set<string>()\n const mergedMessages: MergedMessage[] = []\n\n for (const { source, meta } of metas) {\n const platform = meta?.platform || 'unknown'\n source.streamMessages(10000, (messages) => {\n for (const msg of messages) {\n const nid = normalizePlatformId(msg.senderPlatformId, platform, collidingIds)\n const key = generateMessageKey(msg.timestamp, nid, msg.content ?? null)\n if (seenKeys.has(key)) continue\n seenKeys.add(key)\n mergedMessages.push({\n sender: nid,\n accountName: msg.senderAccountName,\n groupNickname: msg.senderGroupNickname,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content,\n })\n }\n })\n }\n\n mergedMessages.sort((a, b) => a.timestamp - b.timestamp)\n\n const sources: MergeSourceInfo[] = dataSources.map(({ source, filename }) => ({\n filename,\n platform: source.getMeta()?.platform || 'unknown',\n messageCount: source.getMessageCount(),\n }))\n\n const uniquePlatforms = [...new Set(metas.map(({ meta }) => meta?.platform || 'unknown'))]\n const platform = uniquePlatforms.length === 1 ? uniquePlatforms[0] : 'mixed'\n\n const groupIds = new Set(metas.map(({ meta }) => meta?.groupId).filter(Boolean))\n const groupId = groupIds.size === 1 ? metas.find(({ meta }) => meta?.groupId)?.meta?.groupId : undefined\n const groupAvatar = groupId\n ? metas.filter(({ meta }) => meta?.groupId === groupId).pop()?.meta?.groupAvatar\n : undefined\n\n const chatLabData: ChatLabOutput = {\n chatlab: {\n version: '0.0.1',\n exportedAt: Math.floor(Date.now() / 1000),\n generator: 'ChatLab Merge Tool',\n description: `Merged from ${dataSources.length} files`,\n },\n meta: {\n name: outputName,\n platform,\n type: metas[0]?.meta?.type || 'group',\n sources,\n groupId,\n groupAvatar,\n },\n members: Array.from(memberMap.values()),\n messages: mergedMessages,\n }\n\n return { success: true, chatLabData }\n}\n\n// ==================== JSONL serialization ====================\n\n/**\n * Serialize ChatLabOutput to JSONL lines (generator for streaming writes).\n */\nexport function* serializeChatLabToJsonl(data: ChatLabOutput): Generator<string> {\n yield JSON.stringify({\n _type: 'header',\n chatlab: data.chatlab,\n meta: data.meta,\n })\n\n for (const member of data.members) {\n yield JSON.stringify({ _type: 'member', ...member })\n }\n\n for (const msg of data.messages) {\n yield JSON.stringify({ _type: 'message', ...msg })\n }\n}\n","/**\n * 语义索引模块(Phase 1)\n *\n * 独立于 dormant 的旧 `ai/rag` 模块,按 chunking-decision-final.md 重新实现:\n * 向量存储、parent/child chunker、embedding provider、混合召回与后台预热。\n */\n\nexport { EmbeddingIndexStore } from './store'\nexport type { LoadSqliteVec } from './store'\nexport {\n EMBEDDING_INDEX_SCHEMA,\n CHUNK_VECTOR_INDEX_TABLE,\n CHUNK_VECTOR_INDEX_INDEXES,\n SEMANTIC_INDEX_SESSION_TABLE,\n vecTableName,\n vecTableSchema,\n} from './schema'\nexport { SemanticIndexStateStore } from './session-state-store'\nexport type {\n SemanticIndexSessionState,\n SemanticIndexStatus,\n SemanticIndexCleanupStatus,\n EnableParams,\n ProgressPatch,\n} from './session-state-store'\nexport type {\n ChunkRecord,\n ChunkInsert,\n ChunkStatus,\n DenseQueryParams,\n DenseQueryResult,\n MessageToChunkParams,\n} from './types'\nexport {\n CHUNKER_VERSION,\n STRATEGY_ID,\n DEFAULT_CHUNKER_CONFIG,\n computeChunkerConfigHash,\n computeDbPathHash,\n composeChunkId,\n deriveParentId,\n parseParentBounds,\n} from './chunker-config'\nexport type { ChunkerConfig } from './chunker-config'\nexport { estimateTokens } from './tokens'\nexport {\n SemanticIndexConfigStore,\n defaultSemanticIndexConfig,\n isSemanticIndexConfigured,\n resolveModelId,\n clampSearchMaxResults,\n SEMANTIC_INDEX_CONFIG_VERSION,\n SEARCH_MAX_RESULTS_DEFAULT,\n SEARCH_MAX_RESULTS_MIN,\n SEARCH_MAX_RESULTS_MAX,\n} from './config'\nexport type {\n SemanticIndexConfig,\n SemanticIndexConfigInput,\n SemanticIndexMode,\n SemanticIndexLocalConfig,\n SemanticIndexApiConfig,\n} from './config'\nexport { createEmbedder } from './embedder-factory'\nexport type { EmbedderFactoryDeps } from './embedder-factory'\nexport {\n SemanticIndexService,\n createSemanticIndexService,\n persistSemanticIndexConfig,\n resolveSemanticIndexApiKeySet,\n SEMANTIC_INDEX_AUTH_PROFILE,\n SEMANTIC_INDEX_DB_FILE,\n SEMANTIC_INDEX_CONFIG_FILE,\n} from './service'\nexport type { SemanticIndexRuntime, MaybePromise } from './runtime'\nexport {\n SemanticIndexWorkerClient,\n createSemanticIndexWorkerClient,\n createSemanticIndexWorkerRuntimeClient,\n} from './worker-client'\nexport type {\n SemanticIndexWorkerClientOptions,\n SemanticIndexWorkerRuntimeClientOptions,\n SemanticIndexWorkerTransport,\n SemanticIndexWorkerTransportFactory,\n} from './worker-client'\nexport { createSemanticIndexWorkerThreadTransport } from './worker-thread-transport'\nexport type { SemanticIndexWorkerThreadTransportOptions, SemanticIndexWorkerLike } from './worker-thread-transport'\nexport { StaticPathProvider, snapshotPathProvider } from './static-path-provider'\nexport type { StaticPathProviderSnapshot } from './static-path-provider'\nexport type {\n SemanticIndexServiceOptions,\n SemanticIndexSessionStatus,\n SemanticSearchResult,\n SemanticSearchReason,\n SemanticSearchToolResult,\n SemanticSearchToolSource,\n SemanticSearchToolOptions,\n} from './service'\nexport { runWarmup } from './warmup/runner'\nexport type {\n SemanticMessageSource,\n StopSignal,\n WarmupRunnerOptions,\n WarmupResult,\n WarmupStatus,\n} from './warmup/runner'\nexport { SemanticIndexJobQueue } from './warmup/job-queue'\nexport type { SemanticIndexJob, SemanticIndexJobType, JobContext, JobExecutor } from './warmup/job-queue'\nexport { reciprocalRankFusion } from './retrieval/rrf'\nexport type { RrfResult } from './retrieval/rrf'\nexport { hybridSearch } from './retrieval/hybrid-search'\nexport type { FtsSearcher, HybridSearchDeps, HybridSearchParams, HybridSearchResult } from './retrieval/hybrid-search'\nexport { createChatDbMessageSource } from './chat-db/message-source'\nexport { createChatDbMessageRangeReader } from './chat-db/message-range-reader'\nexport { createChatDbFtsSearcher, extractFtsKeywords } from './chat-db/fts-searcher'\nexport { assembleEvidence, formatEvidenceMessages } from './retrieval/evidence'\nexport type {\n EvidenceMessage,\n MessageRangeReader,\n EvidenceHit,\n EvidenceBudget,\n EvidenceBlock,\n EvidenceResult,\n} from './retrieval/evidence'\nexport { chunkMessages, isSemanticVoid } from './chunker'\nexport type { ChunkMessageInput, ChunkSource, ChunkMessagesInput, ChildChunk, ChunkResult } from './chunker'\nexport * as embedding from './embedding'\n","import path from 'node:path'\nimport type { PathProvider } from '@openchatlab/core'\nimport type { AuthProfile } from '@openchatlab/config'\nimport { resolveApiKey as defaultResolveApiKey } from '@openchatlab/config'\nimport type { SemanticIndexConfig, SemanticIndexConfigInput } from './config'\nimport { isKeylessSemanticIndexApiBaseUrl, SemanticIndexConfigStore } from './config'\nimport {\n SEMANTIC_INDEX_CONFIG_FILE,\n persistSemanticIndexConfig,\n resolveSemanticIndexApiKeySet,\n type SemanticIndexSessionStatus,\n type SemanticSearchResult,\n type SemanticSearchToolOptions,\n type SemanticSearchToolResult,\n} from './service'\nimport type { SemanticIndexRuntime } from './runtime'\nimport type { RuntimeIdentity } from '../data-dir-compat'\nimport { snapshotPathProvider } from './static-path-provider'\nimport { createSemanticIndexWorkerThreadTransport } from './worker-thread-transport'\n\nexport interface SemanticIndexWorkerTransport {\n request<T>(method: string, args: unknown[]): Promise<T>\n close(): void | Promise<void>\n}\n\nexport type SemanticIndexWorkerTransportFactory = () => SemanticIndexWorkerTransport\n\ninterface WorkerClientTimers {\n setTimeout(callback: () => void, ms: number): unknown\n clearTimeout(timer: unknown): void\n}\n\nexport interface SemanticIndexWorkerClientOptions {\n configStore: SemanticIndexConfigStore\n transportFactory: SemanticIndexWorkerTransportFactory\n idleTimeoutMs?: number\n timers?: WorkerClientTimers\n resolveApiKey?: (provider: string, authProfile?: string) => string\n writeAuthProfile?: (name: string, profile: AuthProfile) => void\n}\n\nexport interface SemanticIndexWorkerRuntimeClientOptions {\n pathProvider: PathProvider\n runtime: RuntimeIdentity\n nativeBinding?: string\n sqliteVecLoadablePath?: string\n workerEntryUrl?: string | URL\n idleTimeoutMs?: number\n resolveApiKey?: (provider: string, authProfile?: string) => string\n writeAuthProfile?: (name: string, profile: AuthProfile) => void\n}\n\nconst DEFAULT_IDLE_TIMEOUT_MS = 10 * 60 * 1000\n\nconst defaultTimers: WorkerClientTimers = {\n setTimeout: (callback, ms) => setTimeout(callback, ms),\n clearTimeout: (timer) => clearTimeout(timer as ReturnType<typeof setTimeout>),\n}\n\nexport class SemanticIndexWorkerClient implements SemanticIndexRuntime {\n private transport: SemanticIndexWorkerTransport | null = null\n private pendingRequests = 0\n private idleTimer: unknown = null\n private activeBuildSessionIds = new Set<string>()\n private hasUnknownActiveBuild = false\n private readonly configStore: SemanticIndexConfigStore\n private readonly transportFactory: SemanticIndexWorkerTransportFactory\n private readonly idleTimeoutMs: number\n private readonly timers: WorkerClientTimers\n private readonly resolveApiKey: (provider: string, authProfile?: string) => string\n private readonly writeAuthProfile?: (name: string, profile: AuthProfile) => void\n\n constructor(options: SemanticIndexWorkerClientOptions) {\n this.configStore = options.configStore\n this.transportFactory = options.transportFactory\n this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS\n this.timers = options.timers ?? defaultTimers\n this.resolveApiKey = options.resolveApiKey ?? defaultResolveApiKey\n this.writeAuthProfile = options.writeAuthProfile\n }\n\n getConfig(): SemanticIndexConfig {\n return this.configStore.get()\n }\n\n async setConfig(config: SemanticIndexConfigInput, options?: { apiKey?: string }): Promise<SemanticIndexConfig> {\n const saved = persistSemanticIndexConfig(this.configStore, config, {\n apiKey: options?.apiKey,\n writeAuthProfile: this.writeAuthProfile,\n })\n if (!saved.enabled) this.clearActiveBuildTracking()\n if (!this.transport) return saved\n return await this.call<SemanticIndexConfig>('setConfig', [saved])\n }\n\n isConfigured(): boolean {\n return this.configStore.isConfigured()\n }\n\n hasApiKey(): boolean {\n return resolveSemanticIndexApiKeySet(this.configStore.get(), this.resolveApiKey)\n }\n\n enable(sessionId: string): Promise<void> {\n return this.call('enable', [sessionId])\n }\n\n disable(sessionId: string): Promise<void> {\n return this.call('disable', [sessionId])\n }\n\n async build(sessionId: string): Promise<void> {\n this.activeBuildSessionIds.add(sessionId)\n try {\n await this.call('build', [sessionId])\n } catch (error) {\n this.activeBuildSessionIds.delete(sessionId)\n throw error\n }\n }\n\n pause(sessionId: string): Promise<void> {\n return this.call('pause', [sessionId])\n }\n\n cancel(sessionId: string): Promise<void> {\n return this.call('cancel', [sessionId])\n }\n\n async rebuild(sessionId: string): Promise<void> {\n this.activeBuildSessionIds.add(sessionId)\n try {\n await this.call('rebuild', [sessionId])\n } catch (error) {\n this.activeBuildSessionIds.delete(sessionId)\n throw error\n }\n }\n\n async buildAllPending(): Promise<void> {\n this.hasUnknownActiveBuild = true\n try {\n await this.call('buildAllPending', [])\n } catch (error) {\n this.hasUnknownActiveBuild = false\n throw error\n }\n }\n\n async listEnabledStatuses(): Promise<SemanticIndexSessionStatus[]> {\n const statuses = await this.callProbe<SemanticIndexSessionStatus[]>('listEnabledStatuses', [], [], {\n scheduleIdle: false,\n })\n this.updateActiveBuildFromCompleteSnapshot(statuses)\n this.scheduleIdleCloseIfNeeded()\n return statuses\n }\n\n async status(sessionId: string): Promise<SemanticIndexSessionStatus | null> {\n const status = await this.callProbe<SemanticIndexSessionStatus | null>('status', [sessionId], null, {\n scheduleIdle: false,\n })\n this.updateActiveBuildFromPartialSnapshot([sessionId], status ? [status] : [])\n this.scheduleIdleCloseIfNeeded()\n return status\n }\n\n async statusForSessions(sessionIds: string[]): Promise<SemanticIndexSessionStatus[]> {\n const statuses = await this.callProbe<SemanticIndexSessionStatus[]>('statusForSessions', [sessionIds], [], {\n scheduleIdle: false,\n })\n this.updateActiveBuildFromPartialSnapshot(sessionIds, statuses)\n this.scheduleIdleCloseIfNeeded()\n return statuses\n }\n\n async canSearch(sessionId: string): Promise<boolean> {\n if (!this.canRunFromLocalConfig()) return false\n return await this.callProbe<boolean>('canSearch', [sessionId], false)\n }\n\n search(\n sessionId: string,\n query: string,\n options?: { finalTopK?: number; timeRangeMs?: { startTs?: number; endTs?: number } }\n ): Promise<SemanticSearchResult> {\n return this.call('search', [sessionId, query, options])\n }\n\n searchForTool(\n sessionId: string,\n query: string,\n options?: SemanticSearchToolOptions\n ): Promise<SemanticSearchToolResult> {\n return this.call('searchForTool', [sessionId, query, options])\n }\n\n cleanupUnused(): Promise<{ cleaned: number }> {\n return this.call('cleanupUnused', [])\n }\n\n recover(): Promise<void> {\n return this.call('recover', [])\n }\n\n async close(): Promise<void> {\n this.clearIdleTimer()\n await this.closeTransport()\n }\n\n private async call<T>(method: string, args: unknown[], options?: { scheduleIdle?: boolean }): Promise<T> {\n const transport = this.ensureTransport()\n this.pendingRequests++\n this.clearIdleTimer()\n try {\n return await transport.request<T>(method, args)\n } finally {\n this.pendingRequests--\n if (options?.scheduleIdle !== false) this.scheduleIdleCloseIfNeeded()\n }\n }\n\n private async callProbe<T>(\n method: string,\n args: unknown[],\n fallback: T,\n options?: { scheduleIdle?: boolean }\n ): Promise<T> {\n try {\n return await this.call<T>(method, args, options)\n } catch {\n await this.closeFailedProbeTransport()\n return fallback\n }\n }\n\n private ensureTransport(): SemanticIndexWorkerTransport {\n if (!this.transport) {\n this.transport = this.transportFactory()\n }\n return this.transport\n }\n\n private canRunFromLocalConfig(): boolean {\n const config = this.configStore.get()\n if (!this.configStore.canRun()) return false\n if (config.mode !== 'api') return true\n if (isKeylessSemanticIndexApiBaseUrl(config.api?.baseUrl)) return true\n return resolveSemanticIndexApiKeySet(config, this.resolveApiKey)\n }\n\n private updateActiveBuildFromCompleteSnapshot(statuses: SemanticIndexSessionStatus[]): void {\n this.hasUnknownActiveBuild = false\n this.activeBuildSessionIds = new Set(\n statuses.filter((status) => this.isActiveBuildStatus(status)).map((status) => status.sessionId)\n )\n }\n\n private updateActiveBuildFromPartialSnapshot(\n requestedSessionIds: string[],\n statuses: SemanticIndexSessionStatus[]\n ): void {\n const bySessionId = new Map(statuses.map((status) => [status.sessionId, status]))\n for (const sessionId of requestedSessionIds) {\n const status = bySessionId.get(sessionId)\n if (status && this.isActiveBuildStatus(status)) this.activeBuildSessionIds.add(sessionId)\n else this.activeBuildSessionIds.delete(sessionId)\n }\n }\n\n private isActiveBuildStatus(status: SemanticIndexSessionStatus): boolean {\n return status.running || status.queued || status.indexStatus === 'running'\n }\n\n private clearActiveBuildTracking(): void {\n this.hasUnknownActiveBuild = false\n this.activeBuildSessionIds.clear()\n }\n\n private scheduleIdleCloseIfNeeded(): void {\n if (\n !this.transport ||\n this.pendingRequests > 0 ||\n this.hasUnknownActiveBuild ||\n this.activeBuildSessionIds.size > 0\n )\n return\n this.clearIdleTimer()\n this.idleTimer = this.timers.setTimeout(() => {\n void this.closeTransport().catch((err) => {\n console.warn('[semantic-index] idle worker close failed:', err instanceof Error ? err.message : String(err))\n })\n }, this.idleTimeoutMs)\n }\n\n private clearIdleTimer(): void {\n if (this.idleTimer) {\n this.timers.clearTimeout(this.idleTimer)\n this.idleTimer = null\n }\n }\n\n private async closeTransport(): Promise<void> {\n const transport = this.transport\n this.transport = null\n this.clearActiveBuildTracking()\n if (transport) await transport.close()\n }\n\n private async closeFailedProbeTransport(): Promise<void> {\n this.clearIdleTimer()\n try {\n await this.closeTransport()\n } catch {\n // 语义索引探针失败只降级为不可用,不能打断普通 AI 对话流程。\n }\n }\n}\n\nexport function createSemanticIndexWorkerClient(options: SemanticIndexWorkerClientOptions): SemanticIndexWorkerClient {\n return new SemanticIndexWorkerClient(options)\n}\n\nexport function createSemanticIndexWorkerRuntimeClient(\n options: SemanticIndexWorkerRuntimeClientOptions\n): SemanticIndexWorkerClient {\n const configStore = new SemanticIndexConfigStore(\n path.join(options.pathProvider.getAiDataDir(), SEMANTIC_INDEX_CONFIG_FILE)\n )\n return createSemanticIndexWorkerClient({\n configStore,\n idleTimeoutMs: options.idleTimeoutMs,\n resolveApiKey: options.resolveApiKey,\n writeAuthProfile: options.writeAuthProfile,\n transportFactory: () =>\n createSemanticIndexWorkerThreadTransport({\n workerEntryUrl: options.workerEntryUrl,\n startup: {\n paths: snapshotPathProvider(options.pathProvider),\n runtime: options.runtime,\n nativeBinding: options.nativeBinding,\n sqliteVecLoadablePath: options.sqliteVecLoadablePath,\n },\n }),\n })\n}\n","import { Worker } from 'node:worker_threads'\nimport type { WorkerOptions } from 'node:worker_threads'\nimport type { SemanticIndexWorkerTransport } from './worker-client'\nimport type { SemanticIndexWorkerStartupOptions } from './worker-runtime'\n\nexport interface SemanticIndexWorkerLike {\n postMessage(message: unknown): void\n terminate(): Promise<number>\n on(event: 'message', listener: (message: unknown) => void): this\n on(event: 'error', listener: (error: Error) => void): this\n on(event: 'exit', listener: (code: number) => void): this\n}\n\nexport interface SemanticIndexWorkerThreadTransportOptions {\n startup: SemanticIndexWorkerStartupOptions\n workerFactory?: () => SemanticIndexWorkerLike\n workerEntryUrl?: string | URL\n closeTimeoutMs?: number\n}\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n}\n\nconst DEFAULT_CLOSE_TIMEOUT_MS = 3_000\n\ntype ModuleWorkerOptions = WorkerOptions & { type: 'module' }\n\nfunction defaultWorkerEntryUrl(): URL {\n return import.meta.url.endsWith('.ts')\n ? new URL('./worker-thread-entry.ts', import.meta.url)\n : new URL('./worker-thread-entry.js', import.meta.url)\n}\n\nfunction normalizeWorkerEntryUrl(entryUrl?: string | URL): URL {\n if (!entryUrl) return defaultWorkerEntryUrl()\n return typeof entryUrl === 'string' ? new URL(entryUrl) : entryUrl\n}\n\nfunction createDefaultWorker(\n startup: SemanticIndexWorkerStartupOptions,\n entryUrlInput?: string | URL\n): SemanticIndexWorkerLike {\n const entryUrl = normalizeWorkerEntryUrl(entryUrlInput)\n if (!entryUrl.href.endsWith('.ts')) {\n return new Worker(entryUrl, { workerData: startup }) as SemanticIndexWorkerLike\n }\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: startup,\n execArgv: [],\n }\n return new Worker(bootstrap, options) as SemanticIndexWorkerLike\n}\n\nexport class SemanticIndexWorkerThreadTransport implements SemanticIndexWorkerTransport {\n private worker: SemanticIndexWorkerLike\n private pending = new Map<string, PendingRequest>()\n private requestId = 0\n private readonly closeTimeoutMs: number\n\n constructor(options: SemanticIndexWorkerThreadTransportOptions) {\n this.closeTimeoutMs = options.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS\n this.worker = options.workerFactory?.() ?? createDefaultWorker(options.startup, options.workerEntryUrl)\n\n this.worker.on('message', (message) => this.handleMessage(message))\n this.worker.on('error', (error) => this.rejectAll(error))\n this.worker.on('exit', (code) => {\n if (code !== 0) this.rejectAll(new Error(`Semantic index worker exited with code ${code}`))\n })\n }\n\n request<T>(method: string, args: unknown[]): Promise<T> {\n const id = `si_${++this.requestId}`\n return new Promise<T>((resolve, reject) => {\n this.pending.set(id, { resolve: resolve as (value: unknown) => void, reject })\n this.worker.postMessage({ id, method, args })\n })\n }\n\n async close(): Promise<void> {\n await this.requestRuntimeClose()\n await this.worker.terminate()\n this.rejectAll(new Error('Semantic index worker closed'))\n }\n\n private async requestRuntimeClose(): Promise<void> {\n let timeout: ReturnType<typeof setTimeout> | null = null\n const timeoutPromise = new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, this.closeTimeoutMs)\n })\n\n try {\n // 先让 worker 内的 service 正常 close,避免 SQLite/vector 资源被直接中断;超时后仍会 terminate。\n await Promise.race([this.request('__close', []).then(() => undefined), timeoutPromise])\n } catch {\n // Worker may already be gone; terminate below still clears local state.\n } finally {\n if (timeout) clearTimeout(timeout)\n }\n }\n\n private handleMessage(message: unknown): void {\n const payload = message as { id?: string; success?: boolean; result?: unknown; error?: string }\n if (!payload.id) return\n const pending = this.pending.get(payload.id)\n if (!pending) return\n this.pending.delete(payload.id)\n if (payload.success) pending.resolve(payload.result)\n else pending.reject(new Error(payload.error ?? 'Semantic index worker request failed'))\n }\n\n private rejectAll(error: Error): void {\n for (const [id, pending] of this.pending.entries()) {\n this.pending.delete(id)\n pending.reject(error)\n }\n }\n}\n\nexport function createSemanticIndexWorkerThreadTransport(\n options: SemanticIndexWorkerThreadTransportOptions\n): SemanticIndexWorkerThreadTransport {\n return new SemanticIndexWorkerThreadTransport(options)\n}\n","/**\n * Embedding provider 模块\n */\n\nexport type { EmbeddingProvider, EmbeddingPooling } from './types'\nexport {\n QWEN3_PROFILE,\n BGE_BASE_PROFILE,\n getLocalProfilesForLocale,\n getLocalProfileByModelId,\n type LocalEmbeddingProfile,\n} from './profiles'\nexport { applyQueryInstruction, clampTextChars } from './text'\nexport { LocalEmbeddingProvider } from './local'\nexport type { LocalPipelineFactory, FeatureExtractFn, LocalEmbeddingProviderOptions } from './local'\nexport { OpenAICompatibleEmbeddingProvider } from './api'\nexport type { OpenAICompatibleConfig, FetchFn } from './api'\n"],"mappings":"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","names":["t","path","result","fns","chain","path","LogLevel","fs","path","fs","path","feature","parser","fs","fs","path","streamJson","pickModule","streamValuesModule","streamChain","basename","resolve","fs","path","extractNameFromFilePath","basename","feature","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","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","chain","streamChain","resolve","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","chain","streamChain","feature","extractJsonObject","extractNameFromFilePath","basename","resolve","parser_","module_","feature","parser_","fs","path","readline","feature","convertMessageType","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","resolve","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","resolve","parser_","module_","import_stream_chain","import_stream_json","import_Pick","import_StreamValues","fs","chain","streamChain","parser","streamJson","pick","pickModule","streamValues","streamValuesModule","feature","resolve","parser_","module_","import_stream_chain","import_stream_json","import_StreamValues","fs","chain","streamChain","parser","streamJson","streamValues","streamValuesModule","feature","resolve","parser_","module_","fs","path","readline","extractNameFromFilePath","basename","feature","detectMessageType","parser_","module_","fs","path","readline","extractNameFromFilePath","basename","feature","detectMessageType","parser_","module_","fs","path","feature","extractNameFromFilePath","basename","isSystemMessage","detectMessageType","parser_","module_","fs","path","feature","extractNameFromFilePath","basename","detectMessageType","content","parser_","module_","feature","parser","fs","feature","trackError","errorCount","fs","os","path","fs","path","ensureDir","fs","fs","path","platform","path","resolve","path"]}
|