@taphubhq/sdk-core 0.22.3 → 0.23.1

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
@@ -863,7 +863,15 @@ function normaliseGameConfig(node) {
863
863
  constraints: normaliseConstraints(node.constraints),
864
864
  acceptableBids: node.acceptableBids,
865
865
  minBidAmount: node.minBidAmount,
866
- maxBidAmount: node.maxBidAmount
866
+ maxBidAmount: node.maxBidAmount,
867
+ // Effective cancel policy. Distinguish three states so consumers can apply
868
+ // the right default:
869
+ // undefined → server omitted the field (older server) → use platform default
870
+ // 0 → builder explicitly disabled cancel → keep 0 (cancel OFF)
871
+ // > 0 → builder/effective threshold
872
+ // (null from the wire becomes undefined, NOT 0, so "not sent" ≠ "disabled".)
873
+ bidCancelRefundRate: node.bidCancelRefundRate ?? void 0,
874
+ bidCancelMinSeconds: node.bidCancelMinSeconds ?? void 0
867
875
  };
868
876
  }
869
877
  function normaliseGridConfig(node) {
@@ -925,6 +933,7 @@ var PAIR_QUERY = `query AgencyPair($pairId: ID!) {
925
933
  gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
926
934
  constraints { minBetTime maxBetTime slippage priceMinRange priceMaxRange coefMults maxCoef }
927
935
  acceptableBids minBidAmount maxBidAmount
936
+ bidCancelRefundRate bidCancelMinSeconds
928
937
  }
929
938
  }
930
939
  }`;
@@ -1819,31 +1828,11 @@ function classifyBackendHealth(samples) {
1819
1828
  }
1820
1829
  return "ok";
1821
1830
  }
1822
- var RTT_GOOD_TO_FAIR = 180;
1823
- var RTT_FAIR_TO_GOOD = 120;
1824
- var RTT_FAIR_TO_POOR = 450;
1825
- var RTT_POOR_TO_FAIR = 350;
1826
- var JITTER_POOR = 150;
1827
- var JITTER_LEAVE_POOR = 120;
1828
- var isPoor = (m) => m.rtt > RTT_FAIR_TO_POOR || m.jitter > JITTER_POOR;
1829
- var canLeavePoor = (m) => m.rtt < RTT_POOR_TO_FAIR && m.jitter < JITTER_LEAVE_POOR;
1830
- var isFairUpper = (rtt) => rtt > RTT_GOOD_TO_FAIR;
1831
- var canReachGood = (rtt) => rtt < RTT_FAIR_TO_GOOD;
1832
- function classifyNetworkLevel(metrics, current) {
1833
- if (current === "offline") {
1834
- return isPoor(metrics) ? "poor" : "fair";
1835
- }
1836
- if (current === "good") {
1837
- if (isPoor(metrics)) return "fair";
1838
- if (isFairUpper(metrics.rtt)) return "fair";
1839
- return "good";
1840
- }
1841
- if (current === "fair") {
1842
- if (isPoor(metrics)) return "poor";
1843
- if (canReachGood(metrics.rtt)) return "good";
1844
- return "fair";
1845
- }
1846
- if (canLeavePoor(metrics)) return "fair";
1831
+ var GOOD_MAX_MS = 150;
1832
+ var FAIR_MAX_MS = 400;
1833
+ function classifyNetworkLevel(rtt) {
1834
+ if (rtt <= GOOD_MAX_MS) return "good";
1835
+ if (rtt <= FAIR_MAX_MS) return "fair";
1847
1836
  return "poor";
1848
1837
  }
1849
1838
 
@@ -1866,24 +1855,30 @@ var RttSmoother = class {
1866
1855
  candidateCount = 0;
1867
1856
  overrideOffline = false;
1868
1857
  consecutiveOutliers = 0;
1869
- add(rtt, metrics) {
1858
+ add(rtt) {
1870
1859
  if (this.ema > 0 && rtt > OUTLIER_MULTIPLIER * this.ema) {
1871
1860
  this.consecutiveOutliers += 1;
1872
1861
  if (this.consecutiveOutliers < 2) return;
1873
1862
  } else {
1874
1863
  this.consecutiveOutliers = 0;
1875
1864
  }
1876
- this.ema = this.ema === 0 ? rtt : EMA_ALPHA * rtt + (1 - EMA_ALPHA) * this.ema;
1877
- const next = classifyNetworkLevel({ rtt, jitter: metrics.jitter }, this.committed);
1878
- if (next === this.candidate) {
1879
- this.candidateCount += 1;
1880
- } else {
1865
+ const coldStart = this.ema === 0;
1866
+ this.ema = coldStart ? rtt : EMA_ALPHA * rtt + (1 - EMA_ALPHA) * this.ema;
1867
+ const next = classifyNetworkLevel(this.ema);
1868
+ if (coldStart) {
1869
+ this.committed = next;
1881
1870
  this.candidate = next;
1882
1871
  this.candidateCount = 1;
1883
- }
1884
- const required = this.requiredSamplesFor(next);
1885
- if (this.candidateCount >= required) {
1886
- this.committed = next;
1872
+ } else {
1873
+ if (next === this.candidate) {
1874
+ this.candidateCount += 1;
1875
+ } else {
1876
+ this.candidate = next;
1877
+ this.candidateCount = 1;
1878
+ }
1879
+ if (this.candidateCount >= this.requiredSamplesFor(next)) {
1880
+ this.committed = next;
1881
+ }
1887
1882
  }
1888
1883
  this.level = this.overrideOffline ? "offline" : this.committed;
1889
1884
  }
@@ -1951,9 +1946,6 @@ var NetworkQualityMonitor = class {
1951
1946
  if (bucket.length > WINDOW_MAX_SAMPLES_PER_SOURCE) {
1952
1947
  bucket.shift();
1953
1948
  }
1954
- if (sample.source === "mqtt" && sample.reason === "ok") {
1955
- this.smoother.add(sample.rtt, this.deriveMetrics());
1956
- }
1957
1949
  this.recompute(prevMqttConnectedOverride);
1958
1950
  }
1959
1951
  seedFromConnection(seed) {
@@ -1987,6 +1979,7 @@ var NetworkQualityMonitor = class {
1987
1979
  this.recompute(prevMqttConnected);
1988
1980
  }
1989
1981
  notifyMqttPing(rttMs) {
1982
+ this.smoother.add(rttMs);
1990
1983
  this.addSample({
1991
1984
  ts: this.now(),
1992
1985
  rtt: rttMs,
@@ -2084,10 +2077,7 @@ var NetworkQualityMonitor = class {
2084
2077
  const httpSamples = effectiveSamples.filter((s) => isHttpSource(s.source));
2085
2078
  const backend = classifyBackendHealth(httpSamples);
2086
2079
  const metrics = this.deriveMetricsFrom(effectiveSamples);
2087
- const candidate = classifyNetworkLevel(
2088
- { rtt: metrics.emaForLevel, jitter: metrics.jitter },
2089
- this.committedNetwork
2090
- );
2080
+ const candidate = classifyNetworkLevel(metrics.emaForLevel);
2091
2081
  let nextNetwork = this.smoother.level;
2092
2082
  if (effectiveSamples.length === 0) {
2093
2083
  nextNetwork = this.committedNetwork;
@@ -2112,9 +2102,6 @@ var NetworkQualityMonitor = class {
2112
2102
  this.bus.emit("network:change", { previous: prev, current });
2113
2103
  }
2114
2104
  }
2115
- deriveMetrics() {
2116
- return this.deriveMetricsFrom(this.allHttpAndMqttSamples());
2117
- }
2118
2105
  deriveMetricsFrom(samples) {
2119
2106
  const httpSamples = samples.filter((s) => isHttpSource(s.source));
2120
2107
  const total = httpSamples.length;
package/dist/index.d.mts CHANGED
@@ -500,6 +500,19 @@ interface GameConfig {
500
500
  acceptableBids: number[];
501
501
  minBidAmount: number;
502
502
  maxBidAmount: number;
503
+ /**
504
+ * Effective cancel policy — the values the server's cancel engine actually
505
+ * enforces (per-game config, falling back to platform defaults). Use these
506
+ * to drive the Cancel button: a bid is cancellable while
507
+ * `time2 - now > bidCancelMinSeconds`; on cancel the player is refunded
508
+ * `amount * bidCancelRefundRate`.
509
+ *
510
+ * Optional for backward compatibility: a server (or a manually-constructed
511
+ * config) that predates this field omits it. normalisePair fills 0 from the
512
+ * wire; consumers should treat `undefined`/`0` as "use the platform default".
513
+ */
514
+ bidCancelRefundRate?: number;
515
+ bidCancelMinSeconds?: number;
503
516
  }
504
517
  interface GridConfig {
505
518
  cellSizeTime: number;
@@ -1039,7 +1052,6 @@ declare class NetworkQualityMonitor {
1039
1052
  private detachBrowserListeners;
1040
1053
  private pruneWindow;
1041
1054
  private recompute;
1042
- private deriveMetrics;
1043
1055
  private deriveMetricsFrom;
1044
1056
  private isHardOffline;
1045
1057
  private allHttpAndMqttSamples;
package/dist/index.d.ts CHANGED
@@ -500,6 +500,19 @@ interface GameConfig {
500
500
  acceptableBids: number[];
501
501
  minBidAmount: number;
502
502
  maxBidAmount: number;
503
+ /**
504
+ * Effective cancel policy — the values the server's cancel engine actually
505
+ * enforces (per-game config, falling back to platform defaults). Use these
506
+ * to drive the Cancel button: a bid is cancellable while
507
+ * `time2 - now > bidCancelMinSeconds`; on cancel the player is refunded
508
+ * `amount * bidCancelRefundRate`.
509
+ *
510
+ * Optional for backward compatibility: a server (or a manually-constructed
511
+ * config) that predates this field omits it. normalisePair fills 0 from the
512
+ * wire; consumers should treat `undefined`/`0` as "use the platform default".
513
+ */
514
+ bidCancelRefundRate?: number;
515
+ bidCancelMinSeconds?: number;
503
516
  }
504
517
  interface GridConfig {
505
518
  cellSizeTime: number;
@@ -1039,7 +1052,6 @@ declare class NetworkQualityMonitor {
1039
1052
  private detachBrowserListeners;
1040
1053
  private pruneWindow;
1041
1054
  private recompute;
1042
- private deriveMetrics;
1043
1055
  private deriveMetricsFrom;
1044
1056
  private isHardOffline;
1045
1057
  private allHttpAndMqttSamples;
package/dist/index.js CHANGED
@@ -798,7 +798,15 @@ function normaliseGameConfig(node) {
798
798
  constraints: normaliseConstraints(node.constraints),
799
799
  acceptableBids: node.acceptableBids,
800
800
  minBidAmount: node.minBidAmount,
801
- maxBidAmount: node.maxBidAmount
801
+ maxBidAmount: node.maxBidAmount,
802
+ // Effective cancel policy. Distinguish three states so consumers can apply
803
+ // the right default:
804
+ // undefined → server omitted the field (older server) → use platform default
805
+ // 0 → builder explicitly disabled cancel → keep 0 (cancel OFF)
806
+ // > 0 → builder/effective threshold
807
+ // (null from the wire becomes undefined, NOT 0, so "not sent" ≠ "disabled".)
808
+ bidCancelRefundRate: node.bidCancelRefundRate ?? void 0,
809
+ bidCancelMinSeconds: node.bidCancelMinSeconds ?? void 0
802
810
  };
803
811
  }
804
812
  function normaliseGridConfig(node) {
@@ -860,6 +868,7 @@ var PAIR_QUERY = `query AgencyPair($pairId: ID!) {
860
868
  gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
861
869
  constraints { minBetTime maxBetTime slippage priceMinRange priceMaxRange coefMults maxCoef }
862
870
  acceptableBids minBidAmount maxBidAmount
871
+ bidCancelRefundRate bidCancelMinSeconds
863
872
  }
864
873
  }
865
874
  }`;
@@ -1754,31 +1763,11 @@ function classifyBackendHealth(samples) {
1754
1763
  }
1755
1764
  return "ok";
1756
1765
  }
1757
- var RTT_GOOD_TO_FAIR = 180;
1758
- var RTT_FAIR_TO_GOOD = 120;
1759
- var RTT_FAIR_TO_POOR = 450;
1760
- var RTT_POOR_TO_FAIR = 350;
1761
- var JITTER_POOR = 150;
1762
- var JITTER_LEAVE_POOR = 120;
1763
- var isPoor = (m) => m.rtt > RTT_FAIR_TO_POOR || m.jitter > JITTER_POOR;
1764
- var canLeavePoor = (m) => m.rtt < RTT_POOR_TO_FAIR && m.jitter < JITTER_LEAVE_POOR;
1765
- var isFairUpper = (rtt) => rtt > RTT_GOOD_TO_FAIR;
1766
- var canReachGood = (rtt) => rtt < RTT_FAIR_TO_GOOD;
1767
- function classifyNetworkLevel(metrics, current) {
1768
- if (current === "offline") {
1769
- return isPoor(metrics) ? "poor" : "fair";
1770
- }
1771
- if (current === "good") {
1772
- if (isPoor(metrics)) return "fair";
1773
- if (isFairUpper(metrics.rtt)) return "fair";
1774
- return "good";
1775
- }
1776
- if (current === "fair") {
1777
- if (isPoor(metrics)) return "poor";
1778
- if (canReachGood(metrics.rtt)) return "good";
1779
- return "fair";
1780
- }
1781
- if (canLeavePoor(metrics)) return "fair";
1766
+ var GOOD_MAX_MS = 150;
1767
+ var FAIR_MAX_MS = 400;
1768
+ function classifyNetworkLevel(rtt) {
1769
+ if (rtt <= GOOD_MAX_MS) return "good";
1770
+ if (rtt <= FAIR_MAX_MS) return "fair";
1782
1771
  return "poor";
1783
1772
  }
1784
1773
 
@@ -1801,24 +1790,30 @@ var RttSmoother = class {
1801
1790
  candidateCount = 0;
1802
1791
  overrideOffline = false;
1803
1792
  consecutiveOutliers = 0;
1804
- add(rtt, metrics) {
1793
+ add(rtt) {
1805
1794
  if (this.ema > 0 && rtt > OUTLIER_MULTIPLIER * this.ema) {
1806
1795
  this.consecutiveOutliers += 1;
1807
1796
  if (this.consecutiveOutliers < 2) return;
1808
1797
  } else {
1809
1798
  this.consecutiveOutliers = 0;
1810
1799
  }
1811
- this.ema = this.ema === 0 ? rtt : EMA_ALPHA * rtt + (1 - EMA_ALPHA) * this.ema;
1812
- const next = classifyNetworkLevel({ rtt, jitter: metrics.jitter }, this.committed);
1813
- if (next === this.candidate) {
1814
- this.candidateCount += 1;
1815
- } else {
1800
+ const coldStart = this.ema === 0;
1801
+ this.ema = coldStart ? rtt : EMA_ALPHA * rtt + (1 - EMA_ALPHA) * this.ema;
1802
+ const next = classifyNetworkLevel(this.ema);
1803
+ if (coldStart) {
1804
+ this.committed = next;
1816
1805
  this.candidate = next;
1817
1806
  this.candidateCount = 1;
1818
- }
1819
- const required = this.requiredSamplesFor(next);
1820
- if (this.candidateCount >= required) {
1821
- this.committed = next;
1807
+ } else {
1808
+ if (next === this.candidate) {
1809
+ this.candidateCount += 1;
1810
+ } else {
1811
+ this.candidate = next;
1812
+ this.candidateCount = 1;
1813
+ }
1814
+ if (this.candidateCount >= this.requiredSamplesFor(next)) {
1815
+ this.committed = next;
1816
+ }
1822
1817
  }
1823
1818
  this.level = this.overrideOffline ? "offline" : this.committed;
1824
1819
  }
@@ -1886,9 +1881,6 @@ var NetworkQualityMonitor = class {
1886
1881
  if (bucket.length > WINDOW_MAX_SAMPLES_PER_SOURCE) {
1887
1882
  bucket.shift();
1888
1883
  }
1889
- if (sample.source === "mqtt" && sample.reason === "ok") {
1890
- this.smoother.add(sample.rtt, this.deriveMetrics());
1891
- }
1892
1884
  this.recompute(prevMqttConnectedOverride);
1893
1885
  }
1894
1886
  seedFromConnection(seed) {
@@ -1922,6 +1914,7 @@ var NetworkQualityMonitor = class {
1922
1914
  this.recompute(prevMqttConnected);
1923
1915
  }
1924
1916
  notifyMqttPing(rttMs) {
1917
+ this.smoother.add(rttMs);
1925
1918
  this.addSample({
1926
1919
  ts: this.now(),
1927
1920
  rtt: rttMs,
@@ -2019,10 +2012,7 @@ var NetworkQualityMonitor = class {
2019
2012
  const httpSamples = effectiveSamples.filter((s) => isHttpSource(s.source));
2020
2013
  const backend = classifyBackendHealth(httpSamples);
2021
2014
  const metrics = this.deriveMetricsFrom(effectiveSamples);
2022
- const candidate = classifyNetworkLevel(
2023
- { rtt: metrics.emaForLevel, jitter: metrics.jitter },
2024
- this.committedNetwork
2025
- );
2015
+ const candidate = classifyNetworkLevel(metrics.emaForLevel);
2026
2016
  let nextNetwork = this.smoother.level;
2027
2017
  if (effectiveSamples.length === 0) {
2028
2018
  nextNetwork = this.committedNetwork;
@@ -2047,9 +2037,6 @@ var NetworkQualityMonitor = class {
2047
2037
  this.bus.emit("network:change", { previous: prev, current });
2048
2038
  }
2049
2039
  }
2050
- deriveMetrics() {
2051
- return this.deriveMetricsFrom(this.allHttpAndMqttSamples());
2052
- }
2053
2040
  deriveMetricsFrom(samples) {
2054
2041
  const httpSamples = samples.filter((s) => isHttpSource(s.source));
2055
2042
  const total = httpSamples.length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taphubhq/sdk-core",
3
- "version": "0.22.3",
3
+ "version": "0.23.1",
4
4
  "description": "Core SDK for building on the TabHub platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",