@taphubhq/sdk-core 0.23.2 → 0.23.4
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/index.cjs +46 -31
- package/dist/index.d.mts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +46 -31
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1402,6 +1402,9 @@ function mapWireConfig(raw) {
|
|
|
1402
1402
|
function mapWireIdealConfig(raw) {
|
|
1403
1403
|
const p = raw;
|
|
1404
1404
|
return {
|
|
1405
|
+
// bid-260612 guard ①: carry the producer's pairId so consumers can reject cross-pair
|
|
1406
|
+
// suggestions. Undefined when talking to a producer that predates the field.
|
|
1407
|
+
pairId: p.pairId,
|
|
1405
1408
|
cellSizeValue: p.cellSizeValue,
|
|
1406
1409
|
currentPrice: p.currentPrice,
|
|
1407
1410
|
reason: p.reason ?? "volatility_shift"
|
|
@@ -1636,15 +1639,15 @@ function normaliseMeResponse(body) {
|
|
|
1636
1639
|
};
|
|
1637
1640
|
}
|
|
1638
1641
|
function normaliseCurrencies(body) {
|
|
1639
|
-
if (!Array.isArray(body)) {
|
|
1642
|
+
if (!body || !Array.isArray(body.listEnabledCurrencies)) {
|
|
1640
1643
|
throw new TaphubServerError("Invalid response from server", {
|
|
1641
1644
|
code: "InvalidResponse"
|
|
1642
1645
|
});
|
|
1643
1646
|
}
|
|
1644
|
-
return body.map((entry) => ({
|
|
1647
|
+
return body.listEnabledCurrencies.map((entry) => ({
|
|
1645
1648
|
code: entry.code,
|
|
1646
1649
|
unit: entry.unit,
|
|
1647
|
-
unitSymbol: entry.
|
|
1650
|
+
unitSymbol: entry.unitSymbol
|
|
1648
1651
|
}));
|
|
1649
1652
|
}
|
|
1650
1653
|
function normaliseWalletNode(node) {
|
|
@@ -1700,6 +1703,9 @@ function normaliseUserPnL(node) {
|
|
|
1700
1703
|
// src/modules/user/queries.ts
|
|
1701
1704
|
var ME_QUERY = "query Me { me { id username wallet_address balance is_demo } }";
|
|
1702
1705
|
var MY_WALLETS_QUERY = "query MyWallets { myWallets { id amount currency isEnable } }";
|
|
1706
|
+
var LIST_ENABLED_CURRENCIES_QUERY = `query ListEnabledCurrencies($input: ListEnabledCurrenciesInput!) {
|
|
1707
|
+
listEnabledCurrencies(input: $input) { code unit unitSymbol }
|
|
1708
|
+
}`;
|
|
1703
1709
|
var MY_WALLET_BY_CURRENCY_QUERY = "query MyWalletByCurrency($currency: String!) { myWalletByCurrency(currency: $currency) { id amount currency isEnable } }";
|
|
1704
1710
|
var USER_PNL_QUERY = `query UserPnL($period: String) {
|
|
1705
1711
|
userPnL(period: $period) {
|
|
@@ -1709,12 +1715,10 @@ var USER_PNL_QUERY = `query UserPnL($period: String) {
|
|
|
1709
1715
|
|
|
1710
1716
|
// src/modules/user/index.ts
|
|
1711
1717
|
var UserModule = class {
|
|
1712
|
-
#rest;
|
|
1713
1718
|
#graphql;
|
|
1714
1719
|
#graphqlUser;
|
|
1715
1720
|
#currencies = null;
|
|
1716
1721
|
constructor(deps) {
|
|
1717
|
-
this.#rest = deps.rest;
|
|
1718
1722
|
this.#graphql = deps.graphql;
|
|
1719
1723
|
this.#graphqlUser = deps.graphqlUser;
|
|
1720
1724
|
}
|
|
@@ -1756,7 +1760,10 @@ var UserModule = class {
|
|
|
1756
1760
|
return this.#currencies;
|
|
1757
1761
|
}
|
|
1758
1762
|
async refreshCurrencies() {
|
|
1759
|
-
const body = await this.#
|
|
1763
|
+
const body = await this.#graphqlUser.request(
|
|
1764
|
+
LIST_ENABLED_CURRENCIES_QUERY,
|
|
1765
|
+
{ input: {} }
|
|
1766
|
+
);
|
|
1760
1767
|
const currencies = normaliseCurrencies(body);
|
|
1761
1768
|
this.#currencies = currencies;
|
|
1762
1769
|
return currencies;
|
|
@@ -1932,23 +1939,28 @@ var NetworkQualityMonitor = class {
|
|
|
1932
1939
|
mqttDisconnectedAt = null;
|
|
1933
1940
|
committedNetwork = "good";
|
|
1934
1941
|
committedBackend = "ok";
|
|
1942
|
+
// The last snapshot we emitted, used to decide whether the next recompute is
|
|
1943
|
+
// worth emitting. Seeded with the initial default so the first real change
|
|
1944
|
+
// (e.g. first backend5xx, first ping) emits against the true starting state.
|
|
1945
|
+
lastEmitted;
|
|
1935
1946
|
tickHandle = null;
|
|
1936
1947
|
onlineListener = null;
|
|
1937
1948
|
offlineListener = null;
|
|
1938
1949
|
disposed = false;
|
|
1939
1950
|
constructor(opts) {
|
|
1940
1951
|
this.bus = opts.bus;
|
|
1952
|
+
this.lastEmitted = this.snapshot();
|
|
1941
1953
|
this.attachBrowserListeners();
|
|
1942
1954
|
this.startTick();
|
|
1943
1955
|
}
|
|
1944
|
-
addSample(sample
|
|
1956
|
+
addSample(sample) {
|
|
1945
1957
|
if (this.disposed) return;
|
|
1946
1958
|
const bucket = this.buckets[sample.source];
|
|
1947
1959
|
bucket.push(sample);
|
|
1948
1960
|
if (bucket.length > WINDOW_MAX_SAMPLES_PER_SOURCE) {
|
|
1949
1961
|
bucket.shift();
|
|
1950
1962
|
}
|
|
1951
|
-
this.recompute(
|
|
1963
|
+
this.recompute();
|
|
1952
1964
|
}
|
|
1953
1965
|
seedFromConnection(seed) {
|
|
1954
1966
|
const rtt = this.resolveSeedRtt(seed);
|
|
@@ -1961,24 +1973,19 @@ var NetworkQualityMonitor = class {
|
|
|
1961
1973
|
});
|
|
1962
1974
|
}
|
|
1963
1975
|
notifyMqttConnect(rttMs) {
|
|
1964
|
-
const prevMqttConnected = this.mqttConnected;
|
|
1965
1976
|
this.mqttConnected = true;
|
|
1966
1977
|
this.mqttDisconnectedAt = null;
|
|
1967
|
-
this.addSample(
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
},
|
|
1974
|
-
prevMqttConnected
|
|
1975
|
-
);
|
|
1978
|
+
this.addSample({
|
|
1979
|
+
ts: this.now(),
|
|
1980
|
+
rtt: rttMs,
|
|
1981
|
+
source: "mqtt",
|
|
1982
|
+
reason: "connect"
|
|
1983
|
+
});
|
|
1976
1984
|
}
|
|
1977
1985
|
notifyMqttDisconnect() {
|
|
1978
|
-
const prevMqttConnected = this.mqttConnected;
|
|
1979
1986
|
this.mqttConnected = false;
|
|
1980
1987
|
this.mqttDisconnectedAt = this.now();
|
|
1981
|
-
this.recompute(
|
|
1988
|
+
this.recompute();
|
|
1982
1989
|
}
|
|
1983
1990
|
notifyMqttPing(rttMs) {
|
|
1984
1991
|
this.smoother.add(rttMs);
|
|
@@ -2002,7 +2009,6 @@ var NetworkQualityMonitor = class {
|
|
|
2002
2009
|
return this.snapshot();
|
|
2003
2010
|
}
|
|
2004
2011
|
reset() {
|
|
2005
|
-
const prev = this.snapshot();
|
|
2006
2012
|
for (const key of Object.keys(this.buckets)) {
|
|
2007
2013
|
this.buckets[key] = [];
|
|
2008
2014
|
}
|
|
@@ -2011,10 +2017,7 @@ var NetworkQualityMonitor = class {
|
|
|
2011
2017
|
this.mqttDisconnectedAt = null;
|
|
2012
2018
|
this.committedNetwork = "good";
|
|
2013
2019
|
this.committedBackend = "ok";
|
|
2014
|
-
|
|
2015
|
-
if (prev.network !== current.network || prev.backend !== current.backend) {
|
|
2016
|
-
this.bus.emit("network:change", { previous: prev, current });
|
|
2017
|
-
}
|
|
2020
|
+
this.emitIfChanged(this.snapshot());
|
|
2018
2021
|
}
|
|
2019
2022
|
tick() {
|
|
2020
2023
|
this.pruneWindow();
|
|
@@ -2069,9 +2072,8 @@ var NetworkQualityMonitor = class {
|
|
|
2069
2072
|
this.buckets[key] = this.buckets[key].filter((s) => s.ts >= cutoff);
|
|
2070
2073
|
}
|
|
2071
2074
|
}
|
|
2072
|
-
recompute(
|
|
2075
|
+
recompute() {
|
|
2073
2076
|
this.pruneWindow();
|
|
2074
|
-
const prev = this.snapshot();
|
|
2075
2077
|
const allSamples = this.allHttpAndMqttSamples();
|
|
2076
2078
|
const realSamples = allSamples.filter((s) => s.source !== "connection");
|
|
2077
2079
|
const useConnection = realSamples.length < COLD_START_REAL_SAMPLE_THRESHOLD;
|
|
@@ -2095,12 +2097,26 @@ var NetworkQualityMonitor = class {
|
|
|
2095
2097
|
this.smoother.releaseOffline();
|
|
2096
2098
|
nextNetwork = this.smoother.level;
|
|
2097
2099
|
}
|
|
2098
|
-
const prevMqttConnected = prevMqttConnectedOverride ?? prev.mqttConnected;
|
|
2099
|
-
const changed = nextNetwork !== this.committedNetwork || backend !== this.committedBackend || this.mqttConnected !== prevMqttConnected;
|
|
2100
2100
|
this.committedNetwork = nextNetwork;
|
|
2101
2101
|
this.committedBackend = backend;
|
|
2102
|
+
this.emitIfChanged(this.snapshot());
|
|
2103
|
+
}
|
|
2104
|
+
// Emits network:change when any *displayed* field of the snapshot moves. This
|
|
2105
|
+
// is compared against the last emitted snapshot (not a snapshot taken at the
|
|
2106
|
+
// top of recompute): notifyMqttPing feeds the smoother BEFORE recompute, so a
|
|
2107
|
+
// same-call snapshot would already carry the new rtt and the change would be
|
|
2108
|
+
// invisible (the original "rtt label freezes on a stable connection" bug).
|
|
2109
|
+
//
|
|
2110
|
+
// Trigger fields: network, backend, mqttConnected, rtt, jitter. rtt/jitter are
|
|
2111
|
+
// bounded by the MQTT ping cadence (~keepalive), so this cannot spam. lossRate
|
|
2112
|
+
// and samplesInWindow are deliberately excluded — they move on every HTTP
|
|
2113
|
+
// sample (request-volume driven) and are diagnostics only; consumers that show
|
|
2114
|
+
// them poll getCurrent().
|
|
2115
|
+
emitIfChanged(current) {
|
|
2116
|
+
const prev = this.lastEmitted;
|
|
2117
|
+
this.lastEmitted = current;
|
|
2118
|
+
const changed = current.network !== prev.network || current.backend !== prev.backend || current.mqttConnected !== prev.mqttConnected || current.rtt !== prev.rtt || current.jitter !== prev.jitter;
|
|
2102
2119
|
if (changed) {
|
|
2103
|
-
const current = this.snapshot();
|
|
2104
2120
|
this.bus.emit("network:change", { previous: prev, current });
|
|
2105
2121
|
}
|
|
2106
2122
|
}
|
|
@@ -2740,7 +2756,6 @@ var TaphubClient = class {
|
|
|
2740
2756
|
onRequest: graphqlProbe
|
|
2741
2757
|
});
|
|
2742
2758
|
this.user = new UserModule({
|
|
2743
|
-
rest: this.#rest,
|
|
2744
2759
|
graphql: this.#graphql,
|
|
2745
2760
|
graphqlUser: this.#graphqlUser
|
|
2746
2761
|
});
|
package/dist/index.d.mts
CHANGED
|
@@ -35,7 +35,7 @@ interface TaphubClientConfig {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
type SignalSource = 'graphql' | 'rest' | 'mqtt' | 'browser' | 'connection';
|
|
38
|
-
type SampleReason = 'ok' | 'network' | 'timeout' | 'offline' | 'backend5xx' | 'backend4xx' | 'gqlError';
|
|
38
|
+
type SampleReason = 'ok' | 'connect' | 'network' | 'timeout' | 'offline' | 'backend5xx' | 'backend4xx' | 'gqlError';
|
|
39
39
|
type Sample = {
|
|
40
40
|
ts: number;
|
|
41
41
|
rtt: number;
|
|
@@ -838,6 +838,12 @@ interface MqttConfigEvent {
|
|
|
838
838
|
acceptableBids: number[];
|
|
839
839
|
}
|
|
840
840
|
interface MqttIdealConfigEvent {
|
|
841
|
+
/**
|
|
842
|
+
* game_pairs.id (e.g. "grid-ETH-USD") the suggestion was computed for. Optional for
|
|
843
|
+
* backward compat with producers predating bid-260612; when present, a consumer drops
|
|
844
|
+
* suggestions whose pairId does not match the pair it is currently viewing (guard ①).
|
|
845
|
+
*/
|
|
846
|
+
pairId?: string;
|
|
841
847
|
cellSizeValue: number;
|
|
842
848
|
currentPrice: number;
|
|
843
849
|
reason: string;
|
|
@@ -992,7 +998,6 @@ interface UserPnL {
|
|
|
992
998
|
}
|
|
993
999
|
|
|
994
1000
|
interface UserModuleDeps {
|
|
995
|
-
rest: RestTransport;
|
|
996
1001
|
graphql: GraphQLTransport;
|
|
997
1002
|
graphqlUser: GraphQLTransport;
|
|
998
1003
|
}
|
|
@@ -1039,6 +1044,7 @@ declare class NetworkQualityMonitor {
|
|
|
1039
1044
|
private mqttDisconnectedAt;
|
|
1040
1045
|
private committedNetwork;
|
|
1041
1046
|
private committedBackend;
|
|
1047
|
+
private lastEmitted;
|
|
1042
1048
|
private tickHandle;
|
|
1043
1049
|
private onlineListener;
|
|
1044
1050
|
private offlineListener;
|
|
@@ -1046,7 +1052,7 @@ declare class NetworkQualityMonitor {
|
|
|
1046
1052
|
constructor(opts: {
|
|
1047
1053
|
bus: TaphubEventBus<EventMapWithNetwork>;
|
|
1048
1054
|
});
|
|
1049
|
-
addSample(sample: Sample
|
|
1055
|
+
addSample(sample: Sample): void;
|
|
1050
1056
|
seedFromConnection(seed: ConnectionSeed): void;
|
|
1051
1057
|
notifyMqttConnect(rttMs: number): void;
|
|
1052
1058
|
notifyMqttDisconnect(): void;
|
|
@@ -1062,6 +1068,7 @@ declare class NetworkQualityMonitor {
|
|
|
1062
1068
|
private detachBrowserListeners;
|
|
1063
1069
|
private pruneWindow;
|
|
1064
1070
|
private recompute;
|
|
1071
|
+
private emitIfChanged;
|
|
1065
1072
|
private deriveMetricsFrom;
|
|
1066
1073
|
private isHardOffline;
|
|
1067
1074
|
private allHttpAndMqttSamples;
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ interface TaphubClientConfig {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
type SignalSource = 'graphql' | 'rest' | 'mqtt' | 'browser' | 'connection';
|
|
38
|
-
type SampleReason = 'ok' | 'network' | 'timeout' | 'offline' | 'backend5xx' | 'backend4xx' | 'gqlError';
|
|
38
|
+
type SampleReason = 'ok' | 'connect' | 'network' | 'timeout' | 'offline' | 'backend5xx' | 'backend4xx' | 'gqlError';
|
|
39
39
|
type Sample = {
|
|
40
40
|
ts: number;
|
|
41
41
|
rtt: number;
|
|
@@ -838,6 +838,12 @@ interface MqttConfigEvent {
|
|
|
838
838
|
acceptableBids: number[];
|
|
839
839
|
}
|
|
840
840
|
interface MqttIdealConfigEvent {
|
|
841
|
+
/**
|
|
842
|
+
* game_pairs.id (e.g. "grid-ETH-USD") the suggestion was computed for. Optional for
|
|
843
|
+
* backward compat with producers predating bid-260612; when present, a consumer drops
|
|
844
|
+
* suggestions whose pairId does not match the pair it is currently viewing (guard ①).
|
|
845
|
+
*/
|
|
846
|
+
pairId?: string;
|
|
841
847
|
cellSizeValue: number;
|
|
842
848
|
currentPrice: number;
|
|
843
849
|
reason: string;
|
|
@@ -992,7 +998,6 @@ interface UserPnL {
|
|
|
992
998
|
}
|
|
993
999
|
|
|
994
1000
|
interface UserModuleDeps {
|
|
995
|
-
rest: RestTransport;
|
|
996
1001
|
graphql: GraphQLTransport;
|
|
997
1002
|
graphqlUser: GraphQLTransport;
|
|
998
1003
|
}
|
|
@@ -1039,6 +1044,7 @@ declare class NetworkQualityMonitor {
|
|
|
1039
1044
|
private mqttDisconnectedAt;
|
|
1040
1045
|
private committedNetwork;
|
|
1041
1046
|
private committedBackend;
|
|
1047
|
+
private lastEmitted;
|
|
1042
1048
|
private tickHandle;
|
|
1043
1049
|
private onlineListener;
|
|
1044
1050
|
private offlineListener;
|
|
@@ -1046,7 +1052,7 @@ declare class NetworkQualityMonitor {
|
|
|
1046
1052
|
constructor(opts: {
|
|
1047
1053
|
bus: TaphubEventBus<EventMapWithNetwork>;
|
|
1048
1054
|
});
|
|
1049
|
-
addSample(sample: Sample
|
|
1055
|
+
addSample(sample: Sample): void;
|
|
1050
1056
|
seedFromConnection(seed: ConnectionSeed): void;
|
|
1051
1057
|
notifyMqttConnect(rttMs: number): void;
|
|
1052
1058
|
notifyMqttDisconnect(): void;
|
|
@@ -1062,6 +1068,7 @@ declare class NetworkQualityMonitor {
|
|
|
1062
1068
|
private detachBrowserListeners;
|
|
1063
1069
|
private pruneWindow;
|
|
1064
1070
|
private recompute;
|
|
1071
|
+
private emitIfChanged;
|
|
1065
1072
|
private deriveMetricsFrom;
|
|
1066
1073
|
private isHardOffline;
|
|
1067
1074
|
private allHttpAndMqttSamples;
|
package/dist/index.js
CHANGED
|
@@ -1337,6 +1337,9 @@ function mapWireConfig(raw) {
|
|
|
1337
1337
|
function mapWireIdealConfig(raw) {
|
|
1338
1338
|
const p = raw;
|
|
1339
1339
|
return {
|
|
1340
|
+
// bid-260612 guard ①: carry the producer's pairId so consumers can reject cross-pair
|
|
1341
|
+
// suggestions. Undefined when talking to a producer that predates the field.
|
|
1342
|
+
pairId: p.pairId,
|
|
1340
1343
|
cellSizeValue: p.cellSizeValue,
|
|
1341
1344
|
currentPrice: p.currentPrice,
|
|
1342
1345
|
reason: p.reason ?? "volatility_shift"
|
|
@@ -1571,15 +1574,15 @@ function normaliseMeResponse(body) {
|
|
|
1571
1574
|
};
|
|
1572
1575
|
}
|
|
1573
1576
|
function normaliseCurrencies(body) {
|
|
1574
|
-
if (!Array.isArray(body)) {
|
|
1577
|
+
if (!body || !Array.isArray(body.listEnabledCurrencies)) {
|
|
1575
1578
|
throw new TaphubServerError("Invalid response from server", {
|
|
1576
1579
|
code: "InvalidResponse"
|
|
1577
1580
|
});
|
|
1578
1581
|
}
|
|
1579
|
-
return body.map((entry) => ({
|
|
1582
|
+
return body.listEnabledCurrencies.map((entry) => ({
|
|
1580
1583
|
code: entry.code,
|
|
1581
1584
|
unit: entry.unit,
|
|
1582
|
-
unitSymbol: entry.
|
|
1585
|
+
unitSymbol: entry.unitSymbol
|
|
1583
1586
|
}));
|
|
1584
1587
|
}
|
|
1585
1588
|
function normaliseWalletNode(node) {
|
|
@@ -1635,6 +1638,9 @@ function normaliseUserPnL(node) {
|
|
|
1635
1638
|
// src/modules/user/queries.ts
|
|
1636
1639
|
var ME_QUERY = "query Me { me { id username wallet_address balance is_demo } }";
|
|
1637
1640
|
var MY_WALLETS_QUERY = "query MyWallets { myWallets { id amount currency isEnable } }";
|
|
1641
|
+
var LIST_ENABLED_CURRENCIES_QUERY = `query ListEnabledCurrencies($input: ListEnabledCurrenciesInput!) {
|
|
1642
|
+
listEnabledCurrencies(input: $input) { code unit unitSymbol }
|
|
1643
|
+
}`;
|
|
1638
1644
|
var MY_WALLET_BY_CURRENCY_QUERY = "query MyWalletByCurrency($currency: String!) { myWalletByCurrency(currency: $currency) { id amount currency isEnable } }";
|
|
1639
1645
|
var USER_PNL_QUERY = `query UserPnL($period: String) {
|
|
1640
1646
|
userPnL(period: $period) {
|
|
@@ -1644,12 +1650,10 @@ var USER_PNL_QUERY = `query UserPnL($period: String) {
|
|
|
1644
1650
|
|
|
1645
1651
|
// src/modules/user/index.ts
|
|
1646
1652
|
var UserModule = class {
|
|
1647
|
-
#rest;
|
|
1648
1653
|
#graphql;
|
|
1649
1654
|
#graphqlUser;
|
|
1650
1655
|
#currencies = null;
|
|
1651
1656
|
constructor(deps) {
|
|
1652
|
-
this.#rest = deps.rest;
|
|
1653
1657
|
this.#graphql = deps.graphql;
|
|
1654
1658
|
this.#graphqlUser = deps.graphqlUser;
|
|
1655
1659
|
}
|
|
@@ -1691,7 +1695,10 @@ var UserModule = class {
|
|
|
1691
1695
|
return this.#currencies;
|
|
1692
1696
|
}
|
|
1693
1697
|
async refreshCurrencies() {
|
|
1694
|
-
const body = await this.#
|
|
1698
|
+
const body = await this.#graphqlUser.request(
|
|
1699
|
+
LIST_ENABLED_CURRENCIES_QUERY,
|
|
1700
|
+
{ input: {} }
|
|
1701
|
+
);
|
|
1695
1702
|
const currencies = normaliseCurrencies(body);
|
|
1696
1703
|
this.#currencies = currencies;
|
|
1697
1704
|
return currencies;
|
|
@@ -1867,23 +1874,28 @@ var NetworkQualityMonitor = class {
|
|
|
1867
1874
|
mqttDisconnectedAt = null;
|
|
1868
1875
|
committedNetwork = "good";
|
|
1869
1876
|
committedBackend = "ok";
|
|
1877
|
+
// The last snapshot we emitted, used to decide whether the next recompute is
|
|
1878
|
+
// worth emitting. Seeded with the initial default so the first real change
|
|
1879
|
+
// (e.g. first backend5xx, first ping) emits against the true starting state.
|
|
1880
|
+
lastEmitted;
|
|
1870
1881
|
tickHandle = null;
|
|
1871
1882
|
onlineListener = null;
|
|
1872
1883
|
offlineListener = null;
|
|
1873
1884
|
disposed = false;
|
|
1874
1885
|
constructor(opts) {
|
|
1875
1886
|
this.bus = opts.bus;
|
|
1887
|
+
this.lastEmitted = this.snapshot();
|
|
1876
1888
|
this.attachBrowserListeners();
|
|
1877
1889
|
this.startTick();
|
|
1878
1890
|
}
|
|
1879
|
-
addSample(sample
|
|
1891
|
+
addSample(sample) {
|
|
1880
1892
|
if (this.disposed) return;
|
|
1881
1893
|
const bucket = this.buckets[sample.source];
|
|
1882
1894
|
bucket.push(sample);
|
|
1883
1895
|
if (bucket.length > WINDOW_MAX_SAMPLES_PER_SOURCE) {
|
|
1884
1896
|
bucket.shift();
|
|
1885
1897
|
}
|
|
1886
|
-
this.recompute(
|
|
1898
|
+
this.recompute();
|
|
1887
1899
|
}
|
|
1888
1900
|
seedFromConnection(seed) {
|
|
1889
1901
|
const rtt = this.resolveSeedRtt(seed);
|
|
@@ -1896,24 +1908,19 @@ var NetworkQualityMonitor = class {
|
|
|
1896
1908
|
});
|
|
1897
1909
|
}
|
|
1898
1910
|
notifyMqttConnect(rttMs) {
|
|
1899
|
-
const prevMqttConnected = this.mqttConnected;
|
|
1900
1911
|
this.mqttConnected = true;
|
|
1901
1912
|
this.mqttDisconnectedAt = null;
|
|
1902
|
-
this.addSample(
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
},
|
|
1909
|
-
prevMqttConnected
|
|
1910
|
-
);
|
|
1913
|
+
this.addSample({
|
|
1914
|
+
ts: this.now(),
|
|
1915
|
+
rtt: rttMs,
|
|
1916
|
+
source: "mqtt",
|
|
1917
|
+
reason: "connect"
|
|
1918
|
+
});
|
|
1911
1919
|
}
|
|
1912
1920
|
notifyMqttDisconnect() {
|
|
1913
|
-
const prevMqttConnected = this.mqttConnected;
|
|
1914
1921
|
this.mqttConnected = false;
|
|
1915
1922
|
this.mqttDisconnectedAt = this.now();
|
|
1916
|
-
this.recompute(
|
|
1923
|
+
this.recompute();
|
|
1917
1924
|
}
|
|
1918
1925
|
notifyMqttPing(rttMs) {
|
|
1919
1926
|
this.smoother.add(rttMs);
|
|
@@ -1937,7 +1944,6 @@ var NetworkQualityMonitor = class {
|
|
|
1937
1944
|
return this.snapshot();
|
|
1938
1945
|
}
|
|
1939
1946
|
reset() {
|
|
1940
|
-
const prev = this.snapshot();
|
|
1941
1947
|
for (const key of Object.keys(this.buckets)) {
|
|
1942
1948
|
this.buckets[key] = [];
|
|
1943
1949
|
}
|
|
@@ -1946,10 +1952,7 @@ var NetworkQualityMonitor = class {
|
|
|
1946
1952
|
this.mqttDisconnectedAt = null;
|
|
1947
1953
|
this.committedNetwork = "good";
|
|
1948
1954
|
this.committedBackend = "ok";
|
|
1949
|
-
|
|
1950
|
-
if (prev.network !== current.network || prev.backend !== current.backend) {
|
|
1951
|
-
this.bus.emit("network:change", { previous: prev, current });
|
|
1952
|
-
}
|
|
1955
|
+
this.emitIfChanged(this.snapshot());
|
|
1953
1956
|
}
|
|
1954
1957
|
tick() {
|
|
1955
1958
|
this.pruneWindow();
|
|
@@ -2004,9 +2007,8 @@ var NetworkQualityMonitor = class {
|
|
|
2004
2007
|
this.buckets[key] = this.buckets[key].filter((s) => s.ts >= cutoff);
|
|
2005
2008
|
}
|
|
2006
2009
|
}
|
|
2007
|
-
recompute(
|
|
2010
|
+
recompute() {
|
|
2008
2011
|
this.pruneWindow();
|
|
2009
|
-
const prev = this.snapshot();
|
|
2010
2012
|
const allSamples = this.allHttpAndMqttSamples();
|
|
2011
2013
|
const realSamples = allSamples.filter((s) => s.source !== "connection");
|
|
2012
2014
|
const useConnection = realSamples.length < COLD_START_REAL_SAMPLE_THRESHOLD;
|
|
@@ -2030,12 +2032,26 @@ var NetworkQualityMonitor = class {
|
|
|
2030
2032
|
this.smoother.releaseOffline();
|
|
2031
2033
|
nextNetwork = this.smoother.level;
|
|
2032
2034
|
}
|
|
2033
|
-
const prevMqttConnected = prevMqttConnectedOverride ?? prev.mqttConnected;
|
|
2034
|
-
const changed = nextNetwork !== this.committedNetwork || backend !== this.committedBackend || this.mqttConnected !== prevMqttConnected;
|
|
2035
2035
|
this.committedNetwork = nextNetwork;
|
|
2036
2036
|
this.committedBackend = backend;
|
|
2037
|
+
this.emitIfChanged(this.snapshot());
|
|
2038
|
+
}
|
|
2039
|
+
// Emits network:change when any *displayed* field of the snapshot moves. This
|
|
2040
|
+
// is compared against the last emitted snapshot (not a snapshot taken at the
|
|
2041
|
+
// top of recompute): notifyMqttPing feeds the smoother BEFORE recompute, so a
|
|
2042
|
+
// same-call snapshot would already carry the new rtt and the change would be
|
|
2043
|
+
// invisible (the original "rtt label freezes on a stable connection" bug).
|
|
2044
|
+
//
|
|
2045
|
+
// Trigger fields: network, backend, mqttConnected, rtt, jitter. rtt/jitter are
|
|
2046
|
+
// bounded by the MQTT ping cadence (~keepalive), so this cannot spam. lossRate
|
|
2047
|
+
// and samplesInWindow are deliberately excluded — they move on every HTTP
|
|
2048
|
+
// sample (request-volume driven) and are diagnostics only; consumers that show
|
|
2049
|
+
// them poll getCurrent().
|
|
2050
|
+
emitIfChanged(current) {
|
|
2051
|
+
const prev = this.lastEmitted;
|
|
2052
|
+
this.lastEmitted = current;
|
|
2053
|
+
const changed = current.network !== prev.network || current.backend !== prev.backend || current.mqttConnected !== prev.mqttConnected || current.rtt !== prev.rtt || current.jitter !== prev.jitter;
|
|
2037
2054
|
if (changed) {
|
|
2038
|
-
const current = this.snapshot();
|
|
2039
2055
|
this.bus.emit("network:change", { previous: prev, current });
|
|
2040
2056
|
}
|
|
2041
2057
|
}
|
|
@@ -2675,7 +2691,6 @@ var TaphubClient = class {
|
|
|
2675
2691
|
onRequest: graphqlProbe
|
|
2676
2692
|
});
|
|
2677
2693
|
this.user = new UserModule({
|
|
2678
|
-
rest: this.#rest,
|
|
2679
2694
|
graphql: this.#graphql,
|
|
2680
2695
|
graphqlUser: this.#graphqlUser
|
|
2681
2696
|
});
|