diff --git a/packages/parser/src/formats/chatlab.ts b/packages/parser/src/formats/chatlab.ts index ac4aca7d..69ad91c9 100644 --- a/packages/parser/src/formats/chatlab.ts +++ b/packages/parser/src/formats/chatlab.ts @@ -154,27 +154,8 @@ async function* parseChatLab(options: ParseOptions): AsyncGenerator() const messageBatch: ParsedMessage[] = [] @@ -186,12 +167,24 @@ async function* parseChatLab(options: ParseOptions): AsyncGenerator { + pipeline.on('data', ({ value }: { value: ChatLabMember | ChatLabMessage }) => { + if (!('sender' in value)) { + members.push({ + platformId: value.platformId, + accountName: value.accountName, + groupNickname: value.groupNickname, + avatar: value.avatar, + roles: value.roles, + }) + return + } + const msg = value // 如果前面没解析到 members,从消息中收集 diff --git a/packages/parser/src/native/chatlab-native.test.ts b/packages/parser/src/native/chatlab-native.test.ts index 5b8d71b7..ccde2eb9 100644 --- a/packages/parser/src/native/chatlab-native.test.ts +++ b/packages/parser/src/native/chatlab-native.test.ts @@ -163,11 +163,7 @@ describe('chatlab native parser parity', { skip: !nativeAvailable() && 'native m assert.deepEqual(nativeResult.messages, []) }) - it('keeps member roles and avatars where the TS head-regex heuristic loses them', async () => { - // The TS parser extracts head members with a non-greedy regex that breaks - // on nested arrays (roles), silently degrading to message-collected - // members. The Rust kernel parses the members array structurally, so this - // is a deliberate improvement over TS behavior, not a parity case. + it('keeps member roles and avatars in both parsers', async () => { const withRoles = { chatlab: { version: '0.0.2' }, meta: { name: '带角色群', platform: 'qq', type: 'group' }, @@ -184,18 +180,10 @@ describe('chatlab native parser parity', { skip: !nativeAvailable() && 'native m } const { nativeResult, tsResult } = await parseBothWays('带角色群.json', JSON.stringify(withRoles)) - // TS loses the head members entirely (only u1 appears, from messages). - assert.equal(tsResult.members.length, 1) - assert.equal(tsResult.members[0].avatar, undefined) - - // Native keeps the full member list including roles and avatars. - assert.equal(nativeResult.members.length, 2) - assert.equal(nativeResult.members[0].avatar, 'https://example.com/a.jpg') - assert.deepEqual(nativeResult.members[0].roles, [{ id: 'owner' }, { id: 'admin', name: '管理员' }]) - - // Meta and messages still match exactly. - assert.deepEqual(nativeResult.meta, tsResult.meta) - assert.deepEqual(nativeResult.messages, tsResult.messages) + assertParity(nativeResult, tsResult) + assert.equal(tsResult.members.length, 2) + assert.equal(tsResult.members[0].avatar, 'https://example.com/a.jpg') + assert.deepEqual(tsResult.members[0].roles, [{ id: 'owner' }, { id: 'admin', name: '管理员' }]) }) it('falls back to the TS parser for off-spec files', async () => { diff --git a/tests/chatlab-json-export-roundtrip.test.ts b/tests/chatlab-json-export-roundtrip.test.ts index d6772202..7af73207 100644 --- a/tests/chatlab-json-export-roundtrip.test.ts +++ b/tests/chatlab-json-export-roundtrip.test.ts @@ -44,7 +44,7 @@ test('exports JSON as ChatLab format that can be parsed for re-import', async () ) insertMessage.run(aliceId, 'Alice Account', '爱丽丝', 100, 0, '筛选范围外', 'msg-1', null) insertMessage.run(bobId, 'Bob Account', '鲍勃', 200, 0, '你好', 'msg-2', null) - insertMessage.run(aliceId, 'Alice Account', '爱丽丝', 250, 1, null, 'msg-3', 'msg-2') + insertMessage.run(bobId, 'Bob Account', '鲍勃', 250, 1, null, 'msg-3', 'msg-2') const result = exportWithFormat( { @@ -69,7 +69,15 @@ test('exports JSON as ChatLab format that can be parsed for re-import', async () writeFileSync(filePath, result.content, 'utf8') assert.equal(detectFormat(filePath)?.id, 'chatlab') - const parsed = await parseFileSync(filePath) + const nativePerfDisabled = process.env.CHATLAB_DISABLE_NATIVE_PERF + process.env.CHATLAB_DISABLE_NATIVE_PERF = '1' + const parsed = await parseFileSync(filePath).finally(() => { + if (nativePerfDisabled === undefined) { + delete process.env.CHATLAB_DISABLE_NATIVE_PERF + } else { + process.env.CHATLAB_DISABLE_NATIVE_PERF = nativePerfDisabled + } + }) assert.deepEqual(parsed.meta, { name: '测试群', platform: 'qq', @@ -111,9 +119,9 @@ test('exports JSON as ChatLab format that can be parsed for re-import', async () replyToMessageId: undefined, }, { - senderPlatformId: 'alice', - senderAccountName: 'Alice Account', - senderGroupNickname: '爱丽丝', + senderPlatformId: 'bob', + senderAccountName: 'Bob Account', + senderGroupNickname: '鲍勃', timestamp: 250, type: 1, content: null,