tmc.js 0.3.2 → 0.3.4

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 CHANGED
@@ -9,6 +9,12 @@ Events driven and chained Taobao Message Channel(TMC) for NodeJS
9
9
  [![downloads](https://img.shields.io/npm/dm/tmc.js)](https://www.npmjs.com/package/tmc.js)
10
10
  [![license](https://img.shields.io/npm/l/tmc.js)](https://www.npmjs.com/package/tmc.js)
11
11
 
12
+ ## 设计
13
+
14
+ 核心包以事件作为驱动,通过注册`TOPIC`回调处理函数,实现了TMC产品服务的消息消费处理能力,同时通过`Proxy`代理,以`TOPIC`作为键值,平序注册消息消费处理函数,序图如下:
15
+
16
+ [![SDK Sequence](./.github/sdk-sequence.svg)](./.github/sdk-sequence.mmd)
17
+
12
18
  ## 使用
13
19
 
14
20
  `npm i tmc.js`
@@ -54,6 +60,24 @@ new Tmc('your_app_key', 'your_app_secret')
54
60
 
55
61
  消费端发起建立连接 `onopen` 事件,`address` 默认为 `ws://mc.api.taobao.com/`。
56
62
 
63
+ **`tmc.send(msg: Message, options?: { mask?: true, binary?: true }, cb?: (err: Error) => void) => void`**
64
+
65
+ 实例化后,当自动应答确认消息无法满足需求的时候,比如消息处理失败,需要`Publisher`再次重推消息,在实例初始化时置`options.autoReplyConfirmation=false`,则在消息处理函数内,可以通过 `this.send()` 函数回复`确认`或者`失败`消息。例如:
66
+
67
+ ```js
68
+ new Tmc('your_app_key', 'your_app_secret', { autoReplyConfirmation: false })
69
+ .taobao_trade_TradeDelayConfirmPay(function needDoubleReriesThenConfirm(msg) {
70
+ if (msg instanceof Message && msg.content?.reried === 0) {
71
+ this.send(
72
+ new Message(MessageType.SENDACK, MessageKind.Failed)
73
+ .with(MessageFields.ID, msg.content?.id)
74
+ .with(MessageFields.MSG, 'Something went wrong, please retries this ID.')
75
+ );
76
+ }
77
+ })
78
+ .connect();
79
+ ```
80
+
57
81
  <details><summary>可选设置的 NODE_DEBUG=< label > 环境变量</summary>
58
82
 
59
83
  | label | 说明 |
@@ -63,7 +87,7 @@ new Tmc('your_app_key', 'your_app_secret')
63
87
  | `tmc:onpull` | 开启 `onpull` 时的日志
64
88
  | `tmc:onerror` | 开启 `onerror` 时的日志
65
89
  | `tmc:onclose` | 开启 `onclose` 时的日志
66
- | `tmc:onmessage` | 开启全部 `onmessage` 时的日志(即`From`淘宝消息)
90
+ | `tmc:onmessage*` | 开启全部 `onmessage` 时的日志(即`From`淘宝消息)
67
91
  | `tmc:onmessage:connect` | 开启消费端发起连接 `connect` 时的日志
68
92
  | `tmc:onmessage:connectack` | 开启消费端回复连接 `connectack` 时的日志
69
93
  | `tmc:onmessage:send` | 开启消费端接收到(即`From`淘宝) `send` 的消息时的日志
package/lib/consumer.js CHANGED
@@ -92,6 +92,11 @@ class TaoMessageConsumer extends EventEmitter {
92
92
  ).update(sk).digest('hex').toUpperCase();
93
93
  }
94
94
 
95
+ /** @since v0.3.4 */
96
+ send(data, options = { mask: true, binary: true }, cb = undefined) {
97
+ if (data instanceof Message) { this[kWebSocket]?.send(data.with(TOKEN, this[kToken]).buffer, options, cb); }
98
+ }
99
+
95
100
  onopen() {
96
101
  const now = `${Date.now()}`;
97
102
  const msg = new Message(CONNECT)
@@ -104,7 +109,7 @@ class TaoMessageConsumer extends EventEmitter {
104
109
 
105
110
  logger.onopen(JSONB.stringify(msg));
106
111
 
107
- this[kWebSocket]?.send(msg.buffer, { mask: true, binary: true });
112
+ this.send(msg);
108
113
 
109
114
  if (this[kOptions].intervalId) { clearInterval(this[kOptions].intervalId); }
110
115
 
@@ -114,7 +119,7 @@ class TaoMessageConsumer extends EventEmitter {
114
119
  onpull() {
115
120
  const msg = new Message(SEND, PullRequest).with(TOKEN, this[kToken]);
116
121
  logger.onpull(JSONB.stringify(msg));
117
- this[kWebSocket]?.send(msg.buffer, { mask: true, binary: true });
122
+ this.send(msg);
118
123
  }
119
124
 
120
125
  onping(data) {
@@ -142,7 +147,7 @@ class TaoMessageConsumer extends EventEmitter {
142
147
  if (this[kOptions].autoReplyConfirmation && msg && msg[CONTENT] && msg[CONTENT][ID]) {
143
148
  const reply = new Message(SEND, Confirm).with(TOKEN, this[kToken]).with(ID, msg[CONTENT][ID]);
144
149
  logger.onmessage[SEND][Confirm](JSONB.stringify(reply));
145
- this[kWebSocket]?.send(reply.buffer, { mask: true, binary: true });
150
+ this.send(reply);
146
151
  }
147
152
  }
148
153
 
@@ -178,6 +183,7 @@ class TaoMessageConsumer extends EventEmitter {
178
183
  if (this[kOptions].intervalId) { this[kOptions].intervalId = clearInterval(this[kOptions].intervalId); }
179
184
  if (this[kOptions].timeoutId) { this[kOptions].timeoutId = clearTimeout(this[kOptions].timeoutId); }
180
185
 
186
+ this[kToken] = undefined;
181
187
  this[kWebSocket] = undefined;
182
188
  this[kOptions].timeoutId = setTimeout(() => this.connect(), ms);
183
189
 
@@ -187,16 +193,12 @@ class TaoMessageConsumer extends EventEmitter {
187
193
  connect(address) {
188
194
  if (address) { this[kAddress] = address; }
189
195
 
190
- this[kWebSocket]?.close();
191
-
192
- const ws = new WebSocket(this[kAddress]);
193
- Reflect.set(this, kWebSocket, ws);
194
-
195
- ws.on('open', () => this.onopen());
196
- ws.on('ping', (...arg) => this.onping(...arg));
197
- ws.on('error', (err) => this.onerror(err));
198
- ws.on('close', (...arg) => this.onclose(...arg));
199
- ws.on('message', (...arg) => this.onmessage(...arg));
196
+ this[kWebSocket] = new WebSocket(this[kAddress])
197
+ .on('message', (data, isBinary) => this.onmessage(data, isBinary))
198
+ .on('close', (code, reason) => this.onclose(code, reason))
199
+ .on('error', (err) => this.onerror(err))
200
+ .on('ping', (data) => this.onping(data))
201
+ .on('open', () => this.onopen());
200
202
 
201
203
  return this;
202
204
  }
@@ -12,6 +12,11 @@ const SDK = 'sdk';
12
12
  const INTRANET_IP = 'intranet_ip';
13
13
  const ID = 'id';
14
14
  const TOPIC = 'topic';
15
+ const MSG = 'msg';
16
+ const DATAID = 'dataid';
17
+ const USERID = 'userid';
18
+ const OUTTIME = 'outtime';
19
+ const PUBLISHER = 'publisher';
15
20
 
16
21
  class MessageFields {
17
22
  static TYPE = TYPE;
@@ -41,6 +46,16 @@ class MessageFields {
41
46
  static ID = ID;
42
47
 
43
48
  static TOPIC = TOPIC;
49
+
50
+ static MSG = MSG;
51
+
52
+ static DATAID = DATAID;
53
+
54
+ static USERID = USERID;
55
+
56
+ static OUTTIME = OUTTIME;
57
+
58
+ static PUBLISHER = PUBLISHER;
44
59
  }
45
60
 
46
61
  module.exports = MessageFields;
@@ -59,3 +74,8 @@ module.exports.SDK = SDK;
59
74
  module.exports.INTRANET_IP = INTRANET_IP;
60
75
  module.exports.ID = ID;
61
76
  module.exports.TOPIC = TOPIC;
77
+ module.exports.MSG = MSG;
78
+ module.exports.DATAID = DATAID;
79
+ module.exports.USERID = USERID;
80
+ module.exports.OUTTIME = OUTTIME;
81
+ module.exports.PUBLISHER = PUBLISHER;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmc.js",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Events driven and chained Taobao Message Channel(TMC) for NodeJS",
5
5
  "author": "James ZHANG",
6
6
  "license": "MIT",
package/types/index.d.ts CHANGED
@@ -26,6 +26,10 @@ declare interface ConsumerOptions {
26
26
  declare interface TaoMessageConstractor extends EventEmitter {
27
27
  new (appKey: string, appSecret: BinaryLike, groupName?: string | ConsumerOptions, options?: ConsumerOptions): TaoMessageConsumer;
28
28
  sign(timestamp: string): string;
29
+ /** @since v0.3.4 */
30
+ send(data: Message, cb?: (err?: Error) => void): void;
31
+ /** @since v0.3.4 */
32
+ send(data: Message, options?: { mask?: true, binary?: true, compress?: boolean, fin?: boolean }, cb?: (err: Error) => void): void;
29
33
  onopen(): void;
30
34
  onpull(): void;
31
35
  onping(data: Buffer): void;
@@ -784,6 +788,10 @@ declare interface TaoTopicsDescriptor {
784
788
  tmall_auto_TradeModify(fn: (this: TaoMessageConsumer, message: IncomingMessage.TmallAutoTradeModify) => void): TaoMessageConsumer;
785
789
  /** {@link IncomingMessage.TmallAutoTwoWheelsReceiptCreate 天猫汽车 > 天猫二轮车服务工单创建开放} */
786
790
  tmall_auto_TwoWheelsReceiptCreate(fn: (this: TaoMessageConsumer, message: IncomingMessage.TmallAutoTwoWheelsReceiptCreate) => void): TaoMessageConsumer;
791
+ /** {@link IncomingMessage.TmallCarContractSign 天猫汽车 > 合同签署消息} */
792
+ tmall_car_ContractSign(fn: (this: TaoMessageConsumer, message: IncomingMessage.TmallCarContractSign) => void): TaoMessageConsumer;
793
+ /** {@link IncomingMessage.TmallCarFinanceMsg 天猫汽车 > 汽车金融消息} */
794
+ tmall_car_FinanceMsg(fn: (this: TaoMessageConsumer, message: IncomingMessage.TmallCarFinanceMsg) => void): TaoMessageConsumer;
787
795
  /** {@link IncomingMessage.TmallChannelApplyOrderChange 渠道中心API > 申请单变更消息} */
788
796
  tmall_channel_ApplyOrderChange(fn: (this: TaoMessageConsumer, message: IncomingMessage.TmallChannelApplyOrderChange) => void): TaoMessageConsumer;
789
797
  /** {@link IncomingMessage.TmallChannelDeliverOrderChange 渠道中心API > 发货单消息变更} */
@@ -1609,6 +1617,10 @@ declare interface TaoEventsListener {
1609
1617
  on(topic: 'tmall_auto_TradeModify', listener: (this: TaoMessageConsumer, message: IncomingMessage.TmallAutoTradeModify) => void): TaoMessageConsumer;
1610
1618
  /** {@link IncomingMessage.TmallAutoTwoWheelsReceiptCreate 天猫汽车 > 天猫二轮车服务工单创建开放} */
1611
1619
  on(topic: 'tmall_auto_TwoWheelsReceiptCreate', listener: (this: TaoMessageConsumer, message: IncomingMessage.TmallAutoTwoWheelsReceiptCreate) => void): TaoMessageConsumer;
1620
+ /** {@link IncomingMessage.TmallCarContractSign 天猫汽车 > 合同签署消息} */
1621
+ on(topic: 'tmall_car_ContractSign', listener: (this: TaoMessageConsumer, message: IncomingMessage.TmallCarContractSign) => void): TaoMessageConsumer;
1622
+ /** {@link IncomingMessage.TmallCarFinanceMsg 天猫汽车 > 汽车金融消息} */
1623
+ on(topic: 'tmall_car_FinanceMsg', listener: (this: TaoMessageConsumer, message: IncomingMessage.TmallCarFinanceMsg) => void): TaoMessageConsumer;
1612
1624
  /** {@link IncomingMessage.TmallChannelApplyOrderChange 渠道中心API > 申请单变更消息} */
1613
1625
  on(topic: 'tmall_channel_ApplyOrderChange', listener: (this: TaoMessageConsumer, message: IncomingMessage.TmallChannelApplyOrderChange) => void): TaoMessageConsumer;
1614
1626
  /** {@link IncomingMessage.TmallChannelDeliverOrderChange 渠道中心API > 发货单消息变更} */
@@ -13,6 +13,11 @@ declare namespace MessageFields {
13
13
  type INTRANET_IP = 'intranet_ip';
14
14
  type ID = 'id';
15
15
  type TOPIC = 'topic';
16
+ type MSG = 'msg';
17
+ type DATAID = 'dataid';
18
+ type USERID = 'userid';
19
+ type OUTTIME = 'outtime';
20
+ type PUBLISHER = 'publisher';
16
21
  }
17
22
 
18
23
  declare class MessageFields {
@@ -30,4 +35,9 @@ declare class MessageFields {
30
35
  static INTRANET_IP: MessageFields.INTRANET_IP;
31
36
  static ID: MessageFields.ID;
32
37
  static TOPIC: MessageFields.TOPIC;
38
+ static MSG: MessageFields.MSG;
39
+ static DATAID: MessageFields.DATAID;
40
+ static USERID: MessageFields.USERID;
41
+ static OUTTIME: MessageFields.OUTTIME;
42
+ static PUBLISHER: MessageFields.PUBLISHER;
33
43
  }
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- /// <reference path="header-type.d.ts" />
2
+ /// <reference path="message-type.d.ts" />
3
3
  /// <reference path="message-kind.d.ts" />
4
4
 
5
5
  declare class Message {
@@ -7,14 +7,14 @@ declare class Message {
7
7
  type?: MessageType.CONNECT | MessageType.CONNECTACK | MessageType.SEND | MessageType.SENDACK,
8
8
  kind?: MessageKind.None | MessageKind.PullRequest | MessageKind.Confirm | MessageKind.Data | MessageKind.Failed
9
9
  );
10
- protocolVersion: number;
11
- messageType: number;
10
+ protocolVersion: 2;
11
+ messageType: MessageType.CONNECT | MessageType.CONNECTACK | MessageType.SEND | MessageType.SENDACK;
12
12
  statusCode?: number;
13
13
  statusPhrase?: string;
14
14
  flag?: number;
15
15
  token?: string;
16
16
  content?: MessageContent;
17
- with(key: string, value?: number | bigint | string | Buffer | Date): this;
17
+ with(key: string | MessageKind, value?: number | bigint | string | Buffer | Date): this;
18
18
  get buffer(): Buffer;
19
19
  static from(buf: Buffer): Message;
20
20
  }
@@ -751,6 +751,10 @@ declare namespace IncomingMessage {
751
751
  type TmallAutoTradeModify = Message & { content?: MessageContent & { content?: string | Tmall.Auto.TradeModify } };
752
752
  /** {@link Tmall.Auto.TwoWheelsReceiptCreate 天猫汽车 > 天猫二轮车服务工单创建开放} */
753
753
  type TmallAutoTwoWheelsReceiptCreate = Message & { content?: MessageContent & { content?: string | Tmall.Auto.TwoWheelsReceiptCreate } };
754
+ /** {@link Tmall.Car.ContractSign 天猫汽车 > 合同签署消息} */
755
+ type TmallCarContractSign = Message & { content?: MessageContent & { content?: string | Tmall.Car.ContractSign } };
756
+ /** {@link Tmall.Car.FinanceMsg 天猫汽车 > 汽车金融消息} */
757
+ type TmallCarFinanceMsg = Message & { content?: MessageContent & { content?: string | Tmall.Car.FinanceMsg } };
754
758
  /** {@link Tmall.Channel.ApplyOrderChange 渠道中心API > 申请单变更消息} */
755
759
  type TmallChannelApplyOrderChange = Message & { content?: MessageContent & { content?: string | Tmall.Channel.ApplyOrderChange } };
756
760
  /** {@link Tmall.Channel.DeliverOrderChange 渠道中心API > 发货单消息变更} */
package/types/tmall.d.ts CHANGED
@@ -28,6 +28,25 @@ declare namespace Tmall.Auto {
28
28
  }
29
29
  }
30
30
 
31
+ /** 天猫汽车 */
32
+ declare namespace Tmall.Car {
33
+ /** 合同签署消息 */
34
+ interface ContractSign {
35
+ /** 合同下载地址,有效期10分钟 */
36
+ contract_url: string;
37
+ /** 订单id */
38
+ order_id: number;
39
+ }
40
+
41
+ /** 汽车金融消息 */
42
+ interface FinanceMsg {
43
+ /** 订单id */
44
+ order_id: number;
45
+ /** 消息类型: FULL_PURCHASE_FINANCE_CANCEL_MSG_TYPE?,因全款购车导致金融取消 REFUND_FINANCE_CANCEL,因退款导致的金融单关闭 */
46
+ type: string;
47
+ }
48
+ }
49
+
31
50
  /** 渠道中心API */
32
51
  declare namespace Tmall.Channel {
33
52
  /** 申请单变更消息 */