@taphubhq/sdk-core 0.15.3 → 0.16.0

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
@@ -33,10 +33,10 @@ __export(index_exports, {
33
33
  AgencyPairModule: () => AgencyPairModule,
34
34
  AuthModule: () => AuthModule,
35
35
  BidModule: () => BidModule,
36
- GameModule: () => GameModule,
37
36
  LeaderboardModule: () => LeaderboardModule,
38
37
  LocaleModule: () => LocaleModule,
39
38
  NetworkQualityMonitor: () => NetworkQualityMonitor,
39
+ PairModule: () => PairModule,
40
40
  RealtimeModule: () => RealtimeModule,
41
41
  TaphubAuthError: () => TaphubAuthError,
42
42
  TaphubClient: () => TaphubClient,
@@ -574,8 +574,151 @@ var BidModule = class {
574
574
  }
575
575
  };
576
576
 
577
- // src/modules/game/normalise.ts
578
- function normaliseGame(node) {
577
+ // src/modules/leaderboard/normalise.ts
578
+ function normaliseLeaderboard(rows) {
579
+ if (!Array.isArray(rows)) {
580
+ throw new TaphubServerError("Invalid response from server", {
581
+ code: "InvalidResponse"
582
+ });
583
+ }
584
+ return rows.map((row) => ({
585
+ userId: row.user_id,
586
+ username: row.username,
587
+ rank: row.rank,
588
+ totalBids: row.total_bids,
589
+ totalWins: row.total_wins,
590
+ totalWagered: row.total_wagered,
591
+ totalPayout: row.total_payout,
592
+ gain: row.gain
593
+ }));
594
+ }
595
+
596
+ // src/modules/leaderboard/queries.ts
597
+ var LEADERBOARD_QUERY = `query Leaderboard($period: String!, $sort_by: String!) {
598
+ leaderboard(period: $period, sort_by: $sort_by) {
599
+ user_id username rank total_bids total_wins total_wagered total_payout gain
600
+ }
601
+ }`;
602
+
603
+ // src/modules/leaderboard/index.ts
604
+ var LeaderboardModule = class {
605
+ #graphql;
606
+ constructor(deps) {
607
+ this.#graphql = deps.graphql;
608
+ }
609
+ async list(args) {
610
+ const body = await this.#graphql.request(
611
+ LEADERBOARD_QUERY,
612
+ { period: args.period, sort_by: args.sortBy },
613
+ args.signal ? { signal: args.signal } : void 0
614
+ );
615
+ return normaliseLeaderboard(body.leaderboard);
616
+ }
617
+ };
618
+
619
+ // src/modules/locale/normalise.ts
620
+ function normaliseLocaleResponse(node) {
621
+ let translations = null;
622
+ if (node.translations !== null) {
623
+ try {
624
+ translations = JSON.parse(node.translations);
625
+ } catch (err) {
626
+ throw new TaphubValidationError("Malformed translations JSON string from backend", {
627
+ code: "InvalidLocaleResponse",
628
+ details: {
629
+ lang: node.lang,
630
+ version: node.version,
631
+ parseError: err instanceof Error ? err.message : String(err)
632
+ }
633
+ });
634
+ }
635
+ }
636
+ return {
637
+ lang: node.lang,
638
+ version: node.version,
639
+ notModified: node.notModified,
640
+ translations
641
+ };
642
+ }
643
+ function normaliseRefreshLocalesPayload(node) {
644
+ let versions;
645
+ try {
646
+ versions = JSON.parse(node.versions);
647
+ } catch (err) {
648
+ throw new TaphubValidationError("Malformed versions JSON string from backend", {
649
+ code: "InvalidRefreshLocalesPayload",
650
+ details: {
651
+ parseError: err instanceof Error ? err.message : String(err)
652
+ }
653
+ });
654
+ }
655
+ return {
656
+ refreshed: node.refreshed,
657
+ versions
658
+ };
659
+ }
660
+
661
+ // src/modules/locale/queries.ts
662
+ var LOCALES_QUERY = `query Locales($input: LocalesInput!) {
663
+ locales(input: $input) {
664
+ lang
665
+ version
666
+ notModified
667
+ translations
668
+ }
669
+ }`;
670
+ var REFRESH_LOCALES_MUTATION = `mutation RefreshLocales {
671
+ refreshLocales {
672
+ refreshed
673
+ versions
674
+ }
675
+ }`;
676
+
677
+ // src/modules/locale/index.ts
678
+ var LocaleModule = class {
679
+ #graphql;
680
+ constructor(deps) {
681
+ this.#graphql = deps.graphql;
682
+ }
683
+ /**
684
+ * Fetch translations for a single language.
685
+ *
686
+ * Pass the last-seen `version` as `knownVersion` to opt into not-modified
687
+ * short-circuit semantics: backend returns `{notModified: true, translations: null}`
688
+ * when the cached body still matches, and the caller keeps prior state.
689
+ *
690
+ * Errors surface as TaphubError subclasses with `extensions.code` codes from
691
+ * the backend (e.g. `LangNotSupported`, `InsufficientUpstream`, `FeatureDisabled`).
692
+ * Caller decides the fallback strategy.
693
+ */
694
+ async get(lang, knownVersion, opts) {
695
+ const input = { lang };
696
+ if (knownVersion !== void 0 && knownVersion !== "") {
697
+ input.knownVersion = knownVersion;
698
+ }
699
+ const body = await this.#graphql.request(LOCALES_QUERY, { input }, opts);
700
+ return normaliseLocaleResponse(body.locales);
701
+ }
702
+ /**
703
+ * Trigger an admin refresh — backend re-pulls the source Sheet, invalidates
704
+ * its cache, and writes fresh entries for every supported language.
705
+ *
706
+ * Requires `X-API-Key` header equal to backend `InternalApiKey`. The transport
707
+ * layer is responsible for attaching the header; this method does not handle
708
+ * auth concerns directly.
709
+ */
710
+ async refresh(opts) {
711
+ const body = await this.#graphql.request(
712
+ REFRESH_LOCALES_MUTATION,
713
+ {},
714
+ opts
715
+ );
716
+ return normaliseRefreshLocalesPayload(body.refreshLocales);
717
+ }
718
+ };
719
+
720
+ // src/modules/pair/normalise.ts
721
+ function normalisePair(node) {
579
722
  if (typeof node.id !== "string" || node.id === "" || typeof node.pair !== "string" || node.pair === "" || typeof node.status !== "string" || node.status === "" || !node.config) {
580
723
  throw new TaphubServerError("Invalid response from server", {
581
724
  code: "InvalidResponse"
@@ -634,7 +777,7 @@ function normaliseCandles(list) {
634
777
  coefMults: c.coefMults
635
778
  }));
636
779
  }
637
- function normaliseGamePairInfo(node) {
780
+ function normalisePairInfo(node) {
638
781
  if (typeof node.id !== "string" || node.id === "" || typeof node.pair !== "string" || node.pair === "") {
639
782
  throw new TaphubServerError("Invalid response from server", { code: "InvalidResponse" });
640
783
  }
@@ -644,13 +787,14 @@ function normaliseGamePairInfo(node) {
644
787
  gameplayId: node.gameplayId,
645
788
  gameplayName: node.gameplayName,
646
789
  source: node.source,
647
- gameId: node.gameId ?? null
790
+ // REVIEW[bid-260602]: maps node.agencyPairId (was: node.gameId)
791
+ agencyPairId: node.agencyPairId ?? null
648
792
  };
649
793
  }
650
794
 
651
- // src/modules/game/queries.ts
652
- var GAME_QUERY = `query Game($pair: String!, $gameplaySlug: String) {
653
- game(pair: $pair, gameplaySlug: $gameplaySlug) {
795
+ // src/modules/pair/queries.ts
796
+ var PAIR_QUERY = `query Pair($pair: String!, $gameplaySlug: String) {
797
+ pair(pair: $pair, gameplaySlug: $gameplaySlug) {
654
798
  id pair status created_at
655
799
  config {
656
800
  gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
@@ -659,8 +803,8 @@ var GAME_QUERY = `query Game($pair: String!, $gameplaySlug: String) {
659
803
  }
660
804
  }
661
805
  }`;
662
- var CHART_HISTORY_QUERY = `query ChartHistory($gameId: ID!, $limit: Int) {
663
- chartHistory(gameId: $gameId, limit: $limit) {
806
+ var CHART_HISTORY_QUERY = `query ChartHistory($agencyPairId: ID!, $limit: Int) {
807
+ chartHistory(agencyPairId: $agencyPairId, limit: $limit) {
664
808
  time o h l c volatility coefMults
665
809
  }
666
810
  }`;
@@ -671,12 +815,12 @@ var BUILDER_AVAILABLE_GAME_PAIRS_QUERY = `query BuilderAvailableGamePairs($gamep
671
815
  gameplayId
672
816
  gameplayName
673
817
  source
674
- gameId
818
+ agencyPairId
675
819
  }
676
820
  }`;
677
821
 
678
- // src/modules/game/index.ts
679
- var GameModule = class {
822
+ // src/modules/pair/index.ts
823
+ var PairModule = class {
680
824
  #graphql;
681
825
  constructor(deps) {
682
826
  this.#graphql = deps.graphql;
@@ -686,7 +830,7 @@ var GameModule = class {
686
830
  if (opts?.gameplaySlug) variables.gameplaySlug = opts.gameplaySlug;
687
831
  let body;
688
832
  try {
689
- body = await this.#graphql.request(GAME_QUERY, variables, opts);
833
+ body = await this.#graphql.request(PAIR_QUERY, variables, opts);
690
834
  } catch (err) {
691
835
  if (err instanceof TaphubValidationError && err.code === "NotExist") {
692
836
  throw new TaphubValidationError("Game not found for pair", {
@@ -696,13 +840,13 @@ var GameModule = class {
696
840
  }
697
841
  throw err;
698
842
  }
699
- if (body.game === null) {
843
+ if (body.pair === null) {
700
844
  throw new TaphubValidationError("Game not found for pair", {
701
845
  code: "GameNotFound",
702
846
  details: { pair }
703
847
  });
704
848
  }
705
- return normaliseGame(body.game);
849
+ return normalisePair(body.pair);
706
850
  }
707
851
  /**
708
852
  * Returns available game pairs, optionally filtered by gameplay.
@@ -711,9 +855,9 @@ var GameModule = class {
711
855
  * `gameId` — use it directly as the MQTT topic `game/{gameId}/candle`.
712
856
  *
713
857
  * @example
714
- * const pairs = await client.game.availableGamePairs({ gameplayId: 'taptrading' });
715
- * const game = await client.game.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
716
- * const ch = client.realtime?.subscribe(pairs[0].gameId ?? game.id, userId);
858
+ * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
859
+ * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
860
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
717
861
  */
718
862
  async availableGamePairs(opts) {
719
863
  const variables = {};
@@ -723,10 +867,11 @@ var GameModule = class {
723
867
  variables,
724
868
  opts
725
869
  );
726
- return body.builderAvailableGamePairs.map(normaliseGamePairInfo);
870
+ return body.builderAvailableGamePairs.map(normalisePairInfo);
727
871
  }
728
- async chartHistory(gameId, limit, opts) {
729
- const variables = { gameId };
872
+ // REVIEW[bid-260602]: chartHistory param/variable renamed agencyPairId (was: gameId)
873
+ async chartHistory(agencyPairId, limit, opts) {
874
+ const variables = { agencyPairId };
730
875
  if (limit !== void 0) {
731
876
  variables.limit = limit;
732
877
  }
@@ -739,149 +884,6 @@ var GameModule = class {
739
884
  }
740
885
  };
741
886
 
742
- // src/modules/leaderboard/normalise.ts
743
- function normaliseLeaderboard(rows) {
744
- if (!Array.isArray(rows)) {
745
- throw new TaphubServerError("Invalid response from server", {
746
- code: "InvalidResponse"
747
- });
748
- }
749
- return rows.map((row) => ({
750
- userId: row.user_id,
751
- username: row.username,
752
- rank: row.rank,
753
- totalBids: row.total_bids,
754
- totalWins: row.total_wins,
755
- totalWagered: row.total_wagered,
756
- totalPayout: row.total_payout,
757
- gain: row.gain
758
- }));
759
- }
760
-
761
- // src/modules/leaderboard/queries.ts
762
- var LEADERBOARD_QUERY = `query Leaderboard($period: String!, $sort_by: String!) {
763
- leaderboard(period: $period, sort_by: $sort_by) {
764
- user_id username rank total_bids total_wins total_wagered total_payout gain
765
- }
766
- }`;
767
-
768
- // src/modules/leaderboard/index.ts
769
- var LeaderboardModule = class {
770
- #graphql;
771
- constructor(deps) {
772
- this.#graphql = deps.graphql;
773
- }
774
- async list(args) {
775
- const body = await this.#graphql.request(
776
- LEADERBOARD_QUERY,
777
- { period: args.period, sort_by: args.sortBy },
778
- args.signal ? { signal: args.signal } : void 0
779
- );
780
- return normaliseLeaderboard(body.leaderboard);
781
- }
782
- };
783
-
784
- // src/modules/locale/normalise.ts
785
- function normaliseLocaleResponse(node) {
786
- let translations = null;
787
- if (node.translations !== null) {
788
- try {
789
- translations = JSON.parse(node.translations);
790
- } catch (err) {
791
- throw new TaphubValidationError("Malformed translations JSON string from backend", {
792
- code: "InvalidLocaleResponse",
793
- details: {
794
- lang: node.lang,
795
- version: node.version,
796
- parseError: err instanceof Error ? err.message : String(err)
797
- }
798
- });
799
- }
800
- }
801
- return {
802
- lang: node.lang,
803
- version: node.version,
804
- notModified: node.notModified,
805
- translations
806
- };
807
- }
808
- function normaliseRefreshLocalesPayload(node) {
809
- let versions;
810
- try {
811
- versions = JSON.parse(node.versions);
812
- } catch (err) {
813
- throw new TaphubValidationError("Malformed versions JSON string from backend", {
814
- code: "InvalidRefreshLocalesPayload",
815
- details: {
816
- parseError: err instanceof Error ? err.message : String(err)
817
- }
818
- });
819
- }
820
- return {
821
- refreshed: node.refreshed,
822
- versions
823
- };
824
- }
825
-
826
- // src/modules/locale/queries.ts
827
- var LOCALES_QUERY = `query Locales($input: LocalesInput!) {
828
- locales(input: $input) {
829
- lang
830
- version
831
- notModified
832
- translations
833
- }
834
- }`;
835
- var REFRESH_LOCALES_MUTATION = `mutation RefreshLocales {
836
- refreshLocales {
837
- refreshed
838
- versions
839
- }
840
- }`;
841
-
842
- // src/modules/locale/index.ts
843
- var LocaleModule = class {
844
- #graphql;
845
- constructor(deps) {
846
- this.#graphql = deps.graphql;
847
- }
848
- /**
849
- * Fetch translations for a single language.
850
- *
851
- * Pass the last-seen `version` as `knownVersion` to opt into not-modified
852
- * short-circuit semantics: backend returns `{notModified: true, translations: null}`
853
- * when the cached body still matches, and the caller keeps prior state.
854
- *
855
- * Errors surface as TaphubError subclasses with `extensions.code` codes from
856
- * the backend (e.g. `LangNotSupported`, `InsufficientUpstream`, `FeatureDisabled`).
857
- * Caller decides the fallback strategy.
858
- */
859
- async get(lang, knownVersion, opts) {
860
- const input = { lang };
861
- if (knownVersion !== void 0 && knownVersion !== "") {
862
- input.knownVersion = knownVersion;
863
- }
864
- const body = await this.#graphql.request(LOCALES_QUERY, { input }, opts);
865
- return normaliseLocaleResponse(body.locales);
866
- }
867
- /**
868
- * Trigger an admin refresh — backend re-pulls the source Sheet, invalidates
869
- * its cache, and writes fresh entries for every supported language.
870
- *
871
- * Requires `X-API-Key` header equal to backend `InternalApiKey`. The transport
872
- * layer is responsible for attaching the header; this method does not handle
873
- * auth concerns directly.
874
- */
875
- async refresh(opts) {
876
- const body = await this.#graphql.request(
877
- REFRESH_LOCALES_MUTATION,
878
- {},
879
- opts
880
- );
881
- return normaliseRefreshLocalesPayload(body.refreshLocales);
882
- }
883
- };
884
-
885
887
  // src/transport/mqtt/index.ts
886
888
  var import_mqtt = __toESM(require("mqtt"));
887
889
 
@@ -2536,8 +2538,9 @@ var TaphubClient = class {
2536
2538
  user;
2537
2539
  /** @readonly Auth module — reassignment has no effect at runtime. */
2538
2540
  auth;
2539
- /** @readonly Game module — reassignment has no effect at runtime. */
2540
- game;
2541
+ /** @readonly Pair module — reassignment has no effect at runtime. */
2542
+ // REVIEW[bid-260602]: field pair: PairModule (was: game: GameModule)
2543
+ pair;
2541
2544
  /** @readonly Bid module — reassignment has no effect at runtime. */
2542
2545
  bid;
2543
2546
  /** @readonly Leaderboard module — reassignment has no effect at runtime. */
@@ -2621,7 +2624,7 @@ var TaphubClient = class {
2621
2624
  this.user.clearCurrencies();
2622
2625
  }
2623
2626
  });
2624
- this.game = new GameModule({ graphql: this.#graphql });
2627
+ this.pair = new PairModule({ graphql: this.#graphql });
2625
2628
  this.bid = new BidModule({ graphql: this.#graphql });
2626
2629
  this.leaderboard = new LeaderboardModule({ graphql: this.#graphql });
2627
2630
  this.agencyPairs = new AgencyPairModule({ graphql: this.#graphql });
@@ -2644,8 +2647,8 @@ var TaphubClient = class {
2644
2647
  enumerable: true,
2645
2648
  configurable: false
2646
2649
  });
2647
- Object.defineProperty(this, "game", {
2648
- value: this.game,
2650
+ Object.defineProperty(this, "pair", {
2651
+ value: this.pair,
2649
2652
  writable: false,
2650
2653
  enumerable: true,
2651
2654
  configurable: false
@@ -2807,10 +2810,10 @@ function calculateProbWin_v2(time1, time2, _price1, price2, creationTime, curren
2807
2810
  AgencyPairModule,
2808
2811
  AuthModule,
2809
2812
  BidModule,
2810
- GameModule,
2811
2813
  LeaderboardModule,
2812
2814
  LocaleModule,
2813
2815
  NetworkQualityMonitor,
2816
+ PairModule,
2814
2817
  RealtimeModule,
2815
2818
  TaphubAuthError,
2816
2819
  TaphubClient,
package/dist/index.d.mts CHANGED
@@ -233,97 +233,6 @@ declare class BidModule {
233
233
  }): Promise<Bid[]>;
234
234
  }
235
235
 
236
- interface Game {
237
- id: string;
238
- pair: string;
239
- status: string;
240
- config: GameConfig;
241
- /** ISO-8601 string passthrough from the server. */
242
- createdAt: string | null;
243
- }
244
- interface GameConfig {
245
- gridConfig: GridConfig;
246
- constraints: Constraints;
247
- acceptableBids: number[];
248
- minBidAmount: number;
249
- maxBidAmount: number;
250
- }
251
- interface GridConfig {
252
- cellSizeTime: number;
253
- cellSizeValue: number;
254
- candleSize: number;
255
- baseline: number;
256
- baselineTime: number;
257
- }
258
- interface Constraints {
259
- minBetTime: number;
260
- maxBetTime: number;
261
- slippage: number;
262
- priceMinRange: number;
263
- priceMaxRange: number;
264
- coefMults: number[];
265
- maxCoef: number;
266
- }
267
- interface GamePairInfo {
268
- /** game_pairs.id — catalog identifier for this pair within a gameplay. */
269
- id: string;
270
- /** Trading pair symbol, e.g. "BTC/USD". */
271
- pair: string;
272
- /** ID of the gameplay this pair belongs to. Use as `gameplaySlug` in game.get(). */
273
- gameplayId: string;
274
- /** Human-readable gameplay name. */
275
- gameplayName: string;
276
- /** Price feed source, e.g. "binance". */
277
- source: string;
278
- /**
279
- * agency_game_pairs.id — the runtime game ID for this agency.
280
- * Present only when the caller is authenticated with a valid JWT.
281
- * Use directly as `gameId` in MQTT topic `game/{gameId}/candle`.
282
- * Null when called without authentication.
283
- */
284
- gameId: string | null;
285
- }
286
- interface Candle {
287
- /** Unix epoch SECONDS (not ms). Multiply by 1000 for JS Date. */
288
- time: number;
289
- o: number;
290
- h: number;
291
- l: number;
292
- c: number;
293
- volatility: number;
294
- coefMults: number[];
295
- }
296
-
297
- interface GameModuleDeps {
298
- graphql: GraphQLTransport;
299
- }
300
- declare class GameModule {
301
- #private;
302
- constructor(deps: GameModuleDeps);
303
- get(pair: string, opts?: {
304
- gameplaySlug?: string;
305
- signal?: AbortSignal;
306
- }): Promise<Game>;
307
- /**
308
- * Returns available game pairs, optionally filtered by gameplay.
309
- *
310
- * When called with a valid JWT (authenticated builder), each entry includes
311
- * `gameId` — use it directly as the MQTT topic `game/{gameId}/candle`.
312
- *
313
- * @example
314
- * const pairs = await client.game.availableGamePairs({ gameplayId: 'taptrading' });
315
- * const game = await client.game.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
316
- * const ch = client.realtime?.subscribe(pairs[0].gameId ?? game.id, userId);
317
- */
318
- availableGamePairs(opts?: {
319
- gameplayId?: string;
320
- signal?: AbortSignal;
321
- }): Promise<GamePairInfo[]>;
322
- chartHistory(gameId: string, limit?: number, opts?: {
323
- signal?: AbortSignal;
324
- }): Promise<Candle[]>;
325
- }
326
-
327
236
  type LeaderboardPeriod = 'weekly' | 'all_time';
328
237
  type LeaderboardSortBy = 'gain' | 'wagered';
329
238
  interface LeaderboardEntry {
@@ -427,6 +336,97 @@ declare class LocaleModule {
427
336
  }): Promise<LocaleRefreshResult>;
428
337
  }
429
338
 
339
+ interface Pair {
340
+ id: string;
341
+ pair: string;
342
+ status: string;
343
+ config: GameConfig;
344
+ /** ISO-8601 string passthrough from the server. */
345
+ createdAt: string | null;
346
+ }
347
+ interface GameConfig {
348
+ gridConfig: GridConfig;
349
+ constraints: Constraints;
350
+ acceptableBids: number[];
351
+ minBidAmount: number;
352
+ maxBidAmount: number;
353
+ }
354
+ interface GridConfig {
355
+ cellSizeTime: number;
356
+ cellSizeValue: number;
357
+ candleSize: number;
358
+ baseline: number;
359
+ baselineTime: number;
360
+ }
361
+ interface Constraints {
362
+ minBetTime: number;
363
+ maxBetTime: number;
364
+ slippage: number;
365
+ priceMinRange: number;
366
+ priceMaxRange: number;
367
+ coefMults: number[];
368
+ maxCoef: number;
369
+ }
370
+ interface PairInfo {
371
+ /** game_pairs.id — catalog identifier for this pair within a gameplay. */
372
+ id: string;
373
+ /** Trading pair symbol, e.g. "BTC/USD". */
374
+ pair: string;
375
+ /** ID of the gameplay this pair belongs to. Use as `gameplaySlug` in game.get(). */
376
+ gameplayId: string;
377
+ /** Human-readable gameplay name. */
378
+ gameplayName: string;
379
+ /** Price feed source, e.g. "binance". */
380
+ source: string;
381
+ /**
382
+ * agency_game_pairs.id — the runtime game ID for this agency.
383
+ * Present only when the caller is authenticated with a valid JWT.
384
+ * Use directly as `gameId` in MQTT topic `game/{gameId}/candle`.
385
+ * Null when called without authentication.
386
+ */
387
+ agencyPairId: string | null;
388
+ }
389
+ interface Candle {
390
+ /** Unix epoch SECONDS (not ms). Multiply by 1000 for JS Date. */
391
+ time: number;
392
+ o: number;
393
+ h: number;
394
+ l: number;
395
+ c: number;
396
+ volatility: number;
397
+ coefMults: number[];
398
+ }
399
+
400
+ interface PairModuleDeps {
401
+ graphql: GraphQLTransport;
402
+ }
403
+ declare class PairModule {
404
+ #private;
405
+ constructor(deps: PairModuleDeps);
406
+ get(pair: string, opts?: {
407
+ gameplaySlug?: string;
408
+ signal?: AbortSignal;
409
+ }): Promise<Pair>;
410
+ /**
411
+ * Returns available game pairs, optionally filtered by gameplay.
412
+ *
413
+ * When called with a valid JWT (authenticated builder), each entry includes
414
+ * `gameId` — use it directly as the MQTT topic `game/{gameId}/candle`.
415
+ *
416
+ * @example
417
+ * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
418
+ * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
419
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
420
+ */
421
+ availableGamePairs(opts?: {
422
+ gameplayId?: string;
423
+ signal?: AbortSignal;
424
+ }): Promise<PairInfo[]>;
425
+ chartHistory(agencyPairId: string, limit?: number, opts?: {
426
+ signal?: AbortSignal;
427
+ }): Promise<Candle[]>;
428
+ }
429
+
430
430
  interface MqttWireCandle {
431
431
  type: 'new' | 'update';
432
432
  time: number;
@@ -891,8 +891,8 @@ declare class TaphubClient {
891
891
  user: UserModule;
892
892
  /** @readonly Auth module — reassignment has no effect at runtime. */
893
893
  auth: AuthModule;
894
- /** @readonly Game module — reassignment has no effect at runtime. */
895
- game: GameModule;
894
+ /** @readonly Pair module — reassignment has no effect at runtime. */
895
+ pair: PairModule;
896
896
  /** @readonly Bid module — reassignment has no effect at runtime. */
897
897
  bid: BidModule;
898
898
  /** @readonly Leaderboard module — reassignment has no effect at runtime. */
@@ -988,4 +988,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
988
988
  declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
989
989
  declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
990
990
 
991
- 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 };
991
+ export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, GameChannel, type GameConfig, 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 Pair, type PairInfo, PairModule, 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
@@ -233,97 +233,6 @@ declare class BidModule {
233
233
  }): Promise<Bid[]>;
234
234
  }
235
235
 
236
- interface Game {
237
- id: string;
238
- pair: string;
239
- status: string;
240
- config: GameConfig;
241
- /** ISO-8601 string passthrough from the server. */
242
- createdAt: string | null;
243
- }
244
- interface GameConfig {
245
- gridConfig: GridConfig;
246
- constraints: Constraints;
247
- acceptableBids: number[];
248
- minBidAmount: number;
249
- maxBidAmount: number;
250
- }
251
- interface GridConfig {
252
- cellSizeTime: number;
253
- cellSizeValue: number;
254
- candleSize: number;
255
- baseline: number;
256
- baselineTime: number;
257
- }
258
- interface Constraints {
259
- minBetTime: number;
260
- maxBetTime: number;
261
- slippage: number;
262
- priceMinRange: number;
263
- priceMaxRange: number;
264
- coefMults: number[];
265
- maxCoef: number;
266
- }
267
- interface GamePairInfo {
268
- /** game_pairs.id — catalog identifier for this pair within a gameplay. */
269
- id: string;
270
- /** Trading pair symbol, e.g. "BTC/USD". */
271
- pair: string;
272
- /** ID of the gameplay this pair belongs to. Use as `gameplaySlug` in game.get(). */
273
- gameplayId: string;
274
- /** Human-readable gameplay name. */
275
- gameplayName: string;
276
- /** Price feed source, e.g. "binance". */
277
- source: string;
278
- /**
279
- * agency_game_pairs.id — the runtime game ID for this agency.
280
- * Present only when the caller is authenticated with a valid JWT.
281
- * Use directly as `gameId` in MQTT topic `game/{gameId}/candle`.
282
- * Null when called without authentication.
283
- */
284
- gameId: string | null;
285
- }
286
- interface Candle {
287
- /** Unix epoch SECONDS (not ms). Multiply by 1000 for JS Date. */
288
- time: number;
289
- o: number;
290
- h: number;
291
- l: number;
292
- c: number;
293
- volatility: number;
294
- coefMults: number[];
295
- }
296
-
297
- interface GameModuleDeps {
298
- graphql: GraphQLTransport;
299
- }
300
- declare class GameModule {
301
- #private;
302
- constructor(deps: GameModuleDeps);
303
- get(pair: string, opts?: {
304
- gameplaySlug?: string;
305
- signal?: AbortSignal;
306
- }): Promise<Game>;
307
- /**
308
- * Returns available game pairs, optionally filtered by gameplay.
309
- *
310
- * When called with a valid JWT (authenticated builder), each entry includes
311
- * `gameId` — use it directly as the MQTT topic `game/{gameId}/candle`.
312
- *
313
- * @example
314
- * const pairs = await client.game.availableGamePairs({ gameplayId: 'taptrading' });
315
- * const game = await client.game.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
316
- * const ch = client.realtime?.subscribe(pairs[0].gameId ?? game.id, userId);
317
- */
318
- availableGamePairs(opts?: {
319
- gameplayId?: string;
320
- signal?: AbortSignal;
321
- }): Promise<GamePairInfo[]>;
322
- chartHistory(gameId: string, limit?: number, opts?: {
323
- signal?: AbortSignal;
324
- }): Promise<Candle[]>;
325
- }
326
-
327
236
  type LeaderboardPeriod = 'weekly' | 'all_time';
328
237
  type LeaderboardSortBy = 'gain' | 'wagered';
329
238
  interface LeaderboardEntry {
@@ -427,6 +336,97 @@ declare class LocaleModule {
427
336
  }): Promise<LocaleRefreshResult>;
428
337
  }
429
338
 
339
+ interface Pair {
340
+ id: string;
341
+ pair: string;
342
+ status: string;
343
+ config: GameConfig;
344
+ /** ISO-8601 string passthrough from the server. */
345
+ createdAt: string | null;
346
+ }
347
+ interface GameConfig {
348
+ gridConfig: GridConfig;
349
+ constraints: Constraints;
350
+ acceptableBids: number[];
351
+ minBidAmount: number;
352
+ maxBidAmount: number;
353
+ }
354
+ interface GridConfig {
355
+ cellSizeTime: number;
356
+ cellSizeValue: number;
357
+ candleSize: number;
358
+ baseline: number;
359
+ baselineTime: number;
360
+ }
361
+ interface Constraints {
362
+ minBetTime: number;
363
+ maxBetTime: number;
364
+ slippage: number;
365
+ priceMinRange: number;
366
+ priceMaxRange: number;
367
+ coefMults: number[];
368
+ maxCoef: number;
369
+ }
370
+ interface PairInfo {
371
+ /** game_pairs.id — catalog identifier for this pair within a gameplay. */
372
+ id: string;
373
+ /** Trading pair symbol, e.g. "BTC/USD". */
374
+ pair: string;
375
+ /** ID of the gameplay this pair belongs to. Use as `gameplaySlug` in game.get(). */
376
+ gameplayId: string;
377
+ /** Human-readable gameplay name. */
378
+ gameplayName: string;
379
+ /** Price feed source, e.g. "binance". */
380
+ source: string;
381
+ /**
382
+ * agency_game_pairs.id — the runtime game ID for this agency.
383
+ * Present only when the caller is authenticated with a valid JWT.
384
+ * Use directly as `gameId` in MQTT topic `game/{gameId}/candle`.
385
+ * Null when called without authentication.
386
+ */
387
+ agencyPairId: string | null;
388
+ }
389
+ interface Candle {
390
+ /** Unix epoch SECONDS (not ms). Multiply by 1000 for JS Date. */
391
+ time: number;
392
+ o: number;
393
+ h: number;
394
+ l: number;
395
+ c: number;
396
+ volatility: number;
397
+ coefMults: number[];
398
+ }
399
+
400
+ interface PairModuleDeps {
401
+ graphql: GraphQLTransport;
402
+ }
403
+ declare class PairModule {
404
+ #private;
405
+ constructor(deps: PairModuleDeps);
406
+ get(pair: string, opts?: {
407
+ gameplaySlug?: string;
408
+ signal?: AbortSignal;
409
+ }): Promise<Pair>;
410
+ /**
411
+ * Returns available game pairs, optionally filtered by gameplay.
412
+ *
413
+ * When called with a valid JWT (authenticated builder), each entry includes
414
+ * `gameId` — use it directly as the MQTT topic `game/{gameId}/candle`.
415
+ *
416
+ * @example
417
+ * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
418
+ * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
419
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
420
+ */
421
+ availableGamePairs(opts?: {
422
+ gameplayId?: string;
423
+ signal?: AbortSignal;
424
+ }): Promise<PairInfo[]>;
425
+ chartHistory(agencyPairId: string, limit?: number, opts?: {
426
+ signal?: AbortSignal;
427
+ }): Promise<Candle[]>;
428
+ }
429
+
430
430
  interface MqttWireCandle {
431
431
  type: 'new' | 'update';
432
432
  time: number;
@@ -891,8 +891,8 @@ declare class TaphubClient {
891
891
  user: UserModule;
892
892
  /** @readonly Auth module — reassignment has no effect at runtime. */
893
893
  auth: AuthModule;
894
- /** @readonly Game module — reassignment has no effect at runtime. */
895
- game: GameModule;
894
+ /** @readonly Pair module — reassignment has no effect at runtime. */
895
+ pair: PairModule;
896
896
  /** @readonly Bid module — reassignment has no effect at runtime. */
897
897
  bid: BidModule;
898
898
  /** @readonly Leaderboard module — reassignment has no effect at runtime. */
@@ -988,4 +988,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
988
988
  declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
989
989
  declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
990
990
 
991
- 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 };
991
+ export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, GameChannel, type GameConfig, 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 Pair, type PairInfo, PairModule, 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
@@ -509,8 +509,151 @@ var BidModule = class {
509
509
  }
510
510
  };
511
511
 
512
- // src/modules/game/normalise.ts
513
- function normaliseGame(node) {
512
+ // src/modules/leaderboard/normalise.ts
513
+ function normaliseLeaderboard(rows) {
514
+ if (!Array.isArray(rows)) {
515
+ throw new TaphubServerError("Invalid response from server", {
516
+ code: "InvalidResponse"
517
+ });
518
+ }
519
+ return rows.map((row) => ({
520
+ userId: row.user_id,
521
+ username: row.username,
522
+ rank: row.rank,
523
+ totalBids: row.total_bids,
524
+ totalWins: row.total_wins,
525
+ totalWagered: row.total_wagered,
526
+ totalPayout: row.total_payout,
527
+ gain: row.gain
528
+ }));
529
+ }
530
+
531
+ // src/modules/leaderboard/queries.ts
532
+ var LEADERBOARD_QUERY = `query Leaderboard($period: String!, $sort_by: String!) {
533
+ leaderboard(period: $period, sort_by: $sort_by) {
534
+ user_id username rank total_bids total_wins total_wagered total_payout gain
535
+ }
536
+ }`;
537
+
538
+ // src/modules/leaderboard/index.ts
539
+ var LeaderboardModule = class {
540
+ #graphql;
541
+ constructor(deps) {
542
+ this.#graphql = deps.graphql;
543
+ }
544
+ async list(args) {
545
+ const body = await this.#graphql.request(
546
+ LEADERBOARD_QUERY,
547
+ { period: args.period, sort_by: args.sortBy },
548
+ args.signal ? { signal: args.signal } : void 0
549
+ );
550
+ return normaliseLeaderboard(body.leaderboard);
551
+ }
552
+ };
553
+
554
+ // src/modules/locale/normalise.ts
555
+ function normaliseLocaleResponse(node) {
556
+ let translations = null;
557
+ if (node.translations !== null) {
558
+ try {
559
+ translations = JSON.parse(node.translations);
560
+ } catch (err) {
561
+ throw new TaphubValidationError("Malformed translations JSON string from backend", {
562
+ code: "InvalidLocaleResponse",
563
+ details: {
564
+ lang: node.lang,
565
+ version: node.version,
566
+ parseError: err instanceof Error ? err.message : String(err)
567
+ }
568
+ });
569
+ }
570
+ }
571
+ return {
572
+ lang: node.lang,
573
+ version: node.version,
574
+ notModified: node.notModified,
575
+ translations
576
+ };
577
+ }
578
+ function normaliseRefreshLocalesPayload(node) {
579
+ let versions;
580
+ try {
581
+ versions = JSON.parse(node.versions);
582
+ } catch (err) {
583
+ throw new TaphubValidationError("Malformed versions JSON string from backend", {
584
+ code: "InvalidRefreshLocalesPayload",
585
+ details: {
586
+ parseError: err instanceof Error ? err.message : String(err)
587
+ }
588
+ });
589
+ }
590
+ return {
591
+ refreshed: node.refreshed,
592
+ versions
593
+ };
594
+ }
595
+
596
+ // src/modules/locale/queries.ts
597
+ var LOCALES_QUERY = `query Locales($input: LocalesInput!) {
598
+ locales(input: $input) {
599
+ lang
600
+ version
601
+ notModified
602
+ translations
603
+ }
604
+ }`;
605
+ var REFRESH_LOCALES_MUTATION = `mutation RefreshLocales {
606
+ refreshLocales {
607
+ refreshed
608
+ versions
609
+ }
610
+ }`;
611
+
612
+ // src/modules/locale/index.ts
613
+ var LocaleModule = class {
614
+ #graphql;
615
+ constructor(deps) {
616
+ this.#graphql = deps.graphql;
617
+ }
618
+ /**
619
+ * Fetch translations for a single language.
620
+ *
621
+ * Pass the last-seen `version` as `knownVersion` to opt into not-modified
622
+ * short-circuit semantics: backend returns `{notModified: true, translations: null}`
623
+ * when the cached body still matches, and the caller keeps prior state.
624
+ *
625
+ * Errors surface as TaphubError subclasses with `extensions.code` codes from
626
+ * the backend (e.g. `LangNotSupported`, `InsufficientUpstream`, `FeatureDisabled`).
627
+ * Caller decides the fallback strategy.
628
+ */
629
+ async get(lang, knownVersion, opts) {
630
+ const input = { lang };
631
+ if (knownVersion !== void 0 && knownVersion !== "") {
632
+ input.knownVersion = knownVersion;
633
+ }
634
+ const body = await this.#graphql.request(LOCALES_QUERY, { input }, opts);
635
+ return normaliseLocaleResponse(body.locales);
636
+ }
637
+ /**
638
+ * Trigger an admin refresh — backend re-pulls the source Sheet, invalidates
639
+ * its cache, and writes fresh entries for every supported language.
640
+ *
641
+ * Requires `X-API-Key` header equal to backend `InternalApiKey`. The transport
642
+ * layer is responsible for attaching the header; this method does not handle
643
+ * auth concerns directly.
644
+ */
645
+ async refresh(opts) {
646
+ const body = await this.#graphql.request(
647
+ REFRESH_LOCALES_MUTATION,
648
+ {},
649
+ opts
650
+ );
651
+ return normaliseRefreshLocalesPayload(body.refreshLocales);
652
+ }
653
+ };
654
+
655
+ // src/modules/pair/normalise.ts
656
+ function normalisePair(node) {
514
657
  if (typeof node.id !== "string" || node.id === "" || typeof node.pair !== "string" || node.pair === "" || typeof node.status !== "string" || node.status === "" || !node.config) {
515
658
  throw new TaphubServerError("Invalid response from server", {
516
659
  code: "InvalidResponse"
@@ -569,7 +712,7 @@ function normaliseCandles(list) {
569
712
  coefMults: c.coefMults
570
713
  }));
571
714
  }
572
- function normaliseGamePairInfo(node) {
715
+ function normalisePairInfo(node) {
573
716
  if (typeof node.id !== "string" || node.id === "" || typeof node.pair !== "string" || node.pair === "") {
574
717
  throw new TaphubServerError("Invalid response from server", { code: "InvalidResponse" });
575
718
  }
@@ -579,13 +722,14 @@ function normaliseGamePairInfo(node) {
579
722
  gameplayId: node.gameplayId,
580
723
  gameplayName: node.gameplayName,
581
724
  source: node.source,
582
- gameId: node.gameId ?? null
725
+ // REVIEW[bid-260602]: maps node.agencyPairId (was: node.gameId)
726
+ agencyPairId: node.agencyPairId ?? null
583
727
  };
584
728
  }
585
729
 
586
- // src/modules/game/queries.ts
587
- var GAME_QUERY = `query Game($pair: String!, $gameplaySlug: String) {
588
- game(pair: $pair, gameplaySlug: $gameplaySlug) {
730
+ // src/modules/pair/queries.ts
731
+ var PAIR_QUERY = `query Pair($pair: String!, $gameplaySlug: String) {
732
+ pair(pair: $pair, gameplaySlug: $gameplaySlug) {
589
733
  id pair status created_at
590
734
  config {
591
735
  gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
@@ -594,8 +738,8 @@ var GAME_QUERY = `query Game($pair: String!, $gameplaySlug: String) {
594
738
  }
595
739
  }
596
740
  }`;
597
- var CHART_HISTORY_QUERY = `query ChartHistory($gameId: ID!, $limit: Int) {
598
- chartHistory(gameId: $gameId, limit: $limit) {
741
+ var CHART_HISTORY_QUERY = `query ChartHistory($agencyPairId: ID!, $limit: Int) {
742
+ chartHistory(agencyPairId: $agencyPairId, limit: $limit) {
599
743
  time o h l c volatility coefMults
600
744
  }
601
745
  }`;
@@ -606,12 +750,12 @@ var BUILDER_AVAILABLE_GAME_PAIRS_QUERY = `query BuilderAvailableGamePairs($gamep
606
750
  gameplayId
607
751
  gameplayName
608
752
  source
609
- gameId
753
+ agencyPairId
610
754
  }
611
755
  }`;
612
756
 
613
- // src/modules/game/index.ts
614
- var GameModule = class {
757
+ // src/modules/pair/index.ts
758
+ var PairModule = class {
615
759
  #graphql;
616
760
  constructor(deps) {
617
761
  this.#graphql = deps.graphql;
@@ -621,7 +765,7 @@ var GameModule = class {
621
765
  if (opts?.gameplaySlug) variables.gameplaySlug = opts.gameplaySlug;
622
766
  let body;
623
767
  try {
624
- body = await this.#graphql.request(GAME_QUERY, variables, opts);
768
+ body = await this.#graphql.request(PAIR_QUERY, variables, opts);
625
769
  } catch (err) {
626
770
  if (err instanceof TaphubValidationError && err.code === "NotExist") {
627
771
  throw new TaphubValidationError("Game not found for pair", {
@@ -631,13 +775,13 @@ var GameModule = class {
631
775
  }
632
776
  throw err;
633
777
  }
634
- if (body.game === null) {
778
+ if (body.pair === null) {
635
779
  throw new TaphubValidationError("Game not found for pair", {
636
780
  code: "GameNotFound",
637
781
  details: { pair }
638
782
  });
639
783
  }
640
- return normaliseGame(body.game);
784
+ return normalisePair(body.pair);
641
785
  }
642
786
  /**
643
787
  * Returns available game pairs, optionally filtered by gameplay.
@@ -646,9 +790,9 @@ var GameModule = class {
646
790
  * `gameId` — use it directly as the MQTT topic `game/{gameId}/candle`.
647
791
  *
648
792
  * @example
649
- * const pairs = await client.game.availableGamePairs({ gameplayId: 'taptrading' });
650
- * const game = await client.game.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
651
- * const ch = client.realtime?.subscribe(pairs[0].gameId ?? game.id, userId);
793
+ * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
794
+ * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
795
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
652
796
  */
653
797
  async availableGamePairs(opts) {
654
798
  const variables = {};
@@ -658,10 +802,11 @@ var GameModule = class {
658
802
  variables,
659
803
  opts
660
804
  );
661
- return body.builderAvailableGamePairs.map(normaliseGamePairInfo);
805
+ return body.builderAvailableGamePairs.map(normalisePairInfo);
662
806
  }
663
- async chartHistory(gameId, limit, opts) {
664
- const variables = { gameId };
807
+ // REVIEW[bid-260602]: chartHistory param/variable renamed agencyPairId (was: gameId)
808
+ async chartHistory(agencyPairId, limit, opts) {
809
+ const variables = { agencyPairId };
665
810
  if (limit !== void 0) {
666
811
  variables.limit = limit;
667
812
  }
@@ -674,149 +819,6 @@ var GameModule = class {
674
819
  }
675
820
  };
676
821
 
677
- // src/modules/leaderboard/normalise.ts
678
- function normaliseLeaderboard(rows) {
679
- if (!Array.isArray(rows)) {
680
- throw new TaphubServerError("Invalid response from server", {
681
- code: "InvalidResponse"
682
- });
683
- }
684
- return rows.map((row) => ({
685
- userId: row.user_id,
686
- username: row.username,
687
- rank: row.rank,
688
- totalBids: row.total_bids,
689
- totalWins: row.total_wins,
690
- totalWagered: row.total_wagered,
691
- totalPayout: row.total_payout,
692
- gain: row.gain
693
- }));
694
- }
695
-
696
- // src/modules/leaderboard/queries.ts
697
- var LEADERBOARD_QUERY = `query Leaderboard($period: String!, $sort_by: String!) {
698
- leaderboard(period: $period, sort_by: $sort_by) {
699
- user_id username rank total_bids total_wins total_wagered total_payout gain
700
- }
701
- }`;
702
-
703
- // src/modules/leaderboard/index.ts
704
- var LeaderboardModule = class {
705
- #graphql;
706
- constructor(deps) {
707
- this.#graphql = deps.graphql;
708
- }
709
- async list(args) {
710
- const body = await this.#graphql.request(
711
- LEADERBOARD_QUERY,
712
- { period: args.period, sort_by: args.sortBy },
713
- args.signal ? { signal: args.signal } : void 0
714
- );
715
- return normaliseLeaderboard(body.leaderboard);
716
- }
717
- };
718
-
719
- // src/modules/locale/normalise.ts
720
- function normaliseLocaleResponse(node) {
721
- let translations = null;
722
- if (node.translations !== null) {
723
- try {
724
- translations = JSON.parse(node.translations);
725
- } catch (err) {
726
- throw new TaphubValidationError("Malformed translations JSON string from backend", {
727
- code: "InvalidLocaleResponse",
728
- details: {
729
- lang: node.lang,
730
- version: node.version,
731
- parseError: err instanceof Error ? err.message : String(err)
732
- }
733
- });
734
- }
735
- }
736
- return {
737
- lang: node.lang,
738
- version: node.version,
739
- notModified: node.notModified,
740
- translations
741
- };
742
- }
743
- function normaliseRefreshLocalesPayload(node) {
744
- let versions;
745
- try {
746
- versions = JSON.parse(node.versions);
747
- } catch (err) {
748
- throw new TaphubValidationError("Malformed versions JSON string from backend", {
749
- code: "InvalidRefreshLocalesPayload",
750
- details: {
751
- parseError: err instanceof Error ? err.message : String(err)
752
- }
753
- });
754
- }
755
- return {
756
- refreshed: node.refreshed,
757
- versions
758
- };
759
- }
760
-
761
- // src/modules/locale/queries.ts
762
- var LOCALES_QUERY = `query Locales($input: LocalesInput!) {
763
- locales(input: $input) {
764
- lang
765
- version
766
- notModified
767
- translations
768
- }
769
- }`;
770
- var REFRESH_LOCALES_MUTATION = `mutation RefreshLocales {
771
- refreshLocales {
772
- refreshed
773
- versions
774
- }
775
- }`;
776
-
777
- // src/modules/locale/index.ts
778
- var LocaleModule = class {
779
- #graphql;
780
- constructor(deps) {
781
- this.#graphql = deps.graphql;
782
- }
783
- /**
784
- * Fetch translations for a single language.
785
- *
786
- * Pass the last-seen `version` as `knownVersion` to opt into not-modified
787
- * short-circuit semantics: backend returns `{notModified: true, translations: null}`
788
- * when the cached body still matches, and the caller keeps prior state.
789
- *
790
- * Errors surface as TaphubError subclasses with `extensions.code` codes from
791
- * the backend (e.g. `LangNotSupported`, `InsufficientUpstream`, `FeatureDisabled`).
792
- * Caller decides the fallback strategy.
793
- */
794
- async get(lang, knownVersion, opts) {
795
- const input = { lang };
796
- if (knownVersion !== void 0 && knownVersion !== "") {
797
- input.knownVersion = knownVersion;
798
- }
799
- const body = await this.#graphql.request(LOCALES_QUERY, { input }, opts);
800
- return normaliseLocaleResponse(body.locales);
801
- }
802
- /**
803
- * Trigger an admin refresh — backend re-pulls the source Sheet, invalidates
804
- * its cache, and writes fresh entries for every supported language.
805
- *
806
- * Requires `X-API-Key` header equal to backend `InternalApiKey`. The transport
807
- * layer is responsible for attaching the header; this method does not handle
808
- * auth concerns directly.
809
- */
810
- async refresh(opts) {
811
- const body = await this.#graphql.request(
812
- REFRESH_LOCALES_MUTATION,
813
- {},
814
- opts
815
- );
816
- return normaliseRefreshLocalesPayload(body.refreshLocales);
817
- }
818
- };
819
-
820
822
  // src/transport/mqtt/index.ts
821
823
  import mqtt from "mqtt";
822
824
 
@@ -2471,8 +2473,9 @@ var TaphubClient = class {
2471
2473
  user;
2472
2474
  /** @readonly Auth module — reassignment has no effect at runtime. */
2473
2475
  auth;
2474
- /** @readonly Game module — reassignment has no effect at runtime. */
2475
- game;
2476
+ /** @readonly Pair module — reassignment has no effect at runtime. */
2477
+ // REVIEW[bid-260602]: field pair: PairModule (was: game: GameModule)
2478
+ pair;
2476
2479
  /** @readonly Bid module — reassignment has no effect at runtime. */
2477
2480
  bid;
2478
2481
  /** @readonly Leaderboard module — reassignment has no effect at runtime. */
@@ -2556,7 +2559,7 @@ var TaphubClient = class {
2556
2559
  this.user.clearCurrencies();
2557
2560
  }
2558
2561
  });
2559
- this.game = new GameModule({ graphql: this.#graphql });
2562
+ this.pair = new PairModule({ graphql: this.#graphql });
2560
2563
  this.bid = new BidModule({ graphql: this.#graphql });
2561
2564
  this.leaderboard = new LeaderboardModule({ graphql: this.#graphql });
2562
2565
  this.agencyPairs = new AgencyPairModule({ graphql: this.#graphql });
@@ -2579,8 +2582,8 @@ var TaphubClient = class {
2579
2582
  enumerable: true,
2580
2583
  configurable: false
2581
2584
  });
2582
- Object.defineProperty(this, "game", {
2583
- value: this.game,
2585
+ Object.defineProperty(this, "pair", {
2586
+ value: this.pair,
2584
2587
  writable: false,
2585
2588
  enumerable: true,
2586
2589
  configurable: false
@@ -2741,10 +2744,10 @@ export {
2741
2744
  AgencyPairModule,
2742
2745
  AuthModule,
2743
2746
  BidModule,
2744
- GameModule,
2745
2747
  LeaderboardModule,
2746
2748
  LocaleModule,
2747
2749
  NetworkQualityMonitor,
2750
+ PairModule,
2748
2751
  RealtimeModule,
2749
2752
  TaphubAuthError,
2750
2753
  TaphubClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taphubhq/sdk-core",
3
- "version": "0.15.3",
3
+ "version": "0.16.0",
4
4
  "description": "Core SDK for building on the TabHub platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",