@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.
Files changed (79) hide show
  1. package/dist/cjs/src/auth/call-cred.d.ts +2 -2
  2. package/dist/cjs/src/auth/call-cred.d.ts.map +1 -1
  3. package/dist/cjs/src/auth/call-cred.js.map +1 -1
  4. package/dist/cjs/src/client/puppet-service.d.ts +25 -2
  5. package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
  6. package/dist/cjs/src/client/puppet-service.js +389 -124
  7. package/dist/cjs/src/client/puppet-service.js.map +1 -1
  8. package/dist/cjs/src/client/room-cache-coordinator.d.ts +14 -0
  9. package/dist/cjs/src/client/room-cache-coordinator.d.ts.map +1 -0
  10. package/dist/cjs/src/client/room-cache-coordinator.js +53 -0
  11. package/dist/cjs/src/client/room-cache-coordinator.js.map +1 -0
  12. package/dist/cjs/src/client/room-member-cache.spec.d.ts +2 -0
  13. package/dist/cjs/src/client/room-member-cache.spec.d.ts.map +1 -0
  14. package/dist/cjs/src/client/room-member-cache.spec.js +417 -0
  15. package/dist/cjs/src/client/room-member-cache.spec.js.map +1 -0
  16. package/dist/cjs/src/client/room-member-load.spec.d.ts +2 -0
  17. package/dist/cjs/src/client/room-member-load.spec.d.ts.map +1 -0
  18. package/dist/cjs/src/client/room-member-load.spec.js +324 -0
  19. package/dist/cjs/src/client/room-member-load.spec.js.map +1 -0
  20. package/dist/cjs/src/package-json.js +4 -4
  21. package/dist/cjs/src/server/event-stream-manager.d.ts.map +1 -1
  22. package/dist/cjs/src/server/event-stream-manager.js +16 -0
  23. package/dist/cjs/src/server/event-stream-manager.js.map +1 -1
  24. package/dist/cjs/src/server/puppet-implementation.d.ts.map +1 -1
  25. package/dist/cjs/src/server/puppet-implementation.js +91 -28
  26. package/dist/cjs/src/server/puppet-implementation.js.map +1 -1
  27. package/dist/cjs/src/utils/pb-payload-helper.d.ts +10 -0
  28. package/dist/cjs/src/utils/pb-payload-helper.d.ts.map +1 -1
  29. package/dist/cjs/src/utils/pb-payload-helper.js +180 -1
  30. package/dist/cjs/src/utils/pb-payload-helper.js.map +1 -1
  31. package/dist/cjs/src/utils/room-member-load.spec.d.ts +2 -0
  32. package/dist/cjs/src/utils/room-member-load.spec.d.ts.map +1 -0
  33. package/dist/cjs/src/utils/room-member-load.spec.js +83 -0
  34. package/dist/cjs/src/utils/room-member-load.spec.js.map +1 -0
  35. package/dist/esm/src/auth/call-cred.d.ts +2 -2
  36. package/dist/esm/src/auth/call-cred.d.ts.map +1 -1
  37. package/dist/esm/src/auth/call-cred.js.map +1 -1
  38. package/dist/esm/src/client/puppet-service.d.ts +25 -2
  39. package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
  40. package/dist/esm/src/client/puppet-service.js +390 -125
  41. package/dist/esm/src/client/puppet-service.js.map +1 -1
  42. package/dist/esm/src/client/room-cache-coordinator.d.ts +14 -0
  43. package/dist/esm/src/client/room-cache-coordinator.d.ts.map +1 -0
  44. package/dist/esm/src/client/room-cache-coordinator.js +49 -0
  45. package/dist/esm/src/client/room-cache-coordinator.js.map +1 -0
  46. package/dist/esm/src/client/room-member-cache.spec.d.ts +2 -0
  47. package/dist/esm/src/client/room-member-cache.spec.d.ts.map +1 -0
  48. package/dist/esm/src/client/room-member-cache.spec.js +415 -0
  49. package/dist/esm/src/client/room-member-cache.spec.js.map +1 -0
  50. package/dist/esm/src/client/room-member-load.spec.d.ts +2 -0
  51. package/dist/esm/src/client/room-member-load.spec.d.ts.map +1 -0
  52. package/dist/esm/src/client/room-member-load.spec.js +296 -0
  53. package/dist/esm/src/client/room-member-load.spec.js.map +1 -0
  54. package/dist/esm/src/package-json.js +4 -4
  55. package/dist/esm/src/server/event-stream-manager.d.ts.map +1 -1
  56. package/dist/esm/src/server/event-stream-manager.js +16 -0
  57. package/dist/esm/src/server/event-stream-manager.js.map +1 -1
  58. package/dist/esm/src/server/puppet-implementation.d.ts.map +1 -1
  59. package/dist/esm/src/server/puppet-implementation.js +92 -29
  60. package/dist/esm/src/server/puppet-implementation.js.map +1 -1
  61. package/dist/esm/src/utils/pb-payload-helper.d.ts +10 -0
  62. package/dist/esm/src/utils/pb-payload-helper.d.ts.map +1 -1
  63. package/dist/esm/src/utils/pb-payload-helper.js +169 -0
  64. package/dist/esm/src/utils/pb-payload-helper.js.map +1 -1
  65. package/dist/esm/src/utils/room-member-load.spec.d.ts +2 -0
  66. package/dist/esm/src/utils/room-member-load.spec.d.ts.map +1 -0
  67. package/dist/esm/src/utils/room-member-load.spec.js +58 -0
  68. package/dist/esm/src/utils/room-member-load.spec.js.map +1 -0
  69. package/package.json +4 -4
  70. package/src/auth/call-cred.ts +1 -1
  71. package/src/client/puppet-service.ts +324 -138
  72. package/src/client/room-cache-coordinator.ts +50 -0
  73. package/src/client/room-member-cache.spec.ts +448 -0
  74. package/src/client/room-member-load.spec.ts +319 -0
  75. package/src/package-json.ts +4 -4
  76. package/src/server/event-stream-manager.ts +15 -0
  77. package/src/server/puppet-implementation.ts +94 -29
  78. package/src/utils/pb-payload-helper.ts +150 -0
  79. 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
+ })