@juzi/wechaty-puppet-service 1.0.130 → 1.0.131
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/dist/cjs/src/auth/call-cred.d.ts +2 -2
- package/dist/cjs/src/auth/call-cred.d.ts.map +1 -1
- package/dist/cjs/src/auth/call-cred.js.map +1 -1
- package/dist/cjs/src/client/puppet-service.d.ts +25 -2
- package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
- package/dist/cjs/src/client/puppet-service.js +389 -124
- package/dist/cjs/src/client/puppet-service.js.map +1 -1
- package/dist/cjs/src/client/room-cache-coordinator.d.ts +14 -0
- package/dist/cjs/src/client/room-cache-coordinator.d.ts.map +1 -0
- package/dist/cjs/src/client/room-cache-coordinator.js +53 -0
- package/dist/cjs/src/client/room-cache-coordinator.js.map +1 -0
- package/dist/cjs/src/client/room-member-cache.spec.d.ts +2 -0
- package/dist/cjs/src/client/room-member-cache.spec.d.ts.map +1 -0
- package/dist/cjs/src/client/room-member-cache.spec.js +417 -0
- package/dist/cjs/src/client/room-member-cache.spec.js.map +1 -0
- package/dist/cjs/src/client/room-member-load.spec.d.ts +2 -0
- package/dist/cjs/src/client/room-member-load.spec.d.ts.map +1 -0
- package/dist/cjs/src/client/room-member-load.spec.js +324 -0
- package/dist/cjs/src/client/room-member-load.spec.js.map +1 -0
- package/dist/cjs/src/package-json.js +4 -4
- package/dist/cjs/src/server/event-stream-manager.d.ts.map +1 -1
- package/dist/cjs/src/server/event-stream-manager.js +16 -0
- package/dist/cjs/src/server/event-stream-manager.js.map +1 -1
- package/dist/cjs/src/server/puppet-implementation.d.ts.map +1 -1
- package/dist/cjs/src/server/puppet-implementation.js +91 -28
- package/dist/cjs/src/server/puppet-implementation.js.map +1 -1
- package/dist/cjs/src/utils/pb-payload-helper.d.ts +10 -0
- package/dist/cjs/src/utils/pb-payload-helper.d.ts.map +1 -1
- package/dist/cjs/src/utils/pb-payload-helper.js +180 -1
- package/dist/cjs/src/utils/pb-payload-helper.js.map +1 -1
- package/dist/cjs/src/utils/room-member-load.spec.d.ts +2 -0
- package/dist/cjs/src/utils/room-member-load.spec.d.ts.map +1 -0
- package/dist/cjs/src/utils/room-member-load.spec.js +83 -0
- package/dist/cjs/src/utils/room-member-load.spec.js.map +1 -0
- package/dist/esm/src/auth/call-cred.d.ts +2 -2
- package/dist/esm/src/auth/call-cred.d.ts.map +1 -1
- package/dist/esm/src/auth/call-cred.js.map +1 -1
- package/dist/esm/src/client/puppet-service.d.ts +25 -2
- package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
- package/dist/esm/src/client/puppet-service.js +390 -125
- package/dist/esm/src/client/puppet-service.js.map +1 -1
- package/dist/esm/src/client/room-cache-coordinator.d.ts +14 -0
- package/dist/esm/src/client/room-cache-coordinator.d.ts.map +1 -0
- package/dist/esm/src/client/room-cache-coordinator.js +49 -0
- package/dist/esm/src/client/room-cache-coordinator.js.map +1 -0
- package/dist/esm/src/client/room-member-cache.spec.d.ts +2 -0
- package/dist/esm/src/client/room-member-cache.spec.d.ts.map +1 -0
- package/dist/esm/src/client/room-member-cache.spec.js +415 -0
- package/dist/esm/src/client/room-member-cache.spec.js.map +1 -0
- package/dist/esm/src/client/room-member-load.spec.d.ts +2 -0
- package/dist/esm/src/client/room-member-load.spec.d.ts.map +1 -0
- package/dist/esm/src/client/room-member-load.spec.js +296 -0
- package/dist/esm/src/client/room-member-load.spec.js.map +1 -0
- package/dist/esm/src/package-json.js +4 -4
- package/dist/esm/src/server/event-stream-manager.d.ts.map +1 -1
- package/dist/esm/src/server/event-stream-manager.js +16 -0
- package/dist/esm/src/server/event-stream-manager.js.map +1 -1
- package/dist/esm/src/server/puppet-implementation.d.ts.map +1 -1
- package/dist/esm/src/server/puppet-implementation.js +92 -29
- package/dist/esm/src/server/puppet-implementation.js.map +1 -1
- package/dist/esm/src/utils/pb-payload-helper.d.ts +10 -0
- package/dist/esm/src/utils/pb-payload-helper.d.ts.map +1 -1
- package/dist/esm/src/utils/pb-payload-helper.js +169 -0
- package/dist/esm/src/utils/pb-payload-helper.js.map +1 -1
- package/dist/esm/src/utils/room-member-load.spec.d.ts +2 -0
- package/dist/esm/src/utils/room-member-load.spec.d.ts.map +1 -0
- package/dist/esm/src/utils/room-member-load.spec.js +58 -0
- package/dist/esm/src/utils/room-member-load.spec.js.map +1 -0
- package/package.json +4 -4
- package/src/auth/call-cred.ts +1 -1
- package/src/client/puppet-service.ts +324 -138
- package/src/client/room-cache-coordinator.ts +50 -0
- package/src/client/room-member-cache.spec.ts +448 -0
- package/src/client/room-member-load.spec.ts +319 -0
- package/src/package-json.ts +4 -4
- package/src/server/event-stream-manager.ts +15 -0
- package/src/server/puppet-implementation.ts +94 -29
- package/src/utils/pb-payload-helper.ts +150 -0
- package/src/utils/room-member-load.spec.ts +67 -0
|
@@ -6,8 +6,91 @@ import {
|
|
|
6
6
|
import * as PUPPET from '@juzi/wechaty-puppet'
|
|
7
7
|
import type { FileBox, FileBoxInterface } from 'file-box'
|
|
8
8
|
|
|
9
|
+
import { millisecondsFromTimestamp, timestampFromMilliseconds } from '../pure-functions/timestamp.js'
|
|
10
|
+
|
|
9
11
|
type grpcPuppet = typeof puppet
|
|
10
12
|
|
|
13
|
+
export function roomPbToPayload (pb: puppet.RoomPayloadResponse): PUPPET.payloads.Room {
|
|
14
|
+
const createTime = pb.getCreateTime()
|
|
15
|
+
const loadedAt = pb.getMemberLoadedAt()
|
|
16
|
+
return {
|
|
17
|
+
adminIdList: pb.getAdminIdsList(),
|
|
18
|
+
avatar: pb.getAvatar(),
|
|
19
|
+
handle: pb.getHandle(),
|
|
20
|
+
id: pb.getId(),
|
|
21
|
+
memberIdList: pb.getMemberIdsList(),
|
|
22
|
+
ownerId: pb.getOwnerId(),
|
|
23
|
+
topic: pb.getTopic(),
|
|
24
|
+
additionalInfo: pb.getAdditionalInfo(),
|
|
25
|
+
remark: pb.getRoomRemark(),
|
|
26
|
+
external: pb.getExternal(),
|
|
27
|
+
...(createTime ? { createTime: millisecondsFromTimestamp(createTime) } : {}),
|
|
28
|
+
memberCount: pb.getMemberCount(),
|
|
29
|
+
memberLoadMode: pb.getMemberLoadMode(),
|
|
30
|
+
memberLoadStatus: pb.getMemberLoadStatus(),
|
|
31
|
+
memberLoadRevision: pb.getMemberLoadRevision(),
|
|
32
|
+
...(loadedAt ? { memberLoadedAt: millisecondsFromTimestamp(loadedAt) } : {}),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function roomPayloadToPb (payload: PUPPET.payloads.Room): puppet.RoomPayloadResponse {
|
|
37
|
+
const pb = new puppet.RoomPayloadResponse()
|
|
38
|
+
pb.setAdminIdsList(payload.adminIdList)
|
|
39
|
+
pb.setAvatar(payload.avatar || '')
|
|
40
|
+
pb.setHandle(payload.handle || '')
|
|
41
|
+
pb.setId(payload.id)
|
|
42
|
+
pb.setMemberIdsList(payload.memberIdList)
|
|
43
|
+
pb.setOwnerId(payload.ownerId || '')
|
|
44
|
+
pb.setTopic(payload.topic)
|
|
45
|
+
pb.setAdditionalInfo(payload.additionalInfo || '')
|
|
46
|
+
pb.setRoomRemark(payload.remark || '')
|
|
47
|
+
pb.setExternal(!!payload.external)
|
|
48
|
+
if (payload.createTime !== undefined) { pb.setCreateTime(timestampFromMilliseconds(payload.createTime)) }
|
|
49
|
+
if (payload.memberCount !== undefined) { pb.setMemberCount(payload.memberCount) }
|
|
50
|
+
if (payload.memberLoadMode !== undefined) { pb.setMemberLoadMode(payload.memberLoadMode) }
|
|
51
|
+
if (payload.memberLoadStatus !== undefined) { pb.setMemberLoadStatus(payload.memberLoadStatus) }
|
|
52
|
+
if (payload.memberLoadRevision !== undefined) { pb.setMemberLoadRevision(payload.memberLoadRevision) }
|
|
53
|
+
if (payload.memberLoadedAt !== undefined) { pb.setMemberLoadedAt(timestampFromMilliseconds(payload.memberLoadedAt)) }
|
|
54
|
+
return pb
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function validateRoomMemberRevision (revision: string): void {
|
|
58
|
+
if (typeof revision !== 'string' || !/^(0|[1-9][0-9]*)$/.test(revision)) {
|
|
59
|
+
throw new Error('Invalid room member revision')
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function validateRoomMemberLoadState (state: unknown, roomId?: string): asserts state is PUPPET.payloads.RoomMemberLoadState {
|
|
64
|
+
if (typeof state !== 'object' || state === null) { throw new Error('Invalid room member load state') }
|
|
65
|
+
const value = state as Record<string, unknown>
|
|
66
|
+
if (typeof value['roomId'] !== 'string' || !value['roomId'].trim()
|
|
67
|
+
|| (roomId !== undefined && value['roomId'] !== roomId)) { throw new Error('Invalid room member roomId') }
|
|
68
|
+
if (typeof value['taskId'] !== 'string') { throw new Error('Invalid room member taskId') }
|
|
69
|
+
if (typeof value['revision'] !== 'string') { throw new Error('Invalid room member revision') }
|
|
70
|
+
validateRoomMemberRevision(value['revision'])
|
|
71
|
+
const status = value['status']
|
|
72
|
+
if (typeof status !== 'number' || !Number.isInteger(status) || status < 0 || status > 4) {
|
|
73
|
+
throw new Error('Invalid room member load status')
|
|
74
|
+
}
|
|
75
|
+
if (value['loadedAt'] !== undefined && (typeof value['loadedAt'] !== 'number' || !Number.isFinite(value['loadedAt']))) {
|
|
76
|
+
throw new Error('Invalid room member loadedAt')
|
|
77
|
+
}
|
|
78
|
+
if (value['error'] !== undefined && typeof value['error'] !== 'string') { throw new Error('Invalid room member error') }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function validateRoomMemberSnapshot (
|
|
82
|
+
snapshot: PUPPET.payloads.RoomMemberSnapshot, roomId: string, revision: string,
|
|
83
|
+
): void {
|
|
84
|
+
if (!roomId || snapshot.roomId !== roomId) { throw new Error('Room member snapshot roomId mismatch') }
|
|
85
|
+
validateRoomMemberRevision(snapshot.revision)
|
|
86
|
+
validateRoomMemberRevision(revision)
|
|
87
|
+
if (snapshot.revision !== revision) { throw new Error('Room member snapshot revision mismatch') }
|
|
88
|
+
if (!Array.isArray(snapshot.members)) { throw new Error('Invalid room member snapshot members') }
|
|
89
|
+
if (snapshot.loadedAt !== undefined && !Number.isFinite(snapshot.loadedAt)) {
|
|
90
|
+
throw new Error('Invalid room member snapshot loadedAt')
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
11
94
|
export const miniProgramPayloadToPb = (grpcPuppet: grpcPuppet, miniProgramPayload: PUPPET.payloads.MiniProgram) => {
|
|
12
95
|
const pbMiniProgramPayload = new grpcPuppet.MiniProgramPayload()
|
|
13
96
|
if (miniProgramPayload.appid) { pbMiniProgramPayload.setAppid(miniProgramPayload.appid) }
|
|
@@ -440,6 +523,73 @@ export const roomMemberPbToPayload = (pb: puppet.RoomMemberPayloadResponse) => {
|
|
|
440
523
|
return payload
|
|
441
524
|
}
|
|
442
525
|
|
|
526
|
+
export function roomMemberPayloadToPb (payload: PUPPET.payloads.RoomMember): puppet.RoomMemberPayloadResponse {
|
|
527
|
+
const pb = new puppet.RoomMemberPayloadResponse()
|
|
528
|
+
pb.setAvatar(payload.avatar)
|
|
529
|
+
pb.setId(payload.id)
|
|
530
|
+
pb.setInviterId(payload.inviterId || '')
|
|
531
|
+
pb.setName(payload.name)
|
|
532
|
+
pb.setRoomAlias(payload.roomAlias || '')
|
|
533
|
+
pb.setAdditionalInfo(payload.additionalInfo || '')
|
|
534
|
+
pb.setJoinScene(payload.joinScene ?? PUPPET.types.RoomMemberJoinScene.Unknown)
|
|
535
|
+
pb.setJoinTime(payload.joinTime ?? 0)
|
|
536
|
+
return pb
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export function roomMemberLoadStateToPb (state: PUPPET.payloads.RoomMemberLoadState): puppet.RoomMemberLoadStateResponse {
|
|
540
|
+
validateRoomMemberLoadState(state, state.roomId)
|
|
541
|
+
const pb = new puppet.RoomMemberLoadStateResponse()
|
|
542
|
+
pb.setId(state.roomId)
|
|
543
|
+
pb.setTaskId(state.taskId)
|
|
544
|
+
pb.setStatus(state.status)
|
|
545
|
+
pb.setRevision(state.revision)
|
|
546
|
+
if (state.loadedAt !== undefined) { pb.setLoadedAt(timestampFromMilliseconds(state.loadedAt)) }
|
|
547
|
+
if (state.error !== undefined) { pb.setError(state.error) }
|
|
548
|
+
return pb
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export function roomMemberLoadStatePbToPayload (
|
|
552
|
+
pb: puppet.RoomMemberLoadStateResponse, roomId: string,
|
|
553
|
+
): PUPPET.payloads.RoomMemberLoadState {
|
|
554
|
+
const loadedAt = pb.getLoadedAt()
|
|
555
|
+
const state: PUPPET.payloads.RoomMemberLoadState = {
|
|
556
|
+
roomId: pb.getId(),
|
|
557
|
+
taskId: pb.getTaskId(),
|
|
558
|
+
status: pb.getStatus(),
|
|
559
|
+
revision: pb.getRevision(),
|
|
560
|
+
...(loadedAt ? { loadedAt: millisecondsFromTimestamp(loadedAt) } : {}),
|
|
561
|
+
...(pb.getError() ? { error: pb.getError() } : {}),
|
|
562
|
+
}
|
|
563
|
+
validateRoomMemberLoadState(state, roomId)
|
|
564
|
+
return state
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export function roomMemberSnapshotToPb (snapshot: PUPPET.payloads.RoomMemberSnapshot): puppet.RoomMemberSnapshotResponse {
|
|
568
|
+
validateRoomMemberSnapshot(snapshot, snapshot.roomId, snapshot.revision)
|
|
569
|
+
const pb = new puppet.RoomMemberSnapshotResponse()
|
|
570
|
+
pb.setId(snapshot.roomId)
|
|
571
|
+
pb.setRevision(snapshot.revision)
|
|
572
|
+
pb.setMembersList(snapshot.members.map(roomMemberPayloadToPb))
|
|
573
|
+
if (snapshot.loadedAt !== undefined) { pb.setLoadedAt(timestampFromMilliseconds(snapshot.loadedAt)) }
|
|
574
|
+
if (snapshot.nextPageToken !== undefined) { pb.setNextPageToken(snapshot.nextPageToken) }
|
|
575
|
+
return pb
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export function roomMemberSnapshotPbToPayload (
|
|
579
|
+
pb: puppet.RoomMemberSnapshotResponse, roomId: string, revision: string,
|
|
580
|
+
): PUPPET.payloads.RoomMemberSnapshot {
|
|
581
|
+
const loadedAt = pb.getLoadedAt()
|
|
582
|
+
const snapshot: PUPPET.payloads.RoomMemberSnapshot = {
|
|
583
|
+
roomId: pb.getId(),
|
|
584
|
+
revision: pb.getRevision(),
|
|
585
|
+
members: pb.getMembersList().map(roomMemberPbToPayload),
|
|
586
|
+
...(loadedAt ? { loadedAt: millisecondsFromTimestamp(loadedAt) } : {}),
|
|
587
|
+
...(pb.getNextPageToken() ? { nextPageToken: pb.getNextPageToken() } : {}),
|
|
588
|
+
}
|
|
589
|
+
validateRoomMemberSnapshot(snapshot, roomId, revision)
|
|
590
|
+
return snapshot
|
|
591
|
+
}
|
|
592
|
+
|
|
443
593
|
export const contactPbToPayload = (pb: puppet.ContactPayloadResponse) => {
|
|
444
594
|
const payload: PUPPET.payloads.Contact = {
|
|
445
595
|
address : pb.getAddress(),
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { test } from 'tstest'
|
|
2
|
+
import * as PUPPET from '@juzi/wechaty-puppet'
|
|
3
|
+
import { puppet as grpcPuppet } from '@juzi/wechaty-grpc'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
roomPayloadToPb,
|
|
7
|
+
roomPbToPayload,
|
|
8
|
+
roomMemberLoadStateToPb,
|
|
9
|
+
roomMemberLoadStatePbToPayload,
|
|
10
|
+
validateRoomMemberSnapshot,
|
|
11
|
+
} from './pb-payload-helper.js'
|
|
12
|
+
|
|
13
|
+
test('room payload roundtrip retains lazy fields and timestamp zero', async t => {
|
|
14
|
+
const payload: PUPPET.payloads.Room = {
|
|
15
|
+
id: 'room',
|
|
16
|
+
topic: 'topic',
|
|
17
|
+
adminIdList: [],
|
|
18
|
+
memberIdList: [],
|
|
19
|
+
memberCount: 0,
|
|
20
|
+
memberLoadMode: PUPPET.types.RoomMemberLoadMode.Lazy,
|
|
21
|
+
memberLoadStatus: PUPPET.types.RoomMemberLoadStatus.Loaded,
|
|
22
|
+
memberLoadRevision: '9007199254740993',
|
|
23
|
+
memberLoadedAt: 0,
|
|
24
|
+
}
|
|
25
|
+
const result = roomPbToPayload(roomPayloadToPb(payload))
|
|
26
|
+
t.equal(result.memberCount, 0)
|
|
27
|
+
t.equal(result.memberLoadMode, payload.memberLoadMode)
|
|
28
|
+
t.equal(result.memberLoadStatus, payload.memberLoadStatus)
|
|
29
|
+
t.equal(result.memberLoadRevision, payload.memberLoadRevision)
|
|
30
|
+
t.equal(result.memberLoadedAt, 0)
|
|
31
|
+
const legacy = roomPbToPayload(new grpcPuppet.RoomPayloadResponse())
|
|
32
|
+
t.equal(legacy.memberLoadMode, PUPPET.types.RoomMemberLoadMode.Unspecified)
|
|
33
|
+
t.equal(legacy.memberLoadedAt, undefined)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('load state preserves exact revision and rejects invalid responses', async t => {
|
|
37
|
+
const state: PUPPET.payloads.RoomMemberLoadState = {
|
|
38
|
+
roomId: 'room',
|
|
39
|
+
taskId: 'task',
|
|
40
|
+
status: PUPPET.types.RoomMemberLoadStatus.Pending,
|
|
41
|
+
revision: '9007199254740993',
|
|
42
|
+
loadedAt: 0,
|
|
43
|
+
}
|
|
44
|
+
t.same(roomMemberLoadStatePbToPayload(roomMemberLoadStateToPb(state), 'room'), state)
|
|
45
|
+
t.throws(() => roomMemberLoadStatePbToPayload(roomMemberLoadStateToPb(state), 'other'), /roomId/)
|
|
46
|
+
t.throws(() => roomMemberLoadStateToPb({ ...state, revision: '01' }), /revision/)
|
|
47
|
+
t.throws(() => roomMemberLoadStateToPb({ ...state, status: 99 }), /status/)
|
|
48
|
+
const response = roomMemberLoadStateToPb(state)
|
|
49
|
+
response.setStatus(99 as Parameters<typeof response.setStatus>[0])
|
|
50
|
+
t.throws(() => roomMemberLoadStatePbToPayload(response, 'room'), /status/)
|
|
51
|
+
response.setStatus(state.status)
|
|
52
|
+
response.setRevision('01')
|
|
53
|
+
t.throws(() => roomMemberLoadStatePbToPayload(response, 'room'), /revision/)
|
|
54
|
+
const failed: PUPPET.payloads.RoomMemberLoadState = {
|
|
55
|
+
...state, status: PUPPET.types.RoomMemberLoadStatus.Failed, error: 'load failed',
|
|
56
|
+
}
|
|
57
|
+
t.same(roomMemberLoadStatePbToPayload(roomMemberLoadStateToPb(failed), 'room'), failed)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('snapshot must match both room and requested revision', async t => {
|
|
61
|
+
const snapshot: PUPPET.payloads.RoomMemberSnapshot = {
|
|
62
|
+
roomId: 'room', revision: '2', members: [], nextPageToken: 'next',
|
|
63
|
+
}
|
|
64
|
+
t.doesNotThrow(() => validateRoomMemberSnapshot(snapshot, 'room', '2'))
|
|
65
|
+
t.throws(() => validateRoomMemberSnapshot(snapshot, 'room', '1'), /revision/)
|
|
66
|
+
t.throws(() => validateRoomMemberSnapshot(snapshot, 'other', '2'), /roomId/)
|
|
67
|
+
})
|