@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 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.unit_symbol
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.#rest.post("/currency/list-enable");
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, prevMqttConnectedOverride) {
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(prevMqttConnectedOverride);
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
- ts: this.now(),
1970
- rtt: rttMs,
1971
- source: "mqtt",
1972
- reason: "ok"
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(prevMqttConnected);
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
- const current = this.snapshot();
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(prevMqttConnectedOverride) {
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, prevMqttConnectedOverride?: boolean): void;
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, prevMqttConnectedOverride?: boolean): void;
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.unit_symbol
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.#rest.post("/currency/list-enable");
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, prevMqttConnectedOverride) {
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(prevMqttConnectedOverride);
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
- ts: this.now(),
1905
- rtt: rttMs,
1906
- source: "mqtt",
1907
- reason: "ok"
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(prevMqttConnected);
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
- const current = this.snapshot();
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(prevMqttConnectedOverride) {
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taphubhq/sdk-core",
3
- "version": "0.23.2",
3
+ "version": "0.23.4",
4
4
  "description": "Core SDK for building on the TabHub platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",