@shipload/sdk 1.0.0-next.0 → 1.0.0-next.2
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/lib/shipload.d.ts +19 -1
- package/lib/shipload.js +80 -2
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +80 -2
- package/lib/shipload.m.js.map +1 -1
- package/package.json +7 -2
- package/src/subscriptions/connection.ts +50 -2
- package/src/subscriptions/manager.ts +44 -1
package/lib/shipload.m.js
CHANGED
|
@@ -9095,9 +9095,15 @@ class WebSocketConnection {
|
|
|
9095
9095
|
this._state = 'disconnected';
|
|
9096
9096
|
this.shouldReconnect = true;
|
|
9097
9097
|
this.sendQueue = [];
|
|
9098
|
+
this.pingTimer = null;
|
|
9099
|
+
this.staleTimer = null;
|
|
9098
9100
|
this.url = options.url;
|
|
9099
9101
|
this.onMessage = options.onMessage;
|
|
9100
9102
|
this.onStateChange = options.onStateChange;
|
|
9103
|
+
this.minReconnectDelay =
|
|
9104
|
+
options.minReconnectDelay ?? WebSocketConnection.DEFAULT_MIN_RECONNECT_DELAY;
|
|
9105
|
+
this.pingIntervalMs = options.pingIntervalMs ?? WebSocketConnection.DEFAULT_PING_INTERVAL_MS;
|
|
9106
|
+
this.pongTimeoutMs = options.pongTimeoutMs ?? WebSocketConnection.DEFAULT_PONG_TIMEOUT_MS;
|
|
9101
9107
|
}
|
|
9102
9108
|
get state() {
|
|
9103
9109
|
return this._state;
|
|
@@ -9126,8 +9132,10 @@ class WebSocketConnection {
|
|
|
9126
9132
|
this.ws.readyState === WebSocket.OPEN) {
|
|
9127
9133
|
this.ws.send(this.sendQueue.shift());
|
|
9128
9134
|
}
|
|
9135
|
+
this.startHeartbeat();
|
|
9129
9136
|
};
|
|
9130
9137
|
this.ws.onmessage = (event) => {
|
|
9138
|
+
this.resetStaleTimer();
|
|
9131
9139
|
try {
|
|
9132
9140
|
const message = JSON.parse(event.data);
|
|
9133
9141
|
this.onMessage(message);
|
|
@@ -9137,6 +9145,7 @@ class WebSocketConnection {
|
|
|
9137
9145
|
}
|
|
9138
9146
|
};
|
|
9139
9147
|
this.ws.onclose = () => {
|
|
9148
|
+
this.stopHeartbeat();
|
|
9140
9149
|
this.ws = null;
|
|
9141
9150
|
this.sendQueue.length = 0;
|
|
9142
9151
|
if (this.shouldReconnect) {
|
|
@@ -9161,7 +9170,7 @@ class WebSocketConnection {
|
|
|
9161
9170
|
if (this.reconnectTimeout) {
|
|
9162
9171
|
return;
|
|
9163
9172
|
}
|
|
9164
|
-
const delay = Math.min(
|
|
9173
|
+
const delay = Math.min(this.minReconnectDelay *
|
|
9165
9174
|
WebSocketConnection.RECONNECT_MULTIPLIER ** this.reconnectAttempts, WebSocketConnection.MAX_RECONNECT_DELAY);
|
|
9166
9175
|
debug(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts + 1})`);
|
|
9167
9176
|
this.reconnectTimeout = setTimeout(() => {
|
|
@@ -9176,6 +9185,7 @@ class WebSocketConnection {
|
|
|
9176
9185
|
clearTimeout(this.reconnectTimeout);
|
|
9177
9186
|
this.reconnectTimeout = null;
|
|
9178
9187
|
}
|
|
9188
|
+
this.stopHeartbeat();
|
|
9179
9189
|
if (this.ws) {
|
|
9180
9190
|
this.ws.close();
|
|
9181
9191
|
this.ws = null;
|
|
@@ -9183,6 +9193,34 @@ class WebSocketConnection {
|
|
|
9183
9193
|
this.sendQueue.length = 0;
|
|
9184
9194
|
this.setState('disconnected');
|
|
9185
9195
|
}
|
|
9196
|
+
startHeartbeat() {
|
|
9197
|
+
this.stopHeartbeat();
|
|
9198
|
+
this.resetStaleTimer();
|
|
9199
|
+
this.pingTimer = setInterval(() => {
|
|
9200
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
9201
|
+
this.ws.send(JSON.stringify({ type: 'ping' }));
|
|
9202
|
+
}
|
|
9203
|
+
}, this.pingIntervalMs);
|
|
9204
|
+
}
|
|
9205
|
+
stopHeartbeat() {
|
|
9206
|
+
if (this.pingTimer) {
|
|
9207
|
+
clearInterval(this.pingTimer);
|
|
9208
|
+
this.pingTimer = null;
|
|
9209
|
+
}
|
|
9210
|
+
if (this.staleTimer) {
|
|
9211
|
+
clearTimeout(this.staleTimer);
|
|
9212
|
+
this.staleTimer = null;
|
|
9213
|
+
}
|
|
9214
|
+
}
|
|
9215
|
+
resetStaleTimer() {
|
|
9216
|
+
if (this.staleTimer)
|
|
9217
|
+
clearTimeout(this.staleTimer);
|
|
9218
|
+
this.staleTimer = setTimeout(() => {
|
|
9219
|
+
debug('No frames within ping interval + pong timeout — forcing reconnect');
|
|
9220
|
+
if (this.ws)
|
|
9221
|
+
this.ws.close();
|
|
9222
|
+
}, this.pingIntervalMs + this.pongTimeoutMs);
|
|
9223
|
+
}
|
|
9186
9224
|
close() {
|
|
9187
9225
|
this.disconnect();
|
|
9188
9226
|
}
|
|
@@ -9198,9 +9236,11 @@ class WebSocketConnection {
|
|
|
9198
9236
|
return this._state === 'connected';
|
|
9199
9237
|
}
|
|
9200
9238
|
}
|
|
9201
|
-
WebSocketConnection.
|
|
9239
|
+
WebSocketConnection.DEFAULT_MIN_RECONNECT_DELAY = 1000;
|
|
9202
9240
|
WebSocketConnection.MAX_RECONNECT_DELAY = 30000;
|
|
9203
9241
|
WebSocketConnection.RECONNECT_MULTIPLIER = 2;
|
|
9242
|
+
WebSocketConnection.DEFAULT_PING_INTERVAL_MS = 25000;
|
|
9243
|
+
WebSocketConnection.DEFAULT_PONG_TIMEOUT_MS = 10000;
|
|
9204
9244
|
|
|
9205
9245
|
function mapEntity(ei) {
|
|
9206
9246
|
if (ei.type.equals('ship'))
|
|
@@ -9229,9 +9269,14 @@ class SubscriptionsManager {
|
|
|
9229
9269
|
this.entitySubs = new Map();
|
|
9230
9270
|
this.boundsSubs = new Map();
|
|
9231
9271
|
this.subCounter = 0;
|
|
9272
|
+
this.hasConnected = false;
|
|
9232
9273
|
this.conn = new WebSocketConnection({
|
|
9233
9274
|
url: opts.url,
|
|
9234
9275
|
onMessage: (m) => this.onMessage(m),
|
|
9276
|
+
onStateChange: (s) => this.onStateChange(s),
|
|
9277
|
+
minReconnectDelay: opts.minReconnectDelay,
|
|
9278
|
+
pingIntervalMs: opts.pingIntervalMs,
|
|
9279
|
+
pongTimeoutMs: opts.pongTimeoutMs,
|
|
9235
9280
|
});
|
|
9236
9281
|
this.conn.connect();
|
|
9237
9282
|
}
|
|
@@ -9288,6 +9333,9 @@ class SubscriptionsManager {
|
|
|
9288
9333
|
current: new Map(),
|
|
9289
9334
|
};
|
|
9290
9335
|
this.boundsSubs.set(subId, {
|
|
9336
|
+
bounds,
|
|
9337
|
+
owner: handlers.owner,
|
|
9338
|
+
prioritizeOwner: handlers.prioritizeOwner,
|
|
9291
9339
|
onSnapshot: handlers.onSnapshot,
|
|
9292
9340
|
onUpdate: handlers.onUpdate,
|
|
9293
9341
|
onBoundsDelta: handlers.onBoundsDelta,
|
|
@@ -9301,9 +9349,39 @@ class SubscriptionsManager {
|
|
|
9301
9349
|
this.sendMessage({ type: 'unsubscribe', sub_id: subId });
|
|
9302
9350
|
}
|
|
9303
9351
|
updateBounds(subId, bounds) {
|
|
9352
|
+
const entry = this.boundsSubs.get(subId);
|
|
9353
|
+
if (entry)
|
|
9354
|
+
entry.bounds = bounds;
|
|
9304
9355
|
const msg = { type: 'update_bounds', sub_id: subId, bounds };
|
|
9305
9356
|
this.sendMessage(msg);
|
|
9306
9357
|
}
|
|
9358
|
+
onStateChange(state) {
|
|
9359
|
+
if (state !== 'connected')
|
|
9360
|
+
return;
|
|
9361
|
+
if (!this.hasConnected) {
|
|
9362
|
+
this.hasConnected = true;
|
|
9363
|
+
return;
|
|
9364
|
+
}
|
|
9365
|
+
for (const [subId, entry] of this.entitySubs) {
|
|
9366
|
+
const msg = {
|
|
9367
|
+
type: 'subscribe_entity',
|
|
9368
|
+
sub_id: subId,
|
|
9369
|
+
entity_type: entry.type,
|
|
9370
|
+
entity_id: entry.id,
|
|
9371
|
+
};
|
|
9372
|
+
this.sendMessage(msg);
|
|
9373
|
+
}
|
|
9374
|
+
for (const [subId, entry] of this.boundsSubs) {
|
|
9375
|
+
const msg = {
|
|
9376
|
+
type: 'subscribe',
|
|
9377
|
+
sub_id: subId,
|
|
9378
|
+
bounds: entry.bounds,
|
|
9379
|
+
owner: entry.owner,
|
|
9380
|
+
prioritize_owner: entry.prioritizeOwner,
|
|
9381
|
+
};
|
|
9382
|
+
this.sendMessage(msg);
|
|
9383
|
+
}
|
|
9384
|
+
}
|
|
9307
9385
|
onMessage(msg) {
|
|
9308
9386
|
switch (msg.type) {
|
|
9309
9387
|
case 'snapshot':
|