dingtalk-stream 2.1.5 → 2.1.6-beta.1
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/client.cjs +75 -19
- package/dist/client.d.ts +7 -1
- package/dist/client.mjs +75 -19
- package/package.json +6 -2
package/dist/client.cjs
CHANGED
|
@@ -27,9 +27,13 @@ class DWClient extends EventEmitter {
|
|
|
27
27
|
registered = false;
|
|
28
28
|
reconnecting = false;
|
|
29
29
|
userDisconnect = false;
|
|
30
|
-
|
|
30
|
+
reconnectBaseInterval = 1e3;
|
|
31
|
+
reconnectMaxInterval = 6e4;
|
|
32
|
+
reconnectAttempts = 0;
|
|
31
33
|
heartbeat_interval = 8e3;
|
|
32
34
|
heartbeatIntervallId;
|
|
35
|
+
reconnectTimerId;
|
|
36
|
+
isConnecting = false;
|
|
33
37
|
sslopts = { rejectUnauthorized: true };
|
|
34
38
|
config;
|
|
35
39
|
socket;
|
|
@@ -128,9 +132,39 @@ class DWClient extends EventEmitter {
|
|
|
128
132
|
throw new Error("build: get endpoint failed");
|
|
129
133
|
}
|
|
130
134
|
}
|
|
135
|
+
cleanup() {
|
|
136
|
+
if (this.heartbeatIntervallId !== void 0) {
|
|
137
|
+
clearInterval(this.heartbeatIntervallId);
|
|
138
|
+
this.heartbeatIntervallId = void 0;
|
|
139
|
+
}
|
|
140
|
+
if (this.socket) {
|
|
141
|
+
this.socket.removeAllListeners();
|
|
142
|
+
if (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING) {
|
|
143
|
+
this.socket.terminate();
|
|
144
|
+
}
|
|
145
|
+
this.socket = void 0;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
scheduleReconnect() {
|
|
149
|
+
if (!this.config.autoReconnect || this.userDisconnect || this.isConnecting) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const delay = Math.min(
|
|
153
|
+
this.reconnectBaseInterval * Math.pow(2, this.reconnectAttempts) + Math.random() * 1e3,
|
|
154
|
+
this.reconnectMaxInterval
|
|
155
|
+
);
|
|
156
|
+
this.reconnecting = true;
|
|
157
|
+
this.printDebug("Reconnecting in " + (delay / 1e3).toFixed(1) + " seconds... (attempt " + (this.reconnectAttempts + 1) + ")");
|
|
158
|
+
if (this.reconnectTimerId) {
|
|
159
|
+
clearTimeout(this.reconnectTimerId);
|
|
160
|
+
}
|
|
161
|
+
this.reconnectTimerId = setTimeout(() => {
|
|
162
|
+
this.reconnectTimerId = void 0;
|
|
163
|
+
this.connect();
|
|
164
|
+
}, delay);
|
|
165
|
+
}
|
|
131
166
|
_connect() {
|
|
132
167
|
return new Promise((resolve, reject) => {
|
|
133
|
-
this.userDisconnect = false;
|
|
134
168
|
this.printDebug("Connecting to dingtalk websocket @ " + this.dw_url);
|
|
135
169
|
try {
|
|
136
170
|
this.socket = new WebSocket(this.dw_url, this.sslopts);
|
|
@@ -140,8 +174,10 @@ class DWClient extends EventEmitter {
|
|
|
140
174
|
reject(err);
|
|
141
175
|
return;
|
|
142
176
|
}
|
|
177
|
+
let settled = false;
|
|
143
178
|
this.socket.on("open", () => {
|
|
144
179
|
this.connected = true;
|
|
180
|
+
this.reconnectAttempts = 0;
|
|
145
181
|
console.info("[" + (/* @__PURE__ */ new Date()).toISOString() + "] connect success");
|
|
146
182
|
if (this.config.keepAlive) {
|
|
147
183
|
this.isAlive = true;
|
|
@@ -157,6 +193,7 @@ class DWClient extends EventEmitter {
|
|
|
157
193
|
(_b = this.socket) == null ? void 0 : _b.ping("", true);
|
|
158
194
|
}, this.heartbeat_interval);
|
|
159
195
|
}
|
|
196
|
+
settled = true;
|
|
160
197
|
resolve();
|
|
161
198
|
});
|
|
162
199
|
this.socket.on("pong", () => {
|
|
@@ -165,16 +202,16 @@ class DWClient extends EventEmitter {
|
|
|
165
202
|
this.socket.on("message", (data) => {
|
|
166
203
|
this.onDownStream(data);
|
|
167
204
|
});
|
|
168
|
-
this.socket.on("close", (
|
|
205
|
+
this.socket.on("close", () => {
|
|
169
206
|
this.printDebug("Socket closed");
|
|
170
207
|
this.connected = false;
|
|
171
208
|
this.registered = false;
|
|
172
|
-
if (this.
|
|
173
|
-
this.
|
|
174
|
-
this.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
209
|
+
if (this.heartbeatIntervallId !== void 0) {
|
|
210
|
+
clearInterval(this.heartbeatIntervallId);
|
|
211
|
+
this.heartbeatIntervallId = void 0;
|
|
212
|
+
}
|
|
213
|
+
if (settled) {
|
|
214
|
+
this.scheduleReconnect();
|
|
178
215
|
}
|
|
179
216
|
});
|
|
180
217
|
this.socket.on("error", (err) => {
|
|
@@ -182,31 +219,50 @@ class DWClient extends EventEmitter {
|
|
|
182
219
|
this.printDebug("SOCKET ERROR");
|
|
183
220
|
console.warn("ERROR", err);
|
|
184
221
|
(_a = this.socket) == null ? void 0 : _a.terminate();
|
|
185
|
-
|
|
222
|
+
if (!settled) {
|
|
223
|
+
settled = true;
|
|
224
|
+
reject(err);
|
|
225
|
+
}
|
|
186
226
|
});
|
|
187
227
|
});
|
|
188
228
|
}
|
|
189
229
|
async connect() {
|
|
230
|
+
if (this.isConnecting) {
|
|
231
|
+
this.printDebug("connect() already in progress, skipping");
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
this.userDisconnect = false;
|
|
235
|
+
this.isConnecting = true;
|
|
190
236
|
try {
|
|
237
|
+
this.cleanup();
|
|
191
238
|
await this.getEndpoint();
|
|
239
|
+
if (this.userDisconnect)
|
|
240
|
+
return;
|
|
192
241
|
await this._connect();
|
|
193
242
|
} catch (err) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
243
|
+
this.printDebug("Connect failed: " + (err instanceof Error ? err.message : String(err)));
|
|
244
|
+
if (!this.userDisconnect) {
|
|
245
|
+
this.reconnectAttempts++;
|
|
246
|
+
this.isConnecting = false;
|
|
247
|
+
this.scheduleReconnect();
|
|
199
248
|
}
|
|
249
|
+
return;
|
|
250
|
+
} finally {
|
|
251
|
+
this.isConnecting = false;
|
|
200
252
|
}
|
|
201
253
|
}
|
|
202
254
|
disconnect() {
|
|
203
|
-
var _a;
|
|
204
255
|
console.info("Disconnecting.");
|
|
205
256
|
this.userDisconnect = true;
|
|
206
|
-
if (this.
|
|
207
|
-
|
|
257
|
+
if (this.reconnectTimerId) {
|
|
258
|
+
clearTimeout(this.reconnectTimerId);
|
|
259
|
+
this.reconnectTimerId = void 0;
|
|
208
260
|
}
|
|
209
|
-
|
|
261
|
+
this.reconnecting = false;
|
|
262
|
+
this.reconnectAttempts = 0;
|
|
263
|
+
this.cleanup();
|
|
264
|
+
this.connected = false;
|
|
265
|
+
this.registered = false;
|
|
210
266
|
}
|
|
211
267
|
heartbeat() {
|
|
212
268
|
this.isAlive = true;
|
package/dist/client.d.ts
CHANGED
|
@@ -50,9 +50,13 @@ declare class DWClient extends EventEmitter {
|
|
|
50
50
|
registered: boolean;
|
|
51
51
|
reconnecting: boolean;
|
|
52
52
|
private userDisconnect;
|
|
53
|
-
private
|
|
53
|
+
private reconnectBaseInterval;
|
|
54
|
+
private reconnectMaxInterval;
|
|
55
|
+
private reconnectAttempts;
|
|
54
56
|
private heartbeat_interval;
|
|
55
57
|
private heartbeatIntervallId?;
|
|
58
|
+
private reconnectTimerId?;
|
|
59
|
+
private isConnecting;
|
|
56
60
|
private sslopts;
|
|
57
61
|
readonly config: DWClientConfig;
|
|
58
62
|
private socket?;
|
|
@@ -85,6 +89,8 @@ declare class DWClient extends EventEmitter {
|
|
|
85
89
|
registerCallbackListener(eventId: string, callback: (v: DWClientDownStream) => void): this;
|
|
86
90
|
getAccessToken(): Promise<any>;
|
|
87
91
|
getEndpoint(): Promise<this>;
|
|
92
|
+
private cleanup;
|
|
93
|
+
private scheduleReconnect;
|
|
88
94
|
_connect(): Promise<void>;
|
|
89
95
|
connect(): Promise<void>;
|
|
90
96
|
disconnect(): void;
|
package/dist/client.mjs
CHANGED
|
@@ -25,9 +25,13 @@ class DWClient extends EventEmitter {
|
|
|
25
25
|
registered = false;
|
|
26
26
|
reconnecting = false;
|
|
27
27
|
userDisconnect = false;
|
|
28
|
-
|
|
28
|
+
reconnectBaseInterval = 1e3;
|
|
29
|
+
reconnectMaxInterval = 6e4;
|
|
30
|
+
reconnectAttempts = 0;
|
|
29
31
|
heartbeat_interval = 8e3;
|
|
30
32
|
heartbeatIntervallId;
|
|
33
|
+
reconnectTimerId;
|
|
34
|
+
isConnecting = false;
|
|
31
35
|
sslopts = { rejectUnauthorized: true };
|
|
32
36
|
config;
|
|
33
37
|
socket;
|
|
@@ -126,9 +130,39 @@ class DWClient extends EventEmitter {
|
|
|
126
130
|
throw new Error("build: get endpoint failed");
|
|
127
131
|
}
|
|
128
132
|
}
|
|
133
|
+
cleanup() {
|
|
134
|
+
if (this.heartbeatIntervallId !== void 0) {
|
|
135
|
+
clearInterval(this.heartbeatIntervallId);
|
|
136
|
+
this.heartbeatIntervallId = void 0;
|
|
137
|
+
}
|
|
138
|
+
if (this.socket) {
|
|
139
|
+
this.socket.removeAllListeners();
|
|
140
|
+
if (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING) {
|
|
141
|
+
this.socket.terminate();
|
|
142
|
+
}
|
|
143
|
+
this.socket = void 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
scheduleReconnect() {
|
|
147
|
+
if (!this.config.autoReconnect || this.userDisconnect || this.isConnecting) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const delay = Math.min(
|
|
151
|
+
this.reconnectBaseInterval * Math.pow(2, this.reconnectAttempts) + Math.random() * 1e3,
|
|
152
|
+
this.reconnectMaxInterval
|
|
153
|
+
);
|
|
154
|
+
this.reconnecting = true;
|
|
155
|
+
this.printDebug("Reconnecting in " + (delay / 1e3).toFixed(1) + " seconds... (attempt " + (this.reconnectAttempts + 1) + ")");
|
|
156
|
+
if (this.reconnectTimerId) {
|
|
157
|
+
clearTimeout(this.reconnectTimerId);
|
|
158
|
+
}
|
|
159
|
+
this.reconnectTimerId = setTimeout(() => {
|
|
160
|
+
this.reconnectTimerId = void 0;
|
|
161
|
+
this.connect();
|
|
162
|
+
}, delay);
|
|
163
|
+
}
|
|
129
164
|
_connect() {
|
|
130
165
|
return new Promise((resolve, reject) => {
|
|
131
|
-
this.userDisconnect = false;
|
|
132
166
|
this.printDebug("Connecting to dingtalk websocket @ " + this.dw_url);
|
|
133
167
|
try {
|
|
134
168
|
this.socket = new WebSocket(this.dw_url, this.sslopts);
|
|
@@ -138,8 +172,10 @@ class DWClient extends EventEmitter {
|
|
|
138
172
|
reject(err);
|
|
139
173
|
return;
|
|
140
174
|
}
|
|
175
|
+
let settled = false;
|
|
141
176
|
this.socket.on("open", () => {
|
|
142
177
|
this.connected = true;
|
|
178
|
+
this.reconnectAttempts = 0;
|
|
143
179
|
console.info("[" + (/* @__PURE__ */ new Date()).toISOString() + "] connect success");
|
|
144
180
|
if (this.config.keepAlive) {
|
|
145
181
|
this.isAlive = true;
|
|
@@ -155,6 +191,7 @@ class DWClient extends EventEmitter {
|
|
|
155
191
|
(_b = this.socket) == null ? void 0 : _b.ping("", true);
|
|
156
192
|
}, this.heartbeat_interval);
|
|
157
193
|
}
|
|
194
|
+
settled = true;
|
|
158
195
|
resolve();
|
|
159
196
|
});
|
|
160
197
|
this.socket.on("pong", () => {
|
|
@@ -163,16 +200,16 @@ class DWClient extends EventEmitter {
|
|
|
163
200
|
this.socket.on("message", (data) => {
|
|
164
201
|
this.onDownStream(data);
|
|
165
202
|
});
|
|
166
|
-
this.socket.on("close", (
|
|
203
|
+
this.socket.on("close", () => {
|
|
167
204
|
this.printDebug("Socket closed");
|
|
168
205
|
this.connected = false;
|
|
169
206
|
this.registered = false;
|
|
170
|
-
if (this.
|
|
171
|
-
this.
|
|
172
|
-
this.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
207
|
+
if (this.heartbeatIntervallId !== void 0) {
|
|
208
|
+
clearInterval(this.heartbeatIntervallId);
|
|
209
|
+
this.heartbeatIntervallId = void 0;
|
|
210
|
+
}
|
|
211
|
+
if (settled) {
|
|
212
|
+
this.scheduleReconnect();
|
|
176
213
|
}
|
|
177
214
|
});
|
|
178
215
|
this.socket.on("error", (err) => {
|
|
@@ -180,31 +217,50 @@ class DWClient extends EventEmitter {
|
|
|
180
217
|
this.printDebug("SOCKET ERROR");
|
|
181
218
|
console.warn("ERROR", err);
|
|
182
219
|
(_a = this.socket) == null ? void 0 : _a.terminate();
|
|
183
|
-
|
|
220
|
+
if (!settled) {
|
|
221
|
+
settled = true;
|
|
222
|
+
reject(err);
|
|
223
|
+
}
|
|
184
224
|
});
|
|
185
225
|
});
|
|
186
226
|
}
|
|
187
227
|
async connect() {
|
|
228
|
+
if (this.isConnecting) {
|
|
229
|
+
this.printDebug("connect() already in progress, skipping");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
this.userDisconnect = false;
|
|
233
|
+
this.isConnecting = true;
|
|
188
234
|
try {
|
|
235
|
+
this.cleanup();
|
|
189
236
|
await this.getEndpoint();
|
|
237
|
+
if (this.userDisconnect)
|
|
238
|
+
return;
|
|
190
239
|
await this._connect();
|
|
191
240
|
} catch (err) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
241
|
+
this.printDebug("Connect failed: " + (err instanceof Error ? err.message : String(err)));
|
|
242
|
+
if (!this.userDisconnect) {
|
|
243
|
+
this.reconnectAttempts++;
|
|
244
|
+
this.isConnecting = false;
|
|
245
|
+
this.scheduleReconnect();
|
|
197
246
|
}
|
|
247
|
+
return;
|
|
248
|
+
} finally {
|
|
249
|
+
this.isConnecting = false;
|
|
198
250
|
}
|
|
199
251
|
}
|
|
200
252
|
disconnect() {
|
|
201
|
-
var _a;
|
|
202
253
|
console.info("Disconnecting.");
|
|
203
254
|
this.userDisconnect = true;
|
|
204
|
-
if (this.
|
|
205
|
-
|
|
255
|
+
if (this.reconnectTimerId) {
|
|
256
|
+
clearTimeout(this.reconnectTimerId);
|
|
257
|
+
this.reconnectTimerId = void 0;
|
|
206
258
|
}
|
|
207
|
-
|
|
259
|
+
this.reconnecting = false;
|
|
260
|
+
this.reconnectAttempts = 0;
|
|
261
|
+
this.cleanup();
|
|
262
|
+
this.connected = false;
|
|
263
|
+
this.registered = false;
|
|
208
264
|
}
|
|
209
265
|
heartbeat() {
|
|
210
266
|
this.isAlive = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dingtalk-stream",
|
|
3
|
-
"version": "v2.1.
|
|
3
|
+
"version": "v2.1.6-beta.1",
|
|
4
4
|
"description": "Nodejs SDK for DingTalk Stream Mode API, Compared with the webhook mode, it is easier to access the DingTalk",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -37,7 +37,10 @@
|
|
|
37
37
|
"build": "rimraf dist && rollup -c",
|
|
38
38
|
"dev": "rollup -c --watch --watch.include 'src/**' -m inline",
|
|
39
39
|
"prepublishOnly": "pnpm build",
|
|
40
|
-
"typecheck": "tsc --noEmit"
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "pnpm build && node test/reconnect-mock.mjs",
|
|
42
|
+
"test:prod": "pnpm build && node test/reconnect-prod.mjs",
|
|
43
|
+
"test:demo": "node test/reconnect-storm-demo.mjs"
|
|
41
44
|
},
|
|
42
45
|
"repository": {
|
|
43
46
|
"type": "git",
|
|
@@ -72,6 +75,7 @@
|
|
|
72
75
|
"@types/debug": "^4.1.8",
|
|
73
76
|
"@types/node": ">=16",
|
|
74
77
|
"@types/ws": "^8.5.5",
|
|
78
|
+
"nock": "^14.0.11",
|
|
75
79
|
"rimraf": "^5.0.1",
|
|
76
80
|
"rollup": "^3.28.0",
|
|
77
81
|
"rollup-plugin-dts": "^6.0.0",
|