@juzi/wechaty-puppet-service 1.0.119 → 1.0.120
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/client/puppet-service.d.ts +13 -0
- package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
- package/dist/cjs/src/client/puppet-service.js +48 -11
- package/dist/cjs/src/client/puppet-service.js.map +1 -1
- package/dist/cjs/src/package-json.js +1 -1
- package/dist/cjs/tests/fast-dirty-completeness.spec.d.ts +3 -0
- package/dist/cjs/tests/fast-dirty-completeness.spec.d.ts.map +1 -0
- package/dist/cjs/tests/fast-dirty-completeness.spec.js +59 -0
- package/dist/cjs/tests/fast-dirty-completeness.spec.js.map +1 -0
- package/dist/cjs/tests/fast-dirty-room-member.spec.d.ts +3 -0
- package/dist/cjs/tests/fast-dirty-room-member.spec.d.ts.map +1 -0
- package/dist/cjs/tests/fast-dirty-room-member.spec.js +77 -0
- package/dist/cjs/tests/fast-dirty-room-member.spec.js.map +1 -0
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.d.ts +3 -0
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.d.ts.map +1 -0
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.js +72 -0
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.js.map +1 -0
- package/dist/esm/src/client/puppet-service.d.ts +13 -0
- package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
- package/dist/esm/src/client/puppet-service.js +48 -11
- package/dist/esm/src/client/puppet-service.js.map +1 -1
- package/dist/esm/src/package-json.js +1 -1
- package/dist/esm/tests/fast-dirty-completeness.spec.d.ts +3 -0
- package/dist/esm/tests/fast-dirty-completeness.spec.d.ts.map +1 -0
- package/dist/esm/tests/fast-dirty-completeness.spec.js +34 -0
- package/dist/esm/tests/fast-dirty-completeness.spec.js.map +1 -0
- package/dist/esm/tests/fast-dirty-room-member.spec.d.ts +3 -0
- package/dist/esm/tests/fast-dirty-room-member.spec.d.ts.map +1 -0
- package/dist/esm/tests/fast-dirty-room-member.spec.js +49 -0
- package/dist/esm/tests/fast-dirty-room-member.spec.js.map +1 -0
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.d.ts +3 -0
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.d.ts.map +1 -0
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.js +47 -0
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.js.map +1 -0
- package/package.json +1 -1
- package/src/client/puppet-service.ts +52 -14
- package/src/package-json.ts +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env -S node --no-warnings --loader ts-node/esm
|
|
2
|
+
/**
|
|
3
|
+
* Regression test for the double JSON.parse on every EVENT_TYPE_DIRTY
|
|
4
|
+
* stream event.
|
|
5
|
+
*
|
|
6
|
+
* The previous implementation was:
|
|
7
|
+
*
|
|
8
|
+
* case grpcPuppet.EventType.EVENT_TYPE_DIRTY:
|
|
9
|
+
* await this.fastDirty(JSON.parse(payload))
|
|
10
|
+
* this.emit('dirty', JSON.parse(payload) as PUPPET.payloads.EventDirty)
|
|
11
|
+
*
|
|
12
|
+
* which parses the same JSON string twice for every dirty event on the
|
|
13
|
+
* hot stream path. Parse the payload once and reuse the resulting object.
|
|
14
|
+
*/
|
|
15
|
+
import { test, sinon } from 'tstest';
|
|
16
|
+
import * as PUPPET from '@juzi/wechaty-puppet';
|
|
17
|
+
import { puppet as grpcPuppet } from '@juzi/wechaty-grpc';
|
|
18
|
+
import { PuppetService } from '../src/mod.js';
|
|
19
|
+
test('onGrpcStreamEvent must parse a DIRTY payload only once', async (t) => {
|
|
20
|
+
const token = `puppet_service_test_${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
21
|
+
const puppet = new PuppetService({ token });
|
|
22
|
+
// Force the fastDirty branch to be a no-op so we measure only the
|
|
23
|
+
// parsing cost in the dispatch case.
|
|
24
|
+
puppet.fastDirty = async (_) => { };
|
|
25
|
+
const dirtyPayload = JSON.stringify({
|
|
26
|
+
payloadType: PUPPET.types.Dirty.Contact,
|
|
27
|
+
payloadId: 'x',
|
|
28
|
+
});
|
|
29
|
+
// Build a minimal grpcPuppet.EventResponse shape -- only the getters
|
|
30
|
+
// touched by `onGrpcStreamEvent` are needed.
|
|
31
|
+
const fakeEvent = {
|
|
32
|
+
getType: () => grpcPuppet.EventType.EVENT_TYPE_DIRTY,
|
|
33
|
+
getPayload: () => dirtyPayload,
|
|
34
|
+
getSeq: () => 0,
|
|
35
|
+
};
|
|
36
|
+
const parseSpy = sinon.spy(JSON, 'parse');
|
|
37
|
+
try {
|
|
38
|
+
const baseline = parseSpy.callCount;
|
|
39
|
+
await puppet.onGrpcStreamEvent(fakeEvent);
|
|
40
|
+
const delta = parseSpy.callCount - baseline;
|
|
41
|
+
t.equal(delta, 1, `EVENT_TYPE_DIRTY should JSON.parse(payload) only once, observed ${delta}`);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
parseSpy.restore();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=grpc-stream-dirty-parse.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grpc-stream-dirty-parse.spec.js","sourceRoot":"","sources":["../../../tests/grpc-stream-dirty-parse.spec.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAEpC,OAAO,KAAK,MAAM,MAAM,sBAAsB,CAAA;AAC9C,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAE7C,IAAI,CAAC,wDAAwD,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;IACvE,MAAM,KAAK,GAAG,uBAAuB,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAA;IACpF,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,CAAQ,CAAA;IAElD,kEAAkE;IAClE,qCAAqC;IACrC,MAAM,CAAC,SAAS,GAAG,KAAK,EAAE,CAAM,EAAE,EAAE,GAAE,CAAC,CAAA;IAEvC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC;QAClC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO;QACvC,SAAS,EAAI,GAAG;KACjB,CAAC,CAAA;IAEF,qEAAqE;IACrE,6CAA6C;IAC7C,MAAM,SAAS,GAAG;QAChB,OAAO,EAAK,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,gBAAgB;QACvD,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY;QAC9B,MAAM,EAAM,GAAG,EAAE,CAAC,CAAC;KACpB,CAAA;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACzC,IAAI;QACF,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAA;QACnC,MAAM,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAA;QACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAA;QAC3C,CAAC,CAAC,KAAK,CACL,KAAK,EACL,CAAC,EACD,mEAAmE,KAAK,EAAE,CAC3E,CAAA;KACF;YAAS;QACR,QAAQ,CAAC,OAAO,EAAE,CAAA;KACnB;AACH,CAAC,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -367,10 +367,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
367
367
|
)
|
|
368
368
|
}
|
|
369
369
|
break
|
|
370
|
-
case grpcPuppet.EventType.EVENT_TYPE_DIRTY:
|
|
371
|
-
|
|
372
|
-
this.
|
|
370
|
+
case grpcPuppet.EventType.EVENT_TYPE_DIRTY: {
|
|
371
|
+
const dirtyPayload = JSON.parse(payload) as PUPPET.payloads.EventDirty
|
|
372
|
+
await this.fastDirty(dirtyPayload)
|
|
373
|
+
this.emit('dirty', dirtyPayload)
|
|
373
374
|
break
|
|
375
|
+
}
|
|
374
376
|
case grpcPuppet.EventType.EVENT_TYPE_MESSAGE:
|
|
375
377
|
this.emit('message', JSON.parse(payload) as PUPPET.payloads.EventMessage)
|
|
376
378
|
break
|
|
@@ -495,6 +497,44 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
495
497
|
}
|
|
496
498
|
}
|
|
497
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Dirty handler registry, keyed by every `PUPPET.types.Dirty` value.
|
|
502
|
+
*
|
|
503
|
+
* Using a full `Record<...>` (rather than `Partial<...>`) means
|
|
504
|
+
* TypeScript catches a missing entry at compile time -- so when a
|
|
505
|
+
* new DirtyType is added to wechaty-puppet, callers get a build
|
|
506
|
+
* error instead of a silent runtime no-op.
|
|
507
|
+
*
|
|
508
|
+
* Note: `Unspecified` is treated as an upstream contract violation,
|
|
509
|
+
* not a benign no-op -- it logs an error and emits 'error' so hosts
|
|
510
|
+
* notice.
|
|
511
|
+
*/
|
|
512
|
+
private get _dirtyHandlerMap (): Record<PUPPET.types.Dirty, (id: string) => Promise<unknown>> {
|
|
513
|
+
return {
|
|
514
|
+
[PUPPET.types.Dirty.Contact]: async (id: string) => this._payloadStore.contact?.delete(id),
|
|
515
|
+
[PUPPET.types.Dirty.Friendship]: async (_: string) => {},
|
|
516
|
+
[PUPPET.types.Dirty.Message]: async (_: string) => {},
|
|
517
|
+
[PUPPET.types.Dirty.Post]: async (_: string) => {},
|
|
518
|
+
[PUPPET.types.Dirty.Room]: async (id: string) => this._payloadStore.room?.delete(id),
|
|
519
|
+
[PUPPET.types.Dirty.RoomMember]: async (id: string) => {
|
|
520
|
+
const [ roomId ] = id.split(PUPPET.STRING_SPLITTER)
|
|
521
|
+
if (roomId) {
|
|
522
|
+
await this._payloadStore.roomMember?.delete(roomId)
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
[PUPPET.types.Dirty.Tag]: async (id: string) => this._payloadStore.tag?.delete(id),
|
|
526
|
+
[PUPPET.types.Dirty.TagGroup]: async (id: string) => this._payloadStore.tagGroup?.delete(id),
|
|
527
|
+
[PUPPET.types.Dirty.WxxdProduct]: async (_: string) => {},
|
|
528
|
+
[PUPPET.types.Dirty.WxxdOrder]: async (_: string) => {},
|
|
529
|
+
[PUPPET.types.Dirty.Call]: async (_: string) => {},
|
|
530
|
+
[PUPPET.types.Dirty.Unspecified]: async (id: string) => {
|
|
531
|
+
const msg = `fastDirty() received Unspecified dirty type (id=${id}); upstream puppet is leaking protobuf default — this is a server-side contract bug`
|
|
532
|
+
log.error('PuppetService', msg)
|
|
533
|
+
this.emit('error', new Error(msg))
|
|
534
|
+
},
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
498
538
|
/**
|
|
499
539
|
* `onDirty()` is called when the puppet emit `dirty` event.
|
|
500
540
|
* the event listener will be registered in `start()` from the `PuppetAbstract` class
|
|
@@ -507,20 +547,18 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
507
547
|
): Promise<void> {
|
|
508
548
|
log.verbose('PuppetService', 'fastDirty(%s<%s>, %s)', PUPPET.types.Dirty[payloadType], payloadType, payloadId)
|
|
509
549
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
[PUPPET.types.Dirty.TagGroup]: async (id: string) => this._payloadStore.tagGroup?.delete(id),
|
|
519
|
-
[PUPPET.types.Dirty.Unspecified]: async (id: string) => { throw new Error('Unspecified type with id: ' + id) },
|
|
550
|
+
// payloadType is typed as the enum, but at runtime the server may emit a
|
|
551
|
+
// value outside our enum (forward-compat). Look it up via a
|
|
552
|
+
// possibly-undefined view so the runtime guard stays meaningful.
|
|
553
|
+
const lookup = this._dirtyHandlerMap as Partial<Record<PUPPET.types.Dirty, (id: string) => Promise<unknown>>>
|
|
554
|
+
const handler = lookup[payloadType]
|
|
555
|
+
if (!handler) {
|
|
556
|
+
log.warn('PuppetService', 'fastDirty() no handler for payloadType=%s', payloadType)
|
|
557
|
+
return
|
|
520
558
|
}
|
|
521
559
|
|
|
522
560
|
try {
|
|
523
|
-
await
|
|
561
|
+
await handler(payloadId)
|
|
524
562
|
} catch (error) {
|
|
525
563
|
this.emit('error', error)
|
|
526
564
|
}
|
package/src/package-json.ts
CHANGED