@spacemolt/lib 12.2.0 → 12.2.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/dist/account.d.ts +18 -0
- package/dist/index.js +136 -5
- package/package.json +1 -1
package/dist/account.d.ts
CHANGED
|
@@ -160,6 +160,8 @@ export declare class Account {
|
|
|
160
160
|
private subscribedMarketBaseId;
|
|
161
161
|
private observationSubscribedState;
|
|
162
162
|
private observationActiveScanState;
|
|
163
|
+
/** Invalidates an in-flight reconciliation whenever a newer cache update arrives. */
|
|
164
|
+
private stateRevision;
|
|
163
165
|
private subscribedObservationPoiId;
|
|
164
166
|
private readonly reconnectedListeners;
|
|
165
167
|
private readonly reconnectingListeners;
|
|
@@ -374,6 +376,22 @@ export declare class Account {
|
|
|
374
376
|
* after every state update that could carry a `location` change.
|
|
375
377
|
*/
|
|
376
378
|
private checkSubscriptionsAgainstLocation;
|
|
379
|
+
/**
|
|
380
|
+
* Fleet followers do not submit the movement mutation themselves, so their
|
|
381
|
+
* arrival is delivered as a plain `ok` push rather than an `action_result`
|
|
382
|
+
* carrying a state delta. Bridge the location data present on those pushes
|
|
383
|
+
* into the cache. Fleet docking is the one exception: its push only contains
|
|
384
|
+
* the station's display name, not the canonical base ID used by `docked_at`,
|
|
385
|
+
* so refresh the canonical snapshot instead of caching the wrong identifier.
|
|
386
|
+
*/
|
|
387
|
+
private applyFleetMovementPush;
|
|
388
|
+
/**
|
|
389
|
+
* Fleet movement pushes omit follower fuel and skill deltas, while docking
|
|
390
|
+
* omits the canonical base ID. Reconcile those fields from one authoritative
|
|
391
|
+
* snapshot. A newer movement push invalidates an older in-flight query so a
|
|
392
|
+
* delayed dock/arrival snapshot cannot move the cache backwards.
|
|
393
|
+
*/
|
|
394
|
+
private reconcileFleetState;
|
|
377
395
|
/** Close the connection deliberately. Suppresses auto-reconnect. */
|
|
378
396
|
close(): void;
|
|
379
397
|
/** Fired after a successful reconnect + re-auth. Returns an unsubscribe function. */
|
package/dist/index.js
CHANGED
|
@@ -4027,6 +4027,7 @@ class Account {
|
|
|
4027
4027
|
subscribedMarketBaseId;
|
|
4028
4028
|
observationSubscribedState = false;
|
|
4029
4029
|
observationActiveScanState = false;
|
|
4030
|
+
stateRevision = 0;
|
|
4030
4031
|
subscribedObservationPoiId;
|
|
4031
4032
|
reconnectedListeners = new Set;
|
|
4032
4033
|
reconnectingListeners = new Set;
|
|
@@ -4195,6 +4196,8 @@ class Account {
|
|
|
4195
4196
|
const result = await this.commands.spacemolt.get_status();
|
|
4196
4197
|
const snapshot = requireStructuredContent(result, "spacemolt/get_status");
|
|
4197
4198
|
const changed = this.cache.seed(snapshot);
|
|
4199
|
+
if (changed.length)
|
|
4200
|
+
this.stateRevision++;
|
|
4198
4201
|
if (changed.includes("location"))
|
|
4199
4202
|
this.checkSubscriptionsAgainstLocation();
|
|
4200
4203
|
if (changed.length)
|
|
@@ -4227,8 +4230,8 @@ class Account {
|
|
|
4227
4230
|
return this.withRateLimitRetry(async () => {
|
|
4228
4231
|
const id = this.claimRequestId(requestId);
|
|
4229
4232
|
const promise = this.correlator.awaitQuery(id);
|
|
4230
|
-
this.sendFrame(tool, action, payload, id);
|
|
4231
4233
|
try {
|
|
4234
|
+
this.sendFrame(tool, action, payload, id);
|
|
4232
4235
|
return await withTimeout(promise, this.queryTimeoutMs, `No response to ${tool}/${action} within ${this.queryTimeoutMs}ms`);
|
|
4233
4236
|
} catch (err) {
|
|
4234
4237
|
this.correlator.cancel(id);
|
|
@@ -4240,12 +4243,10 @@ class Account {
|
|
|
4240
4243
|
const mutationTimeoutMs = TRANSIT_ACTIONS.has(`${tool}/${action}`) ? this.mutationTimeoutMs : this.fastMutationTimeoutMs;
|
|
4241
4244
|
return this.enqueueMutation(() => this.withRateLimitRetry(() => {
|
|
4242
4245
|
const id = this.claimRequestId(requestId);
|
|
4243
|
-
|
|
4244
|
-
this.sendFrame(tool, action, payload, id);
|
|
4245
|
-
return promise;
|
|
4246
|
+
return this.awaitMutationWithTimeout(id, mutationTimeoutMs, () => this.sendFrame(tool, action, payload, id), onAck);
|
|
4246
4247
|
}));
|
|
4247
4248
|
}
|
|
4248
|
-
awaitMutationWithTimeout(requestId, mutationTimeoutMs, onAck) {
|
|
4249
|
+
awaitMutationWithTimeout(requestId, mutationTimeoutMs, send, onAck) {
|
|
4249
4250
|
return new Promise((resolve, reject) => {
|
|
4250
4251
|
let timer;
|
|
4251
4252
|
const arm = (ms, message) => {
|
|
@@ -4269,6 +4270,14 @@ class Account {
|
|
|
4269
4270
|
clearTimeout(timer);
|
|
4270
4271
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4271
4272
|
});
|
|
4273
|
+
try {
|
|
4274
|
+
send();
|
|
4275
|
+
} catch (err) {
|
|
4276
|
+
if (timer)
|
|
4277
|
+
clearTimeout(timer);
|
|
4278
|
+
this.correlator.cancel(requestId);
|
|
4279
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4280
|
+
}
|
|
4272
4281
|
});
|
|
4273
4282
|
}
|
|
4274
4283
|
send(tool, action, payload, requestId) {
|
|
@@ -4331,6 +4340,8 @@ class Account {
|
|
|
4331
4340
|
nearby_players: nearbyPlayers,
|
|
4332
4341
|
nearby_player_count: view.nearby.size
|
|
4333
4342
|
});
|
|
4343
|
+
if (changed.length)
|
|
4344
|
+
this.stateRevision++;
|
|
4334
4345
|
if (changed.length)
|
|
4335
4346
|
this.emitStateChange(changed);
|
|
4336
4347
|
}
|
|
@@ -4349,6 +4360,123 @@ class Account {
|
|
|
4349
4360
|
this.subscribedObservationPoiId = undefined;
|
|
4350
4361
|
}
|
|
4351
4362
|
}
|
|
4363
|
+
applyFleetMovementPush(frame) {
|
|
4364
|
+
if (frame.type !== "ok" || !isRecord(frame.payload) || typeof frame.payload.action !== "string")
|
|
4365
|
+
return;
|
|
4366
|
+
const payload = frame.payload;
|
|
4367
|
+
let changed = [];
|
|
4368
|
+
let reconcile = false;
|
|
4369
|
+
const currentLocation = this.cache.location;
|
|
4370
|
+
const currentSystem = {
|
|
4371
|
+
connections: currentLocation?.connections,
|
|
4372
|
+
empire: currentLocation?.empire,
|
|
4373
|
+
security_status: currentLocation?.security_status,
|
|
4374
|
+
system_id: currentLocation?.system_id,
|
|
4375
|
+
system_name: currentLocation?.system_name
|
|
4376
|
+
};
|
|
4377
|
+
switch (payload.action) {
|
|
4378
|
+
case "fleet_jump": {
|
|
4379
|
+
if (typeof payload.destination !== "string" || typeof payload.arrival_tick !== "number") {
|
|
4380
|
+
this.warnMalformedFrame(frame);
|
|
4381
|
+
return;
|
|
4382
|
+
}
|
|
4383
|
+
changed = this.cache.applyDelta({
|
|
4384
|
+
location: {
|
|
4385
|
+
in_transit: true,
|
|
4386
|
+
transit_arrival_tick: payload.arrival_tick,
|
|
4387
|
+
transit_dest_system_id: payload.destination,
|
|
4388
|
+
transit_type: "jump"
|
|
4389
|
+
}
|
|
4390
|
+
});
|
|
4391
|
+
reconcile = true;
|
|
4392
|
+
break;
|
|
4393
|
+
}
|
|
4394
|
+
case "jumped":
|
|
4395
|
+
case "pathfinder_arrival": {
|
|
4396
|
+
if (typeof payload.system_id !== "string" || typeof payload.system !== "string") {
|
|
4397
|
+
this.warnMalformedFrame(frame);
|
|
4398
|
+
return;
|
|
4399
|
+
}
|
|
4400
|
+
changed = this.cache.applyDelta({
|
|
4401
|
+
location: {
|
|
4402
|
+
in_transit: false,
|
|
4403
|
+
poi_id: typeof payload.poi === "string" ? payload.poi : undefined,
|
|
4404
|
+
system_id: payload.system_id,
|
|
4405
|
+
system_name: payload.system
|
|
4406
|
+
}
|
|
4407
|
+
});
|
|
4408
|
+
reconcile = true;
|
|
4409
|
+
break;
|
|
4410
|
+
}
|
|
4411
|
+
case "fleet_travel": {
|
|
4412
|
+
if (typeof payload.destination !== "string" || typeof payload.arrival_tick !== "number") {
|
|
4413
|
+
this.warnMalformedFrame(frame);
|
|
4414
|
+
return;
|
|
4415
|
+
}
|
|
4416
|
+
changed = this.cache.applyDelta({
|
|
4417
|
+
location: {
|
|
4418
|
+
...currentSystem,
|
|
4419
|
+
in_transit: true,
|
|
4420
|
+
transit_arrival_tick: payload.arrival_tick,
|
|
4421
|
+
transit_dest_poi_id: payload.destination,
|
|
4422
|
+
transit_type: "travel"
|
|
4423
|
+
}
|
|
4424
|
+
});
|
|
4425
|
+
reconcile = true;
|
|
4426
|
+
break;
|
|
4427
|
+
}
|
|
4428
|
+
case "arrived": {
|
|
4429
|
+
if (typeof payload.poi_id !== "string" || typeof payload.poi !== "string") {
|
|
4430
|
+
this.warnMalformedFrame(frame);
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
changed = this.cache.applyDelta({
|
|
4434
|
+
location: {
|
|
4435
|
+
...currentSystem,
|
|
4436
|
+
in_transit: false,
|
|
4437
|
+
poi_id: payload.poi_id,
|
|
4438
|
+
poi_name: payload.poi
|
|
4439
|
+
}
|
|
4440
|
+
});
|
|
4441
|
+
reconcile = true;
|
|
4442
|
+
break;
|
|
4443
|
+
}
|
|
4444
|
+
case "fleet_undock":
|
|
4445
|
+
changed = currentLocation ? this.cache.patchSection("location", { docked_at: undefined }) : this.cache.applyDelta({ location: {} });
|
|
4446
|
+
break;
|
|
4447
|
+
case "fleet_dock":
|
|
4448
|
+
reconcile = true;
|
|
4449
|
+
break;
|
|
4450
|
+
default:
|
|
4451
|
+
return;
|
|
4452
|
+
}
|
|
4453
|
+
const revision = ++this.stateRevision;
|
|
4454
|
+
if (changed.includes("location"))
|
|
4455
|
+
this.checkSubscriptionsAgainstLocation();
|
|
4456
|
+
if (changed.length)
|
|
4457
|
+
this.emitStateChange(changed);
|
|
4458
|
+
if (reconcile)
|
|
4459
|
+
this.reconcileFleetState(revision, payload.action);
|
|
4460
|
+
}
|
|
4461
|
+
async reconcileFleetState(revision, action) {
|
|
4462
|
+
try {
|
|
4463
|
+
const result = await this.commands.spacemolt.get_status();
|
|
4464
|
+
const snapshot = requireStructuredContent(result, "spacemolt/get_status");
|
|
4465
|
+
if (revision !== this.stateRevision)
|
|
4466
|
+
return;
|
|
4467
|
+
const changed = this.cache.seed(snapshot);
|
|
4468
|
+
if (changed.length)
|
|
4469
|
+
this.stateRevision++;
|
|
4470
|
+
if (changed.includes("location"))
|
|
4471
|
+
this.checkSubscriptionsAgainstLocation();
|
|
4472
|
+
if (changed.length)
|
|
4473
|
+
this.emitStateChange(changed);
|
|
4474
|
+
} catch (err) {
|
|
4475
|
+
if (revision === this.stateRevision) {
|
|
4476
|
+
console.warn(`[spacemolt] failed to reconcile state after ${action}`, err);
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4352
4480
|
close() {
|
|
4353
4481
|
this.userClosing = true;
|
|
4354
4482
|
this.socket.close();
|
|
@@ -4465,6 +4593,8 @@ class Account {
|
|
|
4465
4593
|
const delta = frame.payload.result;
|
|
4466
4594
|
if (delta) {
|
|
4467
4595
|
const changed = this.cache.applyDelta(delta);
|
|
4596
|
+
if (changed.length)
|
|
4597
|
+
this.stateRevision++;
|
|
4468
4598
|
if (changed.includes("location"))
|
|
4469
4599
|
this.checkSubscriptionsAgainstLocation();
|
|
4470
4600
|
if (changed.length)
|
|
@@ -4499,6 +4629,7 @@ class Account {
|
|
|
4499
4629
|
return;
|
|
4500
4630
|
}
|
|
4501
4631
|
default:
|
|
4632
|
+
this.applyFleetMovementPush(frame);
|
|
4502
4633
|
this.emitter.emit(frame);
|
|
4503
4634
|
}
|
|
4504
4635
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spacemolt/lib",
|
|
3
|
-
"version": "12.2.
|
|
3
|
+
"version": "12.2.2",
|
|
4
4
|
"description": "The definitive TypeScript library for SpaceMolt. WebSocket-v2-first, multi-account, with local state caches. Internals are regenerated from the server's OpenAPI spec.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|