tmc.js 0.3.3 → 0.3.5
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 +19 -1
- package/lib/consumer.js +25 -13
- package/lib/message.js +11 -30
- package/package.json +3 -3
- package/types/index.d.ts +4 -0
package/README.md
CHANGED
|
@@ -60,6 +60,24 @@ new Tmc('your_app_key', 'your_app_secret')
|
|
|
60
60
|
|
|
61
61
|
消费端发起建立连接 `onopen` 事件,`address` 默认为 `ws://mc.api.taobao.com/`。
|
|
62
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?.retried === 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
|
+
|
|
63
81
|
<details><summary>可选设置的 NODE_DEBUG=< label > 环境变量</summary>
|
|
64
82
|
|
|
65
83
|
| label | 说明 |
|
|
@@ -69,7 +87,7 @@ new Tmc('your_app_key', 'your_app_secret')
|
|
|
69
87
|
| `tmc:onpull` | 开启 `onpull` 时的日志
|
|
70
88
|
| `tmc:onerror` | 开启 `onerror` 时的日志
|
|
71
89
|
| `tmc:onclose` | 开启 `onclose` 时的日志
|
|
72
|
-
| `tmc:onmessage
|
|
90
|
+
| `tmc:onmessage*` | 开启全部 `onmessage` 时的日志(即`From`淘宝消息)
|
|
73
91
|
| `tmc:onmessage:connect` | 开启消费端发起连接 `connect` 时的日志
|
|
74
92
|
| `tmc:onmessage:connectack` | 开启消费端回复连接 `connectack` 时的日志
|
|
75
93
|
| `tmc:onmessage:send` | 开启消费端接收到(即`From`淘宝) `send` 的消息时的日志
|
package/lib/consumer.js
CHANGED
|
@@ -3,7 +3,7 @@ const { networkInterfaces } = require('os');
|
|
|
3
3
|
const { EventEmitter, captureRejectionSymbol } = require('events');
|
|
4
4
|
|
|
5
5
|
const WebSocket = require('ws');
|
|
6
|
-
const
|
|
6
|
+
const { stringify, parse } = require('json-bigint')({ useNativeBigInt: true });
|
|
7
7
|
|
|
8
8
|
const logger = require('./logger');
|
|
9
9
|
const Message = require('./message');
|
|
@@ -92,6 +92,17 @@ class TaoMessageConsumer extends EventEmitter {
|
|
|
92
92
|
).update(sk).digest('hex').toUpperCase();
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {Message} data - The data
|
|
97
|
+
* @param {object|Function} [options] - The options
|
|
98
|
+
* @param {Function} [cb] - The Callback
|
|
99
|
+
* @returns {void}
|
|
100
|
+
* @since v0.3.4
|
|
101
|
+
*/
|
|
102
|
+
send(data, options = { mask: true, binary: true }, cb = undefined) {
|
|
103
|
+
if (data instanceof Message) { this[kWebSocket]?.send(data.with(TOKEN, this[kToken]).buffer, options, cb); }
|
|
104
|
+
}
|
|
105
|
+
|
|
95
106
|
onopen() {
|
|
96
107
|
const now = `${Date.now()}`;
|
|
97
108
|
const msg = new Message(CONNECT)
|
|
@@ -102,9 +113,9 @@ class TaoMessageConsumer extends EventEmitter {
|
|
|
102
113
|
.with(SDK, this.constructor.name)
|
|
103
114
|
.with(INTRANET_IP, ipAddr());
|
|
104
115
|
|
|
105
|
-
logger.onopen(
|
|
116
|
+
logger.onopen(stringify(msg));
|
|
106
117
|
|
|
107
|
-
this
|
|
118
|
+
this.send(msg);
|
|
108
119
|
|
|
109
120
|
if (this[kOptions].intervalId) { clearInterval(this[kOptions].intervalId); }
|
|
110
121
|
|
|
@@ -113,22 +124,22 @@ class TaoMessageConsumer extends EventEmitter {
|
|
|
113
124
|
|
|
114
125
|
onpull() {
|
|
115
126
|
const msg = new Message(SEND, PullRequest).with(TOKEN, this[kToken]);
|
|
116
|
-
logger.onpull(
|
|
117
|
-
this
|
|
127
|
+
logger.onpull(stringify(msg));
|
|
128
|
+
this.send(msg);
|
|
118
129
|
}
|
|
119
130
|
|
|
120
131
|
onping(data) {
|
|
121
|
-
logger.onping('The channel is onping with data [%s].',
|
|
122
|
-
this[kWebSocket]?.pong(data,
|
|
132
|
+
logger.onping('The channel is onping with data [%s].', stringify(data));
|
|
133
|
+
this[kWebSocket]?.pong(data, true);
|
|
123
134
|
}
|
|
124
135
|
|
|
125
136
|
onerror(err) {
|
|
126
|
-
logger.onerror('The channel is onerror(%s), let us reconnect.',
|
|
137
|
+
logger.onerror('The channel is onerror(%s), let us reconnect.', stringify(err));
|
|
127
138
|
this.reconnect(this[kOptions].onErrorReconnection);
|
|
128
139
|
}
|
|
129
140
|
|
|
130
141
|
onclose(...arg) {
|
|
131
|
-
logger.onclose('The channel is onclose(%s), let us reconnect.',
|
|
142
|
+
logger.onclose('The channel is onclose(%s), let us reconnect.', stringify(arg));
|
|
132
143
|
this.reconnect(this[kOptions].onCloseReconnection);
|
|
133
144
|
}
|
|
134
145
|
|
|
@@ -141,8 +152,8 @@ class TaoMessageConsumer extends EventEmitter {
|
|
|
141
152
|
|
|
142
153
|
if (this[kOptions].autoReplyConfirmation && msg && msg[CONTENT] && msg[CONTENT][ID]) {
|
|
143
154
|
const reply = new Message(SEND, Confirm).with(TOKEN, this[kToken]).with(ID, msg[CONTENT][ID]);
|
|
144
|
-
logger.onmessage[SEND][Confirm](
|
|
145
|
-
this
|
|
155
|
+
logger.onmessage[SEND][Confirm](stringify(reply));
|
|
156
|
+
this.send(reply);
|
|
146
157
|
}
|
|
147
158
|
}
|
|
148
159
|
|
|
@@ -164,10 +175,10 @@ class TaoMessageConsumer extends EventEmitter {
|
|
|
164
175
|
&& msg[CONTENT][CONTENT]
|
|
165
176
|
&& typeof msg[CONTENT][CONTENT] === 'string'
|
|
166
177
|
&& /^\{.*\}$/.test(msg[CONTENT][CONTENT])) {
|
|
167
|
-
Reflect.set(msg[CONTENT], CONTENT,
|
|
178
|
+
Reflect.set(msg[CONTENT], CONTENT, parse(msg[CONTENT][CONTENT]));
|
|
168
179
|
}
|
|
169
180
|
|
|
170
|
-
logger.onmessage[msg[TYPE]](
|
|
181
|
+
logger.onmessage[msg[TYPE]](stringify(msg));
|
|
171
182
|
|
|
172
183
|
this[`process${msg[TYPE]}`](msg);
|
|
173
184
|
}
|
|
@@ -178,6 +189,7 @@ class TaoMessageConsumer extends EventEmitter {
|
|
|
178
189
|
if (this[kOptions].intervalId) { this[kOptions].intervalId = clearInterval(this[kOptions].intervalId); }
|
|
179
190
|
if (this[kOptions].timeoutId) { this[kOptions].timeoutId = clearTimeout(this[kOptions].timeoutId); }
|
|
180
191
|
|
|
192
|
+
this[kToken] = undefined;
|
|
181
193
|
this[kWebSocket] = undefined;
|
|
182
194
|
this[kOptions].timeoutId = setTimeout(() => this.connect(), ms);
|
|
183
195
|
|
package/lib/message.js
CHANGED
|
@@ -29,10 +29,8 @@ class Message {
|
|
|
29
29
|
[CONTENT] = {};
|
|
30
30
|
|
|
31
31
|
constructor(type, kind) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/* eslint-disable-next-line no-unused-expressions */
|
|
35
|
-
MessageKind.includes(kind) && Reflect.set(this[CONTENT], MessageKind, kind);
|
|
32
|
+
if (MessageType.includes(type)) { this[TYPE] = type; }
|
|
33
|
+
if (MessageKind.includes(kind)) { this[CONTENT][MessageKind] = kind; }
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
with(key, value) {
|
|
@@ -93,14 +91,10 @@ class Encoder {
|
|
|
93
91
|
|
|
94
92
|
compress() {
|
|
95
93
|
this.put(this[kData].protocolVersion).put(this[kData][TYPE]);
|
|
96
|
-
|
|
97
|
-
this[kData][
|
|
98
|
-
|
|
99
|
-
this[kData][
|
|
100
|
-
/* eslint-disable-next-line no-unused-expressions */
|
|
101
|
-
this[kData][FLAG] && this.putShort(Flag).putInt(this[kData][FLAG]);
|
|
102
|
-
/* eslint-disable-next-line no-unused-expressions */
|
|
103
|
-
this[kData][TOKEN] && this.putShort(Token).writeCountedString(this[kData][TOKEN]);
|
|
94
|
+
if (this[kData][CODE]) { this.putShort(StatusCode).putShort(this[kData][CODE]); }
|
|
95
|
+
if (this[kData][PHRASE]) { this.putShort(StatusPhrase).writeCountedString(this[kData][PHRASE]); }
|
|
96
|
+
if (this[kData][FLAG]) { this.putShort(Flag).putInt(this[kData][FLAG]); }
|
|
97
|
+
if (this[kData][TOKEN]) { this.putShort(Token).writeCountedString(this[kData][TOKEN]); }
|
|
104
98
|
Object.entries(this[kData][CONTENT] || {}).forEach(([k, v]) => this.putShort(Custom).writeCountedString(k).writeCustomValue(v));
|
|
105
99
|
this.putShort(EndOfHeaders);
|
|
106
100
|
|
|
@@ -134,8 +128,7 @@ class Encoder {
|
|
|
134
128
|
writeCountedString(value) {
|
|
135
129
|
const len = value ? Buffer.byteLength(value) : 0;
|
|
136
130
|
this.putInt(len);
|
|
137
|
-
|
|
138
|
-
len && this[kBuffer].push(pick(len, write, value, utf8));
|
|
131
|
+
if (len) { this[kBuffer].push(pick(len, write, value, utf8)); }
|
|
139
132
|
|
|
140
133
|
return this;
|
|
141
134
|
}
|
|
@@ -187,18 +180,6 @@ class Encoder {
|
|
|
187
180
|
|
|
188
181
|
const kOffset = Symbol('kOffset');
|
|
189
182
|
|
|
190
|
-
const readInt8 = 'readInt8';
|
|
191
|
-
|
|
192
|
-
const readInt16LE = 'readInt16LE';
|
|
193
|
-
|
|
194
|
-
const readInt32LE = 'readInt32LE';
|
|
195
|
-
|
|
196
|
-
const readBigInt64LE = 'readBigInt64LE';
|
|
197
|
-
|
|
198
|
-
function read(buffer, method, offset, ...extra) {
|
|
199
|
-
return buffer[method](offset, ...extra);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
183
|
class Decoder {
|
|
203
184
|
[kOffset] = 1;
|
|
204
185
|
|
|
@@ -245,25 +226,25 @@ class Decoder {
|
|
|
245
226
|
}
|
|
246
227
|
|
|
247
228
|
get() {
|
|
248
|
-
const value =
|
|
229
|
+
const value = this[kBuffer].readInt8(this[kOffset]);
|
|
249
230
|
this[kOffset] += 1;
|
|
250
231
|
return Number(value);
|
|
251
232
|
}
|
|
252
233
|
|
|
253
234
|
getShort() {
|
|
254
|
-
const value =
|
|
235
|
+
const value = this[kBuffer].readInt16LE(this[kOffset]);
|
|
255
236
|
this[kOffset] += 2;
|
|
256
237
|
return Number(value);
|
|
257
238
|
}
|
|
258
239
|
|
|
259
240
|
getInt() {
|
|
260
|
-
const value =
|
|
241
|
+
const value = this[kBuffer].readInt32LE(this[kOffset]);
|
|
261
242
|
this[kOffset] += 4;
|
|
262
243
|
return Number(value);
|
|
263
244
|
}
|
|
264
245
|
|
|
265
246
|
getLong() {
|
|
266
|
-
const value =
|
|
247
|
+
const value = this[kBuffer].readBigInt64LE(this[kOffset]);
|
|
267
248
|
this[kOffset] += 8;
|
|
268
249
|
return BigInt(value);
|
|
269
250
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmc.js",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Events driven and chained Taobao Message Channel(TMC) for NodeJS",
|
|
5
5
|
"author": "James ZHANG",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"json-bigint": "^1.0.0",
|
|
33
|
-
"ws": "^8.
|
|
33
|
+
"ws": "^5.2.2 || ^6.2.1 || ^7.4.5 || ^8.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@babel/eslint-parser": "^7.19.1",
|
|
37
|
-
"@types/node": "
|
|
37
|
+
"@types/node": ">=11.9.0",
|
|
38
38
|
"eslint": "^8.25.0",
|
|
39
39
|
"eslint-config-airbnb-base": "^15.0.0"
|
|
40
40
|
},
|
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;
|