@butlerbot/sdk 0.0.24 → 0.0.25
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/link/link.d.ts +11 -0
- package/dist/link/link.js +42 -10
- package/package.json +1 -1
package/dist/link/link.d.ts
CHANGED
|
@@ -88,6 +88,8 @@ export declare class Link {
|
|
|
88
88
|
private readonly pending;
|
|
89
89
|
private readonly calls;
|
|
90
90
|
private socket;
|
|
91
|
+
/** Every socket this link has opened and not yet closed, by generation. */
|
|
92
|
+
private readonly sockets;
|
|
91
93
|
private frameCounter;
|
|
92
94
|
private currentState;
|
|
93
95
|
private identity?;
|
|
@@ -162,6 +164,15 @@ export declare class Link {
|
|
|
162
164
|
* means the real event, whenever it turns up, is ignored.
|
|
163
165
|
*/
|
|
164
166
|
private dropSocket;
|
|
167
|
+
/**
|
|
168
|
+
* Closes a socket this end has stopped using, whatever generation it belongs to.
|
|
169
|
+
*
|
|
170
|
+
* Every path that walks away from a socket goes through here. Forgetting one is not harmless:
|
|
171
|
+
* the server has no way to tell an abandoned connection from a live one — it has said hello,
|
|
172
|
+
* registered its tools and claimed its link id — so it keeps it, keeps serving from it, and
|
|
173
|
+
* hands the link id back and forth between it and its replacements.
|
|
174
|
+
*/
|
|
175
|
+
private abandon;
|
|
165
176
|
private onClose;
|
|
166
177
|
/** Retries when it is allowed to, and tells everyone waiting when it is not. */
|
|
167
178
|
private retryOrGiveUp;
|
package/dist/link/link.js
CHANGED
|
@@ -28,6 +28,8 @@ class Link {
|
|
|
28
28
|
this.pending = new Map();
|
|
29
29
|
this.calls = new Map();
|
|
30
30
|
this.socket = null;
|
|
31
|
+
/** Every socket this link has opened and not yet closed, by generation. */
|
|
32
|
+
this.sockets = new Map();
|
|
31
33
|
this.frameCounter = 0;
|
|
32
34
|
this.currentState = "idle";
|
|
33
35
|
/**
|
|
@@ -171,7 +173,10 @@ class Link {
|
|
|
171
173
|
this.failPending(closed);
|
|
172
174
|
this.settleWaiters(this.attemptWaiters, closed);
|
|
173
175
|
this.settleWaiters(this.openWaiters, closed);
|
|
174
|
-
|
|
176
|
+
// Every socket, not just the current one: a link that is being closed for good must not
|
|
177
|
+
// leave anything of itself attached to the server.
|
|
178
|
+
for (const generation of [...this.sockets.keys()])
|
|
179
|
+
this.abandon(generation, reason);
|
|
175
180
|
this.socket = null;
|
|
176
181
|
}
|
|
177
182
|
/**
|
|
@@ -194,14 +199,23 @@ class Link {
|
|
|
194
199
|
const { url, protocols } = (0, socket_1.buildHandshake)(this.options.serverUrl, "link", this.options.apiKey);
|
|
195
200
|
this.debug(`connecting to ${url.replace(/api_key=[^&]+/, "api_key=***")}`);
|
|
196
201
|
try {
|
|
197
|
-
|
|
202
|
+
const socket = this.options.socketFactory(url, protocols, {
|
|
198
203
|
onOpen: () => this.onOpen(generation),
|
|
199
204
|
onMessage: (data) => { if (generation === this.generation)
|
|
200
205
|
this.onMessage(data); },
|
|
201
|
-
onClose: (code, reason) =>
|
|
206
|
+
onClose: (code, reason) => {
|
|
207
|
+
this.sockets.delete(generation);
|
|
208
|
+
this.onClose(generation, code, reason);
|
|
209
|
+
},
|
|
202
210
|
onError: (error) => { if (generation === this.generation)
|
|
203
211
|
this.emitter.emit("error", asError(error)); },
|
|
204
212
|
});
|
|
213
|
+
// Held by generation, not only as `this.socket`, so a socket that stops being the
|
|
214
|
+
// current one can still be closed. One that is merely forgotten stays open at the far
|
|
215
|
+
// end: it has already said hello and registered its tools, so the server goes on serving
|
|
216
|
+
// it, and it answers websocket pings forever because the network stack does that for it.
|
|
217
|
+
this.sockets.set(generation, socket);
|
|
218
|
+
this.socket = socket;
|
|
205
219
|
}
|
|
206
220
|
catch (error) {
|
|
207
221
|
// A factory that throws never produces a close event, so this failure is
|
|
@@ -230,9 +244,11 @@ class Link {
|
|
|
230
244
|
const identity = { connectionId: welcome.connectionId, scope: welcome.scope };
|
|
231
245
|
this.identity = identity;
|
|
232
246
|
await this.registerAll();
|
|
233
|
-
// Registration is several round trips; the socket may have gone during them.
|
|
247
|
+
// Registration is several round trips; the socket may have gone during them. It is fully
|
|
248
|
+
// registered at the server by now, so abandoning it quietly would leave it serving tools
|
|
249
|
+
// nothing here will ever answer.
|
|
234
250
|
if (generation !== this.generation)
|
|
235
|
-
return;
|
|
251
|
+
return this.abandon(generation, "superseded during handshake");
|
|
236
252
|
this.attempting = false;
|
|
237
253
|
this.currentState = "open";
|
|
238
254
|
this.reconnectAttempt = 0;
|
|
@@ -243,7 +259,7 @@ class Link {
|
|
|
243
259
|
this.emitter.emit("connect", identity);
|
|
244
260
|
}).catch((error) => {
|
|
245
261
|
if (generation !== this.generation)
|
|
246
|
-
return;
|
|
262
|
+
return this.abandon(generation, "superseded during handshake");
|
|
247
263
|
this.emitter.emit("error", error);
|
|
248
264
|
this.settleWaiters(this.attemptWaiters, error);
|
|
249
265
|
// Only the server saying "do not come back" stops us: a rejected claim or an
|
|
@@ -266,12 +282,28 @@ class Link {
|
|
|
266
282
|
dropSocket(generation, code, reason) {
|
|
267
283
|
if (generation !== this.generation)
|
|
268
284
|
return;
|
|
269
|
-
const socket = this.socket;
|
|
270
285
|
this.onClose(generation, code, reason);
|
|
286
|
+
// 1006 is reserved and rejected by browsers; anything else we raise is a valid
|
|
287
|
+
// application code.
|
|
288
|
+
this.abandon(generation, reason, code === 1006 ? 1000 : code);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Closes a socket this end has stopped using, whatever generation it belongs to.
|
|
292
|
+
*
|
|
293
|
+
* Every path that walks away from a socket goes through here. Forgetting one is not harmless:
|
|
294
|
+
* the server has no way to tell an abandoned connection from a live one — it has said hello,
|
|
295
|
+
* registered its tools and claimed its link id — so it keeps it, keeps serving from it, and
|
|
296
|
+
* hands the link id back and forth between it and its replacements.
|
|
297
|
+
*/
|
|
298
|
+
abandon(generation, reason, code = 1000) {
|
|
299
|
+
const socket = this.sockets.get(generation);
|
|
300
|
+
if (!socket)
|
|
301
|
+
return;
|
|
302
|
+
this.sockets.delete(generation);
|
|
303
|
+
if (this.socket === socket)
|
|
304
|
+
this.socket = null;
|
|
271
305
|
try {
|
|
272
|
-
|
|
273
|
-
// application code.
|
|
274
|
-
socket?.close(code === 1006 ? 1000 : code, reason);
|
|
306
|
+
socket.close(code, reason);
|
|
275
307
|
}
|
|
276
308
|
catch {
|
|
277
309
|
// Already gone, which is the outcome we wanted anyway.
|