@taphubhq/sdk-core 0.15.0 → 0.15.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/index.cjs CHANGED
@@ -885,6 +885,10 @@ var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
885
885
  var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
886
886
  var TOPIC_PREFIX = "game";
887
887
  var MARKET_TOPIC_PREFIX = "market";
888
+ var WALLET_TOPIC_PREFIX = "wallet";
889
+ function walletBalanceTopic(userId) {
890
+ return `${WALLET_TOPIC_PREFIX}/users/${userId}/balance`;
891
+ }
888
892
  function agencyPairStatsTopic(aid, gamePairId) {
889
893
  return `public/agency/${aid}/pair/${gamePairId}/stats`;
890
894
  }
@@ -905,6 +909,7 @@ function createMqttTransport(endpoint, opts = {}) {
905
909
  const subscriptions = /* @__PURE__ */ new Map();
906
910
  const candleSubscriptions = /* @__PURE__ */ new Map();
907
911
  const statsSubscriptions = /* @__PURE__ */ new Map();
912
+ const walletSubscriptions = /* @__PURE__ */ new Map();
908
913
  const { onLifecycle } = opts;
909
914
  let connectStartedAt = 0;
910
915
  function fireLifecycle(event) {
@@ -973,6 +978,18 @@ function createMqttTransport(endpoint, opts = {}) {
973
978
  statsSub.onStats(receivedTopic, payload2);
974
979
  return;
975
980
  }
981
+ const walletSub = [...walletSubscriptions.values()].find((s) => s.topic === receivedTopic);
982
+ if (walletSub) {
983
+ let payload2;
984
+ try {
985
+ payload2 = JSON.parse(message.toString());
986
+ } catch {
987
+ walletSub.onError(new Error(`Invalid JSON on topic ${receivedTopic}`));
988
+ return;
989
+ }
990
+ walletSub.onMessage(receivedTopic, payload2);
991
+ return;
992
+ }
976
993
  let matched;
977
994
  for (const sub of subscriptions.values()) {
978
995
  if (sub.topics.includes(receivedTopic)) {
@@ -1047,6 +1064,19 @@ function createMqttTransport(endpoint, opts = {}) {
1047
1064
  if (client) client.unsubscribe(entry.topic);
1048
1065
  statsSubscriptions.delete(topic);
1049
1066
  },
1067
+ subscribeWallet(userId, onMessage, onError) {
1068
+ if (walletSubscriptions.has(userId)) return;
1069
+ const topic = walletBalanceTopic(userId);
1070
+ const mqttClient = ensureConnected();
1071
+ walletSubscriptions.set(userId, { topic, onMessage, onError });
1072
+ mqttClient.subscribe(topic, { qos: 0 });
1073
+ },
1074
+ unsubscribeWallet(userId) {
1075
+ const entry = walletSubscriptions.get(userId);
1076
+ if (!entry) return;
1077
+ if (client) client.unsubscribe(entry.topic);
1078
+ walletSubscriptions.delete(userId);
1079
+ },
1050
1080
  unsubscribeAll(gameId, userId) {
1051
1081
  const matches = entriesForGame(gameId);
1052
1082
  if (matches.length === 0) return;
@@ -1084,10 +1114,14 @@ function createMqttTransport(endpoint, opts = {}) {
1084
1114
  for (const entry of statsSubscriptions.values()) {
1085
1115
  client.unsubscribe(entry.topic);
1086
1116
  }
1117
+ for (const entry of walletSubscriptions.values()) {
1118
+ client.unsubscribe(entry.topic);
1119
+ }
1087
1120
  }
1088
1121
  subscriptions.clear();
1089
1122
  candleSubscriptions.clear();
1090
1123
  statsSubscriptions.clear();
1124
+ walletSubscriptions.clear();
1091
1125
  if (client) {
1092
1126
  client.end(true);
1093
1127
  client = null;
@@ -1097,7 +1131,7 @@ function createMqttTransport(endpoint, opts = {}) {
1097
1131
  }
1098
1132
 
1099
1133
  // src/modules/realtime/index.ts
1100
- var import_eventemitter32 = __toESM(require("eventemitter3"));
1134
+ var import_eventemitter33 = __toESM(require("eventemitter3"));
1101
1135
 
1102
1136
  // src/modules/realtime/GameChannel.ts
1103
1137
  var import_eventemitter3 = __toESM(require("eventemitter3"));
@@ -1109,6 +1143,16 @@ var GameChannel = class extends import_eventemitter3.default {
1109
1143
  }
1110
1144
  };
1111
1145
 
1146
+ // src/modules/realtime/WalletChannel.ts
1147
+ var import_eventemitter32 = __toESM(require("eventemitter3"));
1148
+ var WalletChannel = class extends import_eventemitter32.default {
1149
+ userId;
1150
+ constructor(userId) {
1151
+ super();
1152
+ this.userId = userId;
1153
+ }
1154
+ };
1155
+
1112
1156
  // src/modules/realtime/index.ts
1113
1157
  var TOPIC_SUFFIX_CANDLE = "candle";
1114
1158
  var TOPIC_SUFFIX_BID_RESULT = "bid_result";
@@ -1168,6 +1212,16 @@ function mapWireBalanceUpdate(raw) {
1168
1212
  balance: p.balance
1169
1213
  };
1170
1214
  }
1215
+ function mapWireWalletBalance(raw) {
1216
+ const p = raw;
1217
+ return {
1218
+ id: p.id,
1219
+ balance: p.balance,
1220
+ reservedAmount: p.reserved_amount,
1221
+ currency: p.currency,
1222
+ reason: p.reason ?? ""
1223
+ };
1224
+ }
1171
1225
  function mapWireConfig(raw) {
1172
1226
  const p = raw;
1173
1227
  return {
@@ -1205,9 +1259,11 @@ function mapWireToEvent(topic, payload) {
1205
1259
  function normaliseUserId2(userId) {
1206
1260
  return userId && userId !== "" ? userId : null;
1207
1261
  }
1208
- var RealtimeModule = class extends import_eventemitter32.default {
1262
+ var RealtimeModule = class extends import_eventemitter33.default {
1209
1263
  #transport;
1210
1264
  #entries = /* @__PURE__ */ new Map();
1265
+ #walletEntries = /* @__PURE__ */ new Map();
1266
+ // keyed by userId
1211
1267
  #agencyId;
1212
1268
  constructor(mqttEndpointOrOptions) {
1213
1269
  super();
@@ -1287,6 +1343,56 @@ var RealtimeModule = class extends import_eventemitter32.default {
1287
1343
  target.channel.removeAllListeners();
1288
1344
  this.#transport.unsubscribeAll(gameId, target.userId);
1289
1345
  }
1346
+ /**
1347
+ * Subscribe to the user-scoped wallet balance stream for `userId`, topic
1348
+ * `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
1349
+ * `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
1350
+ * reserve / commit / cancel / bid settlement) — independent of any game.
1351
+ *
1352
+ * Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
1353
+ * calls return the same channel and the topic is torn down only when balanced
1354
+ * `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
1355
+ * fully independent of game subscriptions.
1356
+ */
1357
+ subscribeWallet(userId) {
1358
+ const cleanUserId = normaliseUserId2(userId);
1359
+ if (!cleanUserId) {
1360
+ throw new TaphubError("userId is required to subscribe to wallet balance", {
1361
+ code: "UserIdRequired"
1362
+ });
1363
+ }
1364
+ const existing = this.#walletEntries.get(cleanUserId);
1365
+ if (existing) {
1366
+ existing.refcount += 1;
1367
+ return existing.channel;
1368
+ }
1369
+ const channel = new WalletChannel(cleanUserId);
1370
+ const onMessage = (_topic, payload) => {
1371
+ channel.emit("walletBalanceUpdate", mapWireWalletBalance(payload));
1372
+ };
1373
+ const onError = (err) => {
1374
+ channel.emit("error", err);
1375
+ };
1376
+ this.#walletEntries.set(cleanUserId, { userId: cleanUserId, channel, refcount: 1 });
1377
+ this.#transport.subscribeWallet(cleanUserId, onMessage, onError);
1378
+ return channel;
1379
+ }
1380
+ /**
1381
+ * Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
1382
+ * topic and removes the `WalletChannel` only when the count reaches zero.
1383
+ * No-op if there is no active wallet subscription for `userId`.
1384
+ */
1385
+ unsubscribeWallet(userId) {
1386
+ const cleanUserId = normaliseUserId2(userId);
1387
+ if (!cleanUserId) return;
1388
+ const entry = this.#walletEntries.get(cleanUserId);
1389
+ if (!entry) return;
1390
+ entry.refcount -= 1;
1391
+ if (entry.refcount > 0) return;
1392
+ this.#walletEntries.delete(cleanUserId);
1393
+ entry.channel.removeAllListeners();
1394
+ this.#transport.unsubscribeWallet(cleanUserId);
1395
+ }
1290
1396
  /**
1291
1397
  * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
1292
1398
  * Independent of game/agency — candle is market-wide public data.
@@ -1330,6 +1436,10 @@ var RealtimeModule = class extends import_eventemitter32.default {
1330
1436
  entry.channel.removeAllListeners();
1331
1437
  }
1332
1438
  this.#entries.clear();
1439
+ for (const entry of this.#walletEntries.values()) {
1440
+ entry.channel.removeAllListeners();
1441
+ }
1442
+ this.#walletEntries.clear();
1333
1443
  this.#transport.close();
1334
1444
  }
1335
1445
  };
@@ -1398,11 +1508,30 @@ function normaliseMyWalletByCurrencyResponse(body) {
1398
1508
  }
1399
1509
  return normaliseWalletNode(body.myWalletByCurrency);
1400
1510
  }
1511
+ function normaliseUserPnL(node) {
1512
+ if (!node) {
1513
+ throw new TaphubServerError("Invalid response from server", {
1514
+ code: "InvalidResponse"
1515
+ });
1516
+ }
1517
+ return {
1518
+ gain: node.gain,
1519
+ totalWagered: node.total_wagered,
1520
+ totalPayout: node.total_payout,
1521
+ totalBids: node.total_bids,
1522
+ totalWins: node.total_wins
1523
+ };
1524
+ }
1401
1525
 
1402
1526
  // src/modules/user/queries.ts
1403
1527
  var ME_QUERY = "query Me { me { id username wallet_address balance is_demo } }";
1404
1528
  var MY_WALLETS_QUERY = "query MyWallets { myWallets { id amount currency isEnable } }";
1405
1529
  var MY_WALLET_BY_CURRENCY_QUERY = "query MyWalletByCurrency($currency: String!) { myWalletByCurrency(currency: $currency) { id amount currency isEnable } }";
1530
+ var USER_PNL_QUERY = `query UserPnL($period: String) {
1531
+ userPnL(period: $period) {
1532
+ gain total_wagered total_payout total_bids total_wins
1533
+ }
1534
+ }`;
1406
1535
 
1407
1536
  // src/modules/user/index.ts
1408
1537
  var UserModule = class {
@@ -1419,6 +1548,20 @@ var UserModule = class {
1419
1548
  const body = await this.#graphql.request(ME_QUERY);
1420
1549
  return normaliseMeResponse(body);
1421
1550
  }
1551
+ /**
1552
+ * Fetch the authenticated user's realized PnL (grid-api).
1553
+ * `period` defaults to 'all_time' ('weekly' also accepted). Agency scope is
1554
+ * resolved server-side from the JWT — never passed from the client.
1555
+ */
1556
+ async pnl(args) {
1557
+ const period = args?.period ?? "all_time";
1558
+ const body = await this.#graphql.request(
1559
+ USER_PNL_QUERY,
1560
+ { period },
1561
+ args?.signal ? { signal: args.signal } : void 0
1562
+ );
1563
+ return normaliseUserPnL(body.userPnL);
1564
+ }
1422
1565
  async wallets(opts) {
1423
1566
  const body = await this.#graphqlUser.request(
1424
1567
  MY_WALLETS_QUERY,
package/dist/index.d.mts CHANGED
@@ -482,7 +482,19 @@ interface MqttWireAgencyPairStats {
482
482
  maxCoef: number;
483
483
  ts: number;
484
484
  }
485
- type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats;
485
+ /**
486
+ * Wire payload on the user-scoped wallet topic `wallet/users/{userId}/balance`,
487
+ * published by `taphub-user-service` (`streams/wallet.go`). All fields are JSON
488
+ * strings. `reserved_amount` is snake_case on the wire.
489
+ */
490
+ interface MqttWireWalletBalance {
491
+ id: string;
492
+ balance: string;
493
+ reserved_amount: string;
494
+ currency: string;
495
+ reason: string;
496
+ }
497
+ type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats | MqttWireWalletBalance;
486
498
  type MqttMessageHandler = (gameId: string, topic: string, payload: MqttWirePayload) => void;
487
499
  type MqttErrorHandler = (err: Error) => void;
488
500
  type MqttLifecycleEvent = {
@@ -503,6 +515,7 @@ type MqttLifecycleEvent = {
503
515
  type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
504
516
  type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
505
517
  type MqttAgencyPairStatsHandler = (topic: string, payload: MqttWirePayload) => void;
518
+ type MqttWalletMessageHandler = (topic: string, payload: MqttWirePayload) => void;
506
519
  interface MqttTransport {
507
520
  /**
508
521
  * Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
@@ -536,6 +549,15 @@ interface MqttTransport {
536
549
  subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
537
550
  /** Tear down the stats subscription for an `(aid, gamePairId)`. */
538
551
  unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
552
+ /**
553
+ * Subscribe to the user-scoped wallet balance stream, topic
554
+ * `wallet/users/{userId}/balance`. Independent of any game subscription — NOT
555
+ * torn down by `unsubscribeAll`. Idempotent: repeat calls for the same
556
+ * `userId` are deduplicated. Subscribed at QoS 0 to match the publisher.
557
+ */
558
+ subscribeWallet(userId: string, onMessage: MqttWalletMessageHandler, onError: MqttErrorHandler): void;
559
+ /** Tear down the wallet subscription for a `userId`. No-op if not subscribed. */
560
+ unsubscribeWallet(userId: string): void;
539
561
  close(): void;
540
562
  }
541
563
 
@@ -598,6 +620,36 @@ interface MqttBalanceEvent {
598
620
  userId: string;
599
621
  balance: string;
600
622
  }
623
+ /**
624
+ * Cause of a wallet balance change. Byte-mirrors `BalanceUpdateReason` in the
625
+ * backend (`taphub-user-service` `streams/wallet.go`). `''` is the documented
626
+ * unspecified/legacy value. Treat unknown reasons defensively (enum fallback) —
627
+ * the SDK passes through values it does not recognise rather than dropping the
628
+ * event.
629
+ */
630
+ type WalletBalanceReason = 'transfer' | 'refund' | 'reserve' | 'commit' | 'cancel' | 'bid_placed' | 'bid_won' | '';
631
+ /**
632
+ * Wallet-level balance event from the user-scoped MQTT topic
633
+ * `wallet/users/{userId}/balance`. Fires on deposit / agency transfer / refund /
634
+ * reserve / commit / cancel / bid settlement — i.e. every balance change, with
635
+ * NO game context (unlike the game-scoped `MqttBalanceEvent`).
636
+ *
637
+ * Money fields (`balance`, `reservedAmount`) are kept as wire strings verbatim;
638
+ * the SDK does NOT coerce decimal money to `number`. Consumers coerce at the
639
+ * app edge if they need a number.
640
+ */
641
+ interface MqttWalletBalanceEvent {
642
+ /** Wallet id (the backend `id` field). */
643
+ id: string;
644
+ /** Spendable balance, as the raw wire string. */
645
+ balance: string;
646
+ /** Amount held by PENDING reservations, as the raw wire string. */
647
+ reservedAmount: string;
648
+ /** Wallet currency (e.g. "USDC.e"). */
649
+ currency: string;
650
+ /** Cause of the change; unknown values are passed through as-is. */
651
+ reason: WalletBalanceReason;
652
+ }
601
653
  interface MqttConfigEvent {
602
654
  minBidAmount: number;
603
655
  maxBidAmount: number;
@@ -638,6 +690,20 @@ declare class GameChannel extends EventEmitter<GameChannelEvents> {
638
690
  constructor(gameId: string);
639
691
  }
640
692
 
693
+ interface WalletChannelEvents {
694
+ walletBalanceUpdate: [MqttWalletBalanceEvent];
695
+ error: [Error];
696
+ }
697
+ /**
698
+ * User-scoped wallet channel. Emits `walletBalanceUpdate` for every balance
699
+ * change on `wallet/users/{userId}/balance`, independent of any game. Mirror of
700
+ * {@link GameChannel} but keyed by `userId` rather than `gameId`.
701
+ */
702
+ declare class WalletChannel extends EventEmitter<WalletChannelEvents> {
703
+ readonly userId: string;
704
+ constructor(userId: string);
705
+ }
706
+
641
707
  interface RealtimeModuleOptions {
642
708
  mqttEndpoint: string;
643
709
  /**
@@ -665,6 +731,24 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
665
731
  get _transport(): MqttTransport;
666
732
  subscribe(gameId: string, userId?: string | null): GameChannel;
667
733
  unsubscribe(gameId: string, userId?: string | null): void;
734
+ /**
735
+ * Subscribe to the user-scoped wallet balance stream for `userId`, topic
736
+ * `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
737
+ * `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
738
+ * reserve / commit / cancel / bid settlement) — independent of any game.
739
+ *
740
+ * Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
741
+ * calls return the same channel and the topic is torn down only when balanced
742
+ * `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
743
+ * fully independent of game subscriptions.
744
+ */
745
+ subscribeWallet(userId: string): WalletChannel;
746
+ /**
747
+ * Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
748
+ * topic and removes the `WalletChannel` only when the count reaches zero.
749
+ * No-op if there is no active wallet subscription for `userId`.
750
+ */
751
+ unsubscribeWallet(userId: string): void;
668
752
  /**
669
753
  * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
670
754
  * Independent of game/agency — candle is market-wide public data.
@@ -701,6 +785,19 @@ interface Wallet {
701
785
  currency: string;
702
786
  isEnable: boolean;
703
787
  }
788
+ type UserPnLPeriod = 'weekly' | 'all_time';
789
+ /**
790
+ * Public, normalised PnL shape (camelCase). The wire shape is the codegen'd
791
+ * `UserPnL` from `@/types/gql/grid`; `normaliseUserPnL` maps wire → this.
792
+ */
793
+ interface UserPnL {
794
+ /** Realized PnL: totalPayout − totalWagered. Decimal string. */
795
+ gain: string;
796
+ totalWagered: string;
797
+ totalPayout: string;
798
+ totalBids: number;
799
+ totalWins: number;
800
+ }
704
801
 
705
802
  interface UserModuleDeps {
706
803
  rest: RestTransport;
@@ -714,6 +811,15 @@ declare class UserModule {
714
811
  #private;
715
812
  constructor(deps: UserModuleDeps);
716
813
  me(): Promise<Me>;
814
+ /**
815
+ * Fetch the authenticated user's realized PnL (grid-api).
816
+ * `period` defaults to 'all_time' ('weekly' also accepted). Agency scope is
817
+ * resolved server-side from the JWT — never passed from the client.
818
+ */
819
+ pnl(args?: {
820
+ period?: UserPnLPeriod;
821
+ signal?: AbortSignal;
822
+ }): Promise<UserPnL>;
717
823
  wallets(opts?: RequestOpts): Promise<Wallet[]>;
718
824
  walletByCurrency(currency: string, opts?: RequestOpts): Promise<Wallet>;
719
825
  get currencies(): Currency[] | null;
@@ -879,4 +985,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
879
985
  declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
880
986
  declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
881
987
 
882
- export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type Wallet as UserWallet, type Wallet$1 as Wallet, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
988
+ export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
package/dist/index.d.ts CHANGED
@@ -482,7 +482,19 @@ interface MqttWireAgencyPairStats {
482
482
  maxCoef: number;
483
483
  ts: number;
484
484
  }
485
- type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats;
485
+ /**
486
+ * Wire payload on the user-scoped wallet topic `wallet/users/{userId}/balance`,
487
+ * published by `taphub-user-service` (`streams/wallet.go`). All fields are JSON
488
+ * strings. `reserved_amount` is snake_case on the wire.
489
+ */
490
+ interface MqttWireWalletBalance {
491
+ id: string;
492
+ balance: string;
493
+ reserved_amount: string;
494
+ currency: string;
495
+ reason: string;
496
+ }
497
+ type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats | MqttWireWalletBalance;
486
498
  type MqttMessageHandler = (gameId: string, topic: string, payload: MqttWirePayload) => void;
487
499
  type MqttErrorHandler = (err: Error) => void;
488
500
  type MqttLifecycleEvent = {
@@ -503,6 +515,7 @@ type MqttLifecycleEvent = {
503
515
  type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
504
516
  type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
505
517
  type MqttAgencyPairStatsHandler = (topic: string, payload: MqttWirePayload) => void;
518
+ type MqttWalletMessageHandler = (topic: string, payload: MqttWirePayload) => void;
506
519
  interface MqttTransport {
507
520
  /**
508
521
  * Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
@@ -536,6 +549,15 @@ interface MqttTransport {
536
549
  subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
537
550
  /** Tear down the stats subscription for an `(aid, gamePairId)`. */
538
551
  unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
552
+ /**
553
+ * Subscribe to the user-scoped wallet balance stream, topic
554
+ * `wallet/users/{userId}/balance`. Independent of any game subscription — NOT
555
+ * torn down by `unsubscribeAll`. Idempotent: repeat calls for the same
556
+ * `userId` are deduplicated. Subscribed at QoS 0 to match the publisher.
557
+ */
558
+ subscribeWallet(userId: string, onMessage: MqttWalletMessageHandler, onError: MqttErrorHandler): void;
559
+ /** Tear down the wallet subscription for a `userId`. No-op if not subscribed. */
560
+ unsubscribeWallet(userId: string): void;
539
561
  close(): void;
540
562
  }
541
563
 
@@ -598,6 +620,36 @@ interface MqttBalanceEvent {
598
620
  userId: string;
599
621
  balance: string;
600
622
  }
623
+ /**
624
+ * Cause of a wallet balance change. Byte-mirrors `BalanceUpdateReason` in the
625
+ * backend (`taphub-user-service` `streams/wallet.go`). `''` is the documented
626
+ * unspecified/legacy value. Treat unknown reasons defensively (enum fallback) —
627
+ * the SDK passes through values it does not recognise rather than dropping the
628
+ * event.
629
+ */
630
+ type WalletBalanceReason = 'transfer' | 'refund' | 'reserve' | 'commit' | 'cancel' | 'bid_placed' | 'bid_won' | '';
631
+ /**
632
+ * Wallet-level balance event from the user-scoped MQTT topic
633
+ * `wallet/users/{userId}/balance`. Fires on deposit / agency transfer / refund /
634
+ * reserve / commit / cancel / bid settlement — i.e. every balance change, with
635
+ * NO game context (unlike the game-scoped `MqttBalanceEvent`).
636
+ *
637
+ * Money fields (`balance`, `reservedAmount`) are kept as wire strings verbatim;
638
+ * the SDK does NOT coerce decimal money to `number`. Consumers coerce at the
639
+ * app edge if they need a number.
640
+ */
641
+ interface MqttWalletBalanceEvent {
642
+ /** Wallet id (the backend `id` field). */
643
+ id: string;
644
+ /** Spendable balance, as the raw wire string. */
645
+ balance: string;
646
+ /** Amount held by PENDING reservations, as the raw wire string. */
647
+ reservedAmount: string;
648
+ /** Wallet currency (e.g. "USDC.e"). */
649
+ currency: string;
650
+ /** Cause of the change; unknown values are passed through as-is. */
651
+ reason: WalletBalanceReason;
652
+ }
601
653
  interface MqttConfigEvent {
602
654
  minBidAmount: number;
603
655
  maxBidAmount: number;
@@ -638,6 +690,20 @@ declare class GameChannel extends EventEmitter<GameChannelEvents> {
638
690
  constructor(gameId: string);
639
691
  }
640
692
 
693
+ interface WalletChannelEvents {
694
+ walletBalanceUpdate: [MqttWalletBalanceEvent];
695
+ error: [Error];
696
+ }
697
+ /**
698
+ * User-scoped wallet channel. Emits `walletBalanceUpdate` for every balance
699
+ * change on `wallet/users/{userId}/balance`, independent of any game. Mirror of
700
+ * {@link GameChannel} but keyed by `userId` rather than `gameId`.
701
+ */
702
+ declare class WalletChannel extends EventEmitter<WalletChannelEvents> {
703
+ readonly userId: string;
704
+ constructor(userId: string);
705
+ }
706
+
641
707
  interface RealtimeModuleOptions {
642
708
  mqttEndpoint: string;
643
709
  /**
@@ -665,6 +731,24 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
665
731
  get _transport(): MqttTransport;
666
732
  subscribe(gameId: string, userId?: string | null): GameChannel;
667
733
  unsubscribe(gameId: string, userId?: string | null): void;
734
+ /**
735
+ * Subscribe to the user-scoped wallet balance stream for `userId`, topic
736
+ * `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
737
+ * `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
738
+ * reserve / commit / cancel / bid settlement) — independent of any game.
739
+ *
740
+ * Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
741
+ * calls return the same channel and the topic is torn down only when balanced
742
+ * `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
743
+ * fully independent of game subscriptions.
744
+ */
745
+ subscribeWallet(userId: string): WalletChannel;
746
+ /**
747
+ * Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
748
+ * topic and removes the `WalletChannel` only when the count reaches zero.
749
+ * No-op if there is no active wallet subscription for `userId`.
750
+ */
751
+ unsubscribeWallet(userId: string): void;
668
752
  /**
669
753
  * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
670
754
  * Independent of game/agency — candle is market-wide public data.
@@ -701,6 +785,19 @@ interface Wallet {
701
785
  currency: string;
702
786
  isEnable: boolean;
703
787
  }
788
+ type UserPnLPeriod = 'weekly' | 'all_time';
789
+ /**
790
+ * Public, normalised PnL shape (camelCase). The wire shape is the codegen'd
791
+ * `UserPnL` from `@/types/gql/grid`; `normaliseUserPnL` maps wire → this.
792
+ */
793
+ interface UserPnL {
794
+ /** Realized PnL: totalPayout − totalWagered. Decimal string. */
795
+ gain: string;
796
+ totalWagered: string;
797
+ totalPayout: string;
798
+ totalBids: number;
799
+ totalWins: number;
800
+ }
704
801
 
705
802
  interface UserModuleDeps {
706
803
  rest: RestTransport;
@@ -714,6 +811,15 @@ declare class UserModule {
714
811
  #private;
715
812
  constructor(deps: UserModuleDeps);
716
813
  me(): Promise<Me>;
814
+ /**
815
+ * Fetch the authenticated user's realized PnL (grid-api).
816
+ * `period` defaults to 'all_time' ('weekly' also accepted). Agency scope is
817
+ * resolved server-side from the JWT — never passed from the client.
818
+ */
819
+ pnl(args?: {
820
+ period?: UserPnLPeriod;
821
+ signal?: AbortSignal;
822
+ }): Promise<UserPnL>;
717
823
  wallets(opts?: RequestOpts): Promise<Wallet[]>;
718
824
  walletByCurrency(currency: string, opts?: RequestOpts): Promise<Wallet>;
719
825
  get currencies(): Currency[] | null;
@@ -879,4 +985,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
879
985
  declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
880
986
  declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
881
987
 
882
- export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type Wallet as UserWallet, type Wallet$1 as Wallet, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
988
+ export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
package/dist/index.js CHANGED
@@ -820,6 +820,10 @@ var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
820
820
  var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
821
821
  var TOPIC_PREFIX = "game";
822
822
  var MARKET_TOPIC_PREFIX = "market";
823
+ var WALLET_TOPIC_PREFIX = "wallet";
824
+ function walletBalanceTopic(userId) {
825
+ return `${WALLET_TOPIC_PREFIX}/users/${userId}/balance`;
826
+ }
823
827
  function agencyPairStatsTopic(aid, gamePairId) {
824
828
  return `public/agency/${aid}/pair/${gamePairId}/stats`;
825
829
  }
@@ -840,6 +844,7 @@ function createMqttTransport(endpoint, opts = {}) {
840
844
  const subscriptions = /* @__PURE__ */ new Map();
841
845
  const candleSubscriptions = /* @__PURE__ */ new Map();
842
846
  const statsSubscriptions = /* @__PURE__ */ new Map();
847
+ const walletSubscriptions = /* @__PURE__ */ new Map();
843
848
  const { onLifecycle } = opts;
844
849
  let connectStartedAt = 0;
845
850
  function fireLifecycle(event) {
@@ -908,6 +913,18 @@ function createMqttTransport(endpoint, opts = {}) {
908
913
  statsSub.onStats(receivedTopic, payload2);
909
914
  return;
910
915
  }
916
+ const walletSub = [...walletSubscriptions.values()].find((s) => s.topic === receivedTopic);
917
+ if (walletSub) {
918
+ let payload2;
919
+ try {
920
+ payload2 = JSON.parse(message.toString());
921
+ } catch {
922
+ walletSub.onError(new Error(`Invalid JSON on topic ${receivedTopic}`));
923
+ return;
924
+ }
925
+ walletSub.onMessage(receivedTopic, payload2);
926
+ return;
927
+ }
911
928
  let matched;
912
929
  for (const sub of subscriptions.values()) {
913
930
  if (sub.topics.includes(receivedTopic)) {
@@ -982,6 +999,19 @@ function createMqttTransport(endpoint, opts = {}) {
982
999
  if (client) client.unsubscribe(entry.topic);
983
1000
  statsSubscriptions.delete(topic);
984
1001
  },
1002
+ subscribeWallet(userId, onMessage, onError) {
1003
+ if (walletSubscriptions.has(userId)) return;
1004
+ const topic = walletBalanceTopic(userId);
1005
+ const mqttClient = ensureConnected();
1006
+ walletSubscriptions.set(userId, { topic, onMessage, onError });
1007
+ mqttClient.subscribe(topic, { qos: 0 });
1008
+ },
1009
+ unsubscribeWallet(userId) {
1010
+ const entry = walletSubscriptions.get(userId);
1011
+ if (!entry) return;
1012
+ if (client) client.unsubscribe(entry.topic);
1013
+ walletSubscriptions.delete(userId);
1014
+ },
985
1015
  unsubscribeAll(gameId, userId) {
986
1016
  const matches = entriesForGame(gameId);
987
1017
  if (matches.length === 0) return;
@@ -1019,10 +1049,14 @@ function createMqttTransport(endpoint, opts = {}) {
1019
1049
  for (const entry of statsSubscriptions.values()) {
1020
1050
  client.unsubscribe(entry.topic);
1021
1051
  }
1052
+ for (const entry of walletSubscriptions.values()) {
1053
+ client.unsubscribe(entry.topic);
1054
+ }
1022
1055
  }
1023
1056
  subscriptions.clear();
1024
1057
  candleSubscriptions.clear();
1025
1058
  statsSubscriptions.clear();
1059
+ walletSubscriptions.clear();
1026
1060
  if (client) {
1027
1061
  client.end(true);
1028
1062
  client = null;
@@ -1032,7 +1066,7 @@ function createMqttTransport(endpoint, opts = {}) {
1032
1066
  }
1033
1067
 
1034
1068
  // src/modules/realtime/index.ts
1035
- import EventEmitter2 from "eventemitter3";
1069
+ import EventEmitter3 from "eventemitter3";
1036
1070
 
1037
1071
  // src/modules/realtime/GameChannel.ts
1038
1072
  import EventEmitter from "eventemitter3";
@@ -1044,6 +1078,16 @@ var GameChannel = class extends EventEmitter {
1044
1078
  }
1045
1079
  };
1046
1080
 
1081
+ // src/modules/realtime/WalletChannel.ts
1082
+ import EventEmitter2 from "eventemitter3";
1083
+ var WalletChannel = class extends EventEmitter2 {
1084
+ userId;
1085
+ constructor(userId) {
1086
+ super();
1087
+ this.userId = userId;
1088
+ }
1089
+ };
1090
+
1047
1091
  // src/modules/realtime/index.ts
1048
1092
  var TOPIC_SUFFIX_CANDLE = "candle";
1049
1093
  var TOPIC_SUFFIX_BID_RESULT = "bid_result";
@@ -1103,6 +1147,16 @@ function mapWireBalanceUpdate(raw) {
1103
1147
  balance: p.balance
1104
1148
  };
1105
1149
  }
1150
+ function mapWireWalletBalance(raw) {
1151
+ const p = raw;
1152
+ return {
1153
+ id: p.id,
1154
+ balance: p.balance,
1155
+ reservedAmount: p.reserved_amount,
1156
+ currency: p.currency,
1157
+ reason: p.reason ?? ""
1158
+ };
1159
+ }
1106
1160
  function mapWireConfig(raw) {
1107
1161
  const p = raw;
1108
1162
  return {
@@ -1140,9 +1194,11 @@ function mapWireToEvent(topic, payload) {
1140
1194
  function normaliseUserId2(userId) {
1141
1195
  return userId && userId !== "" ? userId : null;
1142
1196
  }
1143
- var RealtimeModule = class extends EventEmitter2 {
1197
+ var RealtimeModule = class extends EventEmitter3 {
1144
1198
  #transport;
1145
1199
  #entries = /* @__PURE__ */ new Map();
1200
+ #walletEntries = /* @__PURE__ */ new Map();
1201
+ // keyed by userId
1146
1202
  #agencyId;
1147
1203
  constructor(mqttEndpointOrOptions) {
1148
1204
  super();
@@ -1222,6 +1278,56 @@ var RealtimeModule = class extends EventEmitter2 {
1222
1278
  target.channel.removeAllListeners();
1223
1279
  this.#transport.unsubscribeAll(gameId, target.userId);
1224
1280
  }
1281
+ /**
1282
+ * Subscribe to the user-scoped wallet balance stream for `userId`, topic
1283
+ * `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
1284
+ * `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
1285
+ * reserve / commit / cancel / bid settlement) — independent of any game.
1286
+ *
1287
+ * Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
1288
+ * calls return the same channel and the topic is torn down only when balanced
1289
+ * `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
1290
+ * fully independent of game subscriptions.
1291
+ */
1292
+ subscribeWallet(userId) {
1293
+ const cleanUserId = normaliseUserId2(userId);
1294
+ if (!cleanUserId) {
1295
+ throw new TaphubError("userId is required to subscribe to wallet balance", {
1296
+ code: "UserIdRequired"
1297
+ });
1298
+ }
1299
+ const existing = this.#walletEntries.get(cleanUserId);
1300
+ if (existing) {
1301
+ existing.refcount += 1;
1302
+ return existing.channel;
1303
+ }
1304
+ const channel = new WalletChannel(cleanUserId);
1305
+ const onMessage = (_topic, payload) => {
1306
+ channel.emit("walletBalanceUpdate", mapWireWalletBalance(payload));
1307
+ };
1308
+ const onError = (err) => {
1309
+ channel.emit("error", err);
1310
+ };
1311
+ this.#walletEntries.set(cleanUserId, { userId: cleanUserId, channel, refcount: 1 });
1312
+ this.#transport.subscribeWallet(cleanUserId, onMessage, onError);
1313
+ return channel;
1314
+ }
1315
+ /**
1316
+ * Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
1317
+ * topic and removes the `WalletChannel` only when the count reaches zero.
1318
+ * No-op if there is no active wallet subscription for `userId`.
1319
+ */
1320
+ unsubscribeWallet(userId) {
1321
+ const cleanUserId = normaliseUserId2(userId);
1322
+ if (!cleanUserId) return;
1323
+ const entry = this.#walletEntries.get(cleanUserId);
1324
+ if (!entry) return;
1325
+ entry.refcount -= 1;
1326
+ if (entry.refcount > 0) return;
1327
+ this.#walletEntries.delete(cleanUserId);
1328
+ entry.channel.removeAllListeners();
1329
+ this.#transport.unsubscribeWallet(cleanUserId);
1330
+ }
1225
1331
  /**
1226
1332
  * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
1227
1333
  * Independent of game/agency — candle is market-wide public data.
@@ -1265,6 +1371,10 @@ var RealtimeModule = class extends EventEmitter2 {
1265
1371
  entry.channel.removeAllListeners();
1266
1372
  }
1267
1373
  this.#entries.clear();
1374
+ for (const entry of this.#walletEntries.values()) {
1375
+ entry.channel.removeAllListeners();
1376
+ }
1377
+ this.#walletEntries.clear();
1268
1378
  this.#transport.close();
1269
1379
  }
1270
1380
  };
@@ -1333,11 +1443,30 @@ function normaliseMyWalletByCurrencyResponse(body) {
1333
1443
  }
1334
1444
  return normaliseWalletNode(body.myWalletByCurrency);
1335
1445
  }
1446
+ function normaliseUserPnL(node) {
1447
+ if (!node) {
1448
+ throw new TaphubServerError("Invalid response from server", {
1449
+ code: "InvalidResponse"
1450
+ });
1451
+ }
1452
+ return {
1453
+ gain: node.gain,
1454
+ totalWagered: node.total_wagered,
1455
+ totalPayout: node.total_payout,
1456
+ totalBids: node.total_bids,
1457
+ totalWins: node.total_wins
1458
+ };
1459
+ }
1336
1460
 
1337
1461
  // src/modules/user/queries.ts
1338
1462
  var ME_QUERY = "query Me { me { id username wallet_address balance is_demo } }";
1339
1463
  var MY_WALLETS_QUERY = "query MyWallets { myWallets { id amount currency isEnable } }";
1340
1464
  var MY_WALLET_BY_CURRENCY_QUERY = "query MyWalletByCurrency($currency: String!) { myWalletByCurrency(currency: $currency) { id amount currency isEnable } }";
1465
+ var USER_PNL_QUERY = `query UserPnL($period: String) {
1466
+ userPnL(period: $period) {
1467
+ gain total_wagered total_payout total_bids total_wins
1468
+ }
1469
+ }`;
1341
1470
 
1342
1471
  // src/modules/user/index.ts
1343
1472
  var UserModule = class {
@@ -1354,6 +1483,20 @@ var UserModule = class {
1354
1483
  const body = await this.#graphql.request(ME_QUERY);
1355
1484
  return normaliseMeResponse(body);
1356
1485
  }
1486
+ /**
1487
+ * Fetch the authenticated user's realized PnL (grid-api).
1488
+ * `period` defaults to 'all_time' ('weekly' also accepted). Agency scope is
1489
+ * resolved server-side from the JWT — never passed from the client.
1490
+ */
1491
+ async pnl(args) {
1492
+ const period = args?.period ?? "all_time";
1493
+ const body = await this.#graphql.request(
1494
+ USER_PNL_QUERY,
1495
+ { period },
1496
+ args?.signal ? { signal: args.signal } : void 0
1497
+ );
1498
+ return normaliseUserPnL(body.userPnL);
1499
+ }
1357
1500
  async wallets(opts) {
1358
1501
  const body = await this.#graphqlUser.request(
1359
1502
  MY_WALLETS_QUERY,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taphubhq/sdk-core",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "Core SDK for building on the TabHub platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",