@taphubhq/sdk-core 0.16.2 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -139,7 +139,7 @@ function normaliseAgencyPairs(rows) {
139
139
  });
140
140
  }
141
141
  return rows.map((row) => ({
142
- gamePairId: row.gamePairId,
142
+ pairId: row.pairId,
143
143
  pair: row.pair,
144
144
  thumb: row.thumb,
145
145
  ordering: Number(row.ordering),
@@ -153,7 +153,7 @@ function normaliseAgencyPairs(rows) {
153
153
  // src/modules/agencyPair/queries.ts
154
154
  var FETCH_AGENCY_PAIRS_QUERY = `query FetchAgencyPairs($filter: AgencyPairFilter) {
155
155
  fetchAgencyPairs(filter: $filter) {
156
- gamePairId pair thumb ordering status maxCoef currentPrice currentVol24h
156
+ pairId pair thumb ordering status maxCoef currentPrice currentVol24h
157
157
  }
158
158
  }`;
159
159
 
@@ -479,8 +479,8 @@ var PLACE_BID_MUTATION = `mutation PlaceBid($input: PlaceBidInput!) {
479
479
  id user_id game_id currency amount coefficient time1 time2 price1 price2 status payout slippage created_at
480
480
  }
481
481
  }`;
482
- var MY_BIDS_QUERY = `query MyBids($statuses: [BidStatus!], $limit: Int, $offset: Int, $gameId: ID) {
483
- myBids(statuses: $statuses, limit: $limit, offset: $offset, gameId: $gameId) {
482
+ var MY_BIDS_QUERY = `query MyBids($statuses: [BidStatus!], $limit: Int, $offset: Int, $pairId: ID) {
483
+ myBids(statuses: $statuses, limit: $limit, offset: $offset, pairId: $pairId) {
484
484
  id user_id game_id currency amount coefficient time1 time2 price1 price2 status payout slippage created_at
485
485
  }
486
486
  }`;
@@ -508,7 +508,9 @@ var BidModule = class {
508
508
  async placeBid(input, opts) {
509
509
  const variables = {
510
510
  input: {
511
- game_id: input.gameId,
511
+ // REVIEW[bid-260602-v2]: input field pairId (was: game_id) — the #2 game_pairs.id.
512
+ // Server composes the agencyPairId composite and stores it in bids.game_id (frozen).
513
+ pairId: input.pairId,
512
514
  // wallet_id is forwarded as-is — grid-api enforces ownership +
513
515
  // currency validation. See openspec/bid-260518-wallet-id D1/D8.
514
516
  wallet_id: input.walletId,
@@ -532,7 +534,7 @@ var BidModule = class {
532
534
  if (err instanceof TaphubValidationError && err.code === "NotExist") {
533
535
  throw new TaphubValidationError("Game not found", {
534
536
  code: "GameNotFound",
535
- details: { gameId: input.gameId }
537
+ details: { pairId: input.pairId }
536
538
  });
537
539
  }
538
540
  throw err;
@@ -563,8 +565,8 @@ var BidModule = class {
563
565
  if (opts?.offset !== void 0) {
564
566
  variables.offset = opts.offset;
565
567
  }
566
- if (opts?.gameId !== void 0) {
567
- variables.gameId = opts.gameId;
568
+ if (opts?.pairId !== void 0) {
569
+ variables.pairId = opts.pairId;
568
570
  }
569
571
  const body = await this.#graphql.request(
570
572
  MY_BIDS_QUERY,
@@ -794,8 +796,8 @@ function normalisePairInfo(node) {
794
796
  }
795
797
 
796
798
  // src/modules/pair/queries.ts
797
- var PAIR_QUERY = `query Pair($pair: String!, $gameplaySlug: String) {
798
- pair(pair: $pair, gameplaySlug: $gameplaySlug) {
799
+ var PAIR_QUERY = `query Pair($pairId: ID!) {
800
+ pair(pairId: $pairId) {
799
801
  id pair status created_at
800
802
  config {
801
803
  gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
@@ -804,8 +806,8 @@ var PAIR_QUERY = `query Pair($pair: String!, $gameplaySlug: String) {
804
806
  }
805
807
  }
806
808
  }`;
807
- var CHART_HISTORY_QUERY = `query ChartHistory($agencyPairId: ID!, $limit: Int) {
808
- chartHistory(agencyPairId: $agencyPairId, limit: $limit) {
809
+ var CHART_HISTORY_QUERY = `query ChartHistory($pairId: ID!, $limit: Int) {
810
+ chartHistory(pairId: $pairId, limit: $limit) {
809
811
  time o h l c volatility coefMults
810
812
  }
811
813
  }`;
@@ -826,9 +828,11 @@ var PairModule = class {
826
828
  constructor(deps) {
827
829
  this.#graphql = deps.graphql;
828
830
  }
829
- async get(pair, opts) {
830
- const variables = { pair };
831
- if (opts?.gameplaySlug) variables.gameplaySlug = opts.gameplaySlug;
831
+ // REVIEW[bid-260602-v2]: keyed by pairId (the #2 game_pairs.id, e.g. "grid-ETH-USD").
832
+ // Dropped the `pair` symbol arg + `gameplaySlug` option — the gameplay is already
833
+ // encoded in pairId, and the server composes agencyPairId from agency context.
834
+ async get(pairId, opts) {
835
+ const variables = { pairId };
832
836
  let body;
833
837
  try {
834
838
  body = await this.#graphql.request(PAIR_QUERY, variables, opts);
@@ -836,7 +840,7 @@ var PairModule = class {
836
840
  if (err instanceof TaphubValidationError && err.code === "NotExist") {
837
841
  throw new TaphubValidationError("Game not found for pair", {
838
842
  code: "GameNotFound",
839
- details: { pair }
843
+ details: { pairId }
840
844
  });
841
845
  }
842
846
  throw err;
@@ -844,7 +848,7 @@ var PairModule = class {
844
848
  if (body.pair === null) {
845
849
  throw new TaphubValidationError("Game not found for pair", {
846
850
  code: "GameNotFound",
847
- details: { pair }
851
+ details: { pairId }
848
852
  });
849
853
  }
850
854
  return normalisePair(body.pair);
@@ -857,8 +861,8 @@ var PairModule = class {
857
861
  *
858
862
  * @example
859
863
  * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
860
- * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
861
- * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
864
+ * const pair = await client.pair.get(pairs[0].id);
865
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? pair.id, userId);
862
866
  */
863
867
  async availableGamePairs(opts) {
864
868
  const variables = {};
@@ -870,9 +874,9 @@ var PairModule = class {
870
874
  );
871
875
  return body.builderAvailableGamePairs.map(normalisePairInfo);
872
876
  }
873
- // REVIEW[bid-260602]: chartHistory param/variable renamed agencyPairId (was: gameId)
874
- async chartHistory(agencyPairId, limit, opts) {
875
- const variables = { agencyPairId };
877
+ // REVIEW[bid-260602-v2]: chartHistory param/variable renamed pairId (was: agencyPairId/gameId)
878
+ async chartHistory(pairId, limit, opts) {
879
+ const variables = { pairId };
876
880
  if (limit !== void 0) {
877
881
  variables.limit = limit;
878
882
  }
@@ -899,17 +903,18 @@ var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
899
903
  var TOPIC_PREFIX = "game";
900
904
  var MARKET_TOPIC_PREFIX = "market";
901
905
  var WALLET_TOPIC_PREFIX = "wallet";
906
+ var PUBLIC_PREFIX = "public";
902
907
  function walletBalanceTopic(userId) {
903
908
  return `${WALLET_TOPIC_PREFIX}/users/${userId}/balance`;
904
909
  }
905
- function agencyPairStatsTopic(aid, gamePairId) {
906
- return `public/agency/${aid}/pair/${gamePairId}/stats`;
910
+ function agencyPairStatsTopic(aid, pairId) {
911
+ return `public/agency/${aid}/pair/${pairId}/stats`;
907
912
  }
908
913
  function topicFor(gameId, suffix, userId) {
909
914
  if (USER_SCOPED_SUFFIXES.includes(suffix)) {
910
915
  return `${TOPIC_PREFIX}/${gameId}/user/${userId}/${suffix}`;
911
916
  }
912
- return `${TOPIC_PREFIX}/${gameId}/${suffix}`;
917
+ return `${PUBLIC_PREFIX}/${TOPIC_PREFIX}/${gameId}/${suffix}`;
913
918
  }
914
919
  function normaliseUserId(userId) {
915
920
  return userId && userId !== "" ? userId : null;
@@ -1063,28 +1068,28 @@ function createMqttTransport(endpoint, opts = {}) {
1063
1068
  mqttClient.subscribe(t, { qos: 1 });
1064
1069
  }
1065
1070
  },
1066
- subscribeCandle(pair, onCandle) {
1067
- const topic = `${MARKET_TOPIC_PREFIX}/${pair.replace(/\//g, "_")}/candle`;
1068
- if (candleSubscriptions.has(pair)) return;
1071
+ subscribeCandle(pairId, onCandle) {
1072
+ const topic = `${PUBLIC_PREFIX}/${MARKET_TOPIC_PREFIX}/${pairId}/candle`;
1073
+ if (candleSubscriptions.has(pairId)) return;
1069
1074
  const mqttClient = ensureConnected();
1070
- candleSubscriptions.set(pair, { topic, onCandle });
1075
+ candleSubscriptions.set(pairId, { topic, onCandle });
1071
1076
  mqttClient.subscribe(topic, { qos: 0 });
1072
1077
  },
1073
- unsubscribeCandle(pair) {
1074
- const entry = candleSubscriptions.get(pair);
1078
+ unsubscribeCandle(pairId) {
1079
+ const entry = candleSubscriptions.get(pairId);
1075
1080
  if (!entry) return;
1076
1081
  if (client) client.unsubscribe(entry.topic);
1077
- candleSubscriptions.delete(pair);
1082
+ candleSubscriptions.delete(pairId);
1078
1083
  },
1079
- subscribeAgencyPairStats(aid, gamePairId, onStats) {
1080
- const topic = agencyPairStatsTopic(aid, gamePairId);
1084
+ subscribeAgencyPairStats(aid, pairId, onStats) {
1085
+ const topic = agencyPairStatsTopic(aid, pairId);
1081
1086
  if (statsSubscriptions.has(topic)) return;
1082
1087
  const mqttClient = ensureConnected();
1083
1088
  statsSubscriptions.set(topic, { topic, onStats });
1084
1089
  mqttClient.subscribe(topic, { qos: 0 });
1085
1090
  },
1086
- unsubscribeAgencyPairStats(aid, gamePairId) {
1087
- const topic = agencyPairStatsTopic(aid, gamePairId);
1091
+ unsubscribeAgencyPairStats(aid, pairId) {
1092
+ const topic = agencyPairStatsTopic(aid, pairId);
1088
1093
  const entry = statsSubscriptions.get(topic);
1089
1094
  if (!entry) return;
1090
1095
  if (client) client.unsubscribe(entry.topic);
@@ -1420,22 +1425,25 @@ var RealtimeModule = class extends import_eventemitter33.default {
1420
1425
  this.#transport.unsubscribeWallet(cleanUserId);
1421
1426
  }
1422
1427
  /**
1423
- * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
1424
- * Independent of game/agency — candle is market-wide public data.
1428
+ * Subscribe to public market candle data for a pair, keyed by pairId
1429
+ * (the #2 game_pairs.id, e.g. "grid-ETH-USD"). Independent of game/agency —
1430
+ * candle is market-wide public data.
1425
1431
  */
1426
- subscribeCandle(pair, onCandle) {
1427
- this.#transport.subscribeCandle(pair, onCandle);
1432
+ // REVIEW[bid-260602-v2]: param pairId (was: pair) — slash-free #2 game_pairs.id.
1433
+ subscribeCandle(pairId, onCandle) {
1434
+ this.#transport.subscribeCandle(pairId, onCandle);
1428
1435
  }
1429
- /** Unsubscribe from candle data for a pair. */
1430
- unsubscribeCandle(pair) {
1431
- this.#transport.unsubscribeCandle(pair);
1436
+ /** Unsubscribe from candle data for a pairId. */
1437
+ unsubscribeCandle(pairId) {
1438
+ this.#transport.unsubscribeCandle(pairId);
1432
1439
  }
1433
1440
  /**
1434
1441
  * Subscribe to the live per-pair stats stream for the client's own agency.
1435
1442
  * Scoped to the agency the client was constructed with — there is no argument
1436
1443
  * to target another agency by design.
1437
1444
  */
1438
- subscribeAgencyPairStats(gamePairId, handler) {
1445
+ // REVIEW[bid-260602-v2]: param pairId (was: gamePairId) — the #2 game_pairs.id.
1446
+ subscribeAgencyPairStats(pairId, handler) {
1439
1447
  if (!this.#agencyId) {
1440
1448
  throw new TaphubError("agencyId is required to subscribe to agency pair stats", {
1441
1449
  code: "AgencyIdRequired"
@@ -1450,12 +1458,13 @@ var RealtimeModule = class extends import_eventemitter33.default {
1450
1458
  ts: Number(p.ts)
1451
1459
  });
1452
1460
  };
1453
- this.#transport.subscribeAgencyPairStats(this.#agencyId, gamePairId, onStats);
1461
+ this.#transport.subscribeAgencyPairStats(this.#agencyId, pairId, onStats);
1454
1462
  }
1455
1463
  /** Unsubscribe from the per-pair stats stream for the client's own agency. */
1456
- unsubscribeAgencyPairStats(gamePairId) {
1464
+ // REVIEW[bid-260602-v2]: param pairId (was: gamePairId).
1465
+ unsubscribeAgencyPairStats(pairId) {
1457
1466
  if (!this.#agencyId) return;
1458
- this.#transport.unsubscribeAgencyPairStats(this.#agencyId, gamePairId);
1467
+ this.#transport.unsubscribeAgencyPairStats(this.#agencyId, pairId);
1459
1468
  }
1460
1469
  disconnect() {
1461
1470
  for (const entry of this.#entries.values()) {
package/dist/index.d.mts CHANGED
@@ -71,8 +71,8 @@ interface AgencyPairFilter {
71
71
  status?: AgencyPairStatus;
72
72
  }
73
73
  interface AgencyPairStats {
74
- /** game_pairs.id slug, e.g. "grid-ETH-USD". Use to build the stats MQTT topic. */
75
- gamePairId: string;
74
+ /** game_pairs.id, e.g. "grid-ETH-USD". Use to build the stats MQTT topic. */
75
+ pairId: string;
76
76
  /** Display name, e.g. "ETH/USD". */
77
77
  pair: string;
78
78
  /** Icon URL. */
@@ -186,7 +186,7 @@ interface CancelBidResult {
186
186
  newBalance: string;
187
187
  }
188
188
  interface PlaceBidInput {
189
- gameId: string;
189
+ pairId: string;
190
190
  /**
191
191
  * UUID of the user's wallet to debit. Required (non-optional) since
192
192
  * grid-api's `PlaceBidInput.wallet_id` is NON_NULL (`ID!`). The SDK stays
@@ -230,7 +230,7 @@ declare class BidModule {
230
230
  statuses?: BidStatus[];
231
231
  limit?: number;
232
232
  offset?: number;
233
- gameId?: string;
233
+ pairId?: string;
234
234
  signal?: AbortSignal;
235
235
  }): Promise<Bid[]>;
236
236
  }
@@ -405,8 +405,7 @@ interface PairModuleDeps {
405
405
  declare class PairModule {
406
406
  #private;
407
407
  constructor(deps: PairModuleDeps);
408
- get(pair: string, opts?: {
409
- gameplaySlug?: string;
408
+ get(pairId: string, opts?: {
410
409
  signal?: AbortSignal;
411
410
  }): Promise<Pair>;
412
411
  /**
@@ -417,14 +416,14 @@ declare class PairModule {
417
416
  *
418
417
  * @example
419
418
  * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
420
- * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
421
- * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
419
+ * const pair = await client.pair.get(pairs[0].id);
420
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? pair.id, userId);
422
421
  */
423
422
  availableGamePairs(opts?: {
424
423
  gameplayId?: string;
425
424
  signal?: AbortSignal;
426
425
  }): Promise<PairInfo[]>;
427
- chartHistory(agencyPairId: string, limit?: number, opts?: {
426
+ chartHistory(pairId: string, limit?: number, opts?: {
428
427
  signal?: AbortSignal;
429
428
  }): Promise<Candle[]>;
430
429
  }
@@ -538,22 +537,23 @@ interface MqttTransport {
538
537
  */
539
538
  unsubscribeAll(gameId: string, userId?: string | null): void;
540
539
  /**
541
- * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
542
- * Independent of game/agency context candle is market-wide public data.
543
- * Idempotent: multiple calls for the same pair are deduplicated.
540
+ * Subscribe to public market candle data for a pair, keyed by pairId
541
+ * (the slash-free #2 game_pairs.id, e.g. "grid-ETH-USD"). Independent of
542
+ * game/agency context candle is market-wide public data.
543
+ * Idempotent: multiple calls for the same pairId are deduplicated.
544
544
  */
545
- subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
546
- /** Tear down the candle subscription for a pair. */
547
- unsubscribeCandle(pair: string): void;
545
+ subscribeCandle(pairId: string, onCandle: MqttCandleHandler): void;
546
+ /** Tear down the candle subscription for a pairId. */
547
+ unsubscribeCandle(pairId: string): void;
548
548
  /**
549
549
  * Subscribe to the public per-pair stats stream for an agency, topic
550
- * `public/agency/{aid}/pair/{gamePairId}/stats`. `aid` and `gamePairId` are
551
- * used raw (no slash escaping) to byte-match the backend publisher.
552
- * Idempotent: multiple calls for the same `(aid, gamePairId)` are deduplicated.
550
+ * `public/agency/{aid}/pair/{pairId}/stats`. `aid` and `pairId` are used raw
551
+ * (no slash escaping — pairId is slash-free) to byte-match the backend publisher.
552
+ * Idempotent: multiple calls for the same `(aid, pairId)` are deduplicated.
553
553
  */
554
- subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
555
- /** Tear down the stats subscription for an `(aid, gamePairId)`. */
556
- unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
554
+ subscribeAgencyPairStats(aid: string, pairId: string, onStats: MqttAgencyPairStatsHandler): void;
555
+ /** Tear down the stats subscription for an `(aid, pairId)`. */
556
+ unsubscribeAgencyPairStats(aid: string, pairId: string): void;
557
557
  /**
558
558
  * Subscribe to the user-scoped wallet balance stream, topic
559
559
  * `wallet/users/{userId}/balance`. Independent of any game subscription — NOT
@@ -667,7 +667,7 @@ interface MqttIdealConfigEvent {
667
667
  }
668
668
  /**
669
669
  * Live per-pair stats tick from the public stream
670
- * `public/agency/{aid}/pair/{gamePairId}/stats`. The backend suppresses
670
+ * `public/agency/{aid}/pair/{pairId}/stats`. The backend suppresses
671
671
  * unchanged ticks, so a subscriber may not receive an event until price or
672
672
  * vol24h actually moves — seed initial values from `agencyPairs.list()`.
673
673
  */
@@ -755,20 +755,21 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
755
755
  */
756
756
  unsubscribeWallet(userId: string): void;
757
757
  /**
758
- * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
759
- * Independent of game/agency — candle is market-wide public data.
758
+ * Subscribe to public market candle data for a pair, keyed by pairId
759
+ * (the #2 game_pairs.id, e.g. "grid-ETH-USD"). Independent of game/agency —
760
+ * candle is market-wide public data.
760
761
  */
761
- subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
762
- /** Unsubscribe from candle data for a pair. */
763
- unsubscribeCandle(pair: string): void;
762
+ subscribeCandle(pairId: string, onCandle: MqttCandleHandler): void;
763
+ /** Unsubscribe from candle data for a pairId. */
764
+ unsubscribeCandle(pairId: string): void;
764
765
  /**
765
766
  * Subscribe to the live per-pair stats stream for the client's own agency.
766
767
  * Scoped to the agency the client was constructed with — there is no argument
767
768
  * to target another agency by design.
768
769
  */
769
- subscribeAgencyPairStats(gamePairId: string, handler: (event: MqttAgencyPairStatsEvent) => void): void;
770
+ subscribeAgencyPairStats(pairId: string, handler: (event: MqttAgencyPairStatsEvent) => void): void;
770
771
  /** Unsubscribe from the per-pair stats stream for the client's own agency. */
771
- unsubscribeAgencyPairStats(gamePairId: string): void;
772
+ unsubscribeAgencyPairStats(pairId: string): void;
772
773
  disconnect(): void;
773
774
  }
774
775
 
package/dist/index.d.ts CHANGED
@@ -71,8 +71,8 @@ interface AgencyPairFilter {
71
71
  status?: AgencyPairStatus;
72
72
  }
73
73
  interface AgencyPairStats {
74
- /** game_pairs.id slug, e.g. "grid-ETH-USD". Use to build the stats MQTT topic. */
75
- gamePairId: string;
74
+ /** game_pairs.id, e.g. "grid-ETH-USD". Use to build the stats MQTT topic. */
75
+ pairId: string;
76
76
  /** Display name, e.g. "ETH/USD". */
77
77
  pair: string;
78
78
  /** Icon URL. */
@@ -186,7 +186,7 @@ interface CancelBidResult {
186
186
  newBalance: string;
187
187
  }
188
188
  interface PlaceBidInput {
189
- gameId: string;
189
+ pairId: string;
190
190
  /**
191
191
  * UUID of the user's wallet to debit. Required (non-optional) since
192
192
  * grid-api's `PlaceBidInput.wallet_id` is NON_NULL (`ID!`). The SDK stays
@@ -230,7 +230,7 @@ declare class BidModule {
230
230
  statuses?: BidStatus[];
231
231
  limit?: number;
232
232
  offset?: number;
233
- gameId?: string;
233
+ pairId?: string;
234
234
  signal?: AbortSignal;
235
235
  }): Promise<Bid[]>;
236
236
  }
@@ -405,8 +405,7 @@ interface PairModuleDeps {
405
405
  declare class PairModule {
406
406
  #private;
407
407
  constructor(deps: PairModuleDeps);
408
- get(pair: string, opts?: {
409
- gameplaySlug?: string;
408
+ get(pairId: string, opts?: {
410
409
  signal?: AbortSignal;
411
410
  }): Promise<Pair>;
412
411
  /**
@@ -417,14 +416,14 @@ declare class PairModule {
417
416
  *
418
417
  * @example
419
418
  * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
420
- * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
421
- * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
419
+ * const pair = await client.pair.get(pairs[0].id);
420
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? pair.id, userId);
422
421
  */
423
422
  availableGamePairs(opts?: {
424
423
  gameplayId?: string;
425
424
  signal?: AbortSignal;
426
425
  }): Promise<PairInfo[]>;
427
- chartHistory(agencyPairId: string, limit?: number, opts?: {
426
+ chartHistory(pairId: string, limit?: number, opts?: {
428
427
  signal?: AbortSignal;
429
428
  }): Promise<Candle[]>;
430
429
  }
@@ -538,22 +537,23 @@ interface MqttTransport {
538
537
  */
539
538
  unsubscribeAll(gameId: string, userId?: string | null): void;
540
539
  /**
541
- * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
542
- * Independent of game/agency context candle is market-wide public data.
543
- * Idempotent: multiple calls for the same pair are deduplicated.
540
+ * Subscribe to public market candle data for a pair, keyed by pairId
541
+ * (the slash-free #2 game_pairs.id, e.g. "grid-ETH-USD"). Independent of
542
+ * game/agency context candle is market-wide public data.
543
+ * Idempotent: multiple calls for the same pairId are deduplicated.
544
544
  */
545
- subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
546
- /** Tear down the candle subscription for a pair. */
547
- unsubscribeCandle(pair: string): void;
545
+ subscribeCandle(pairId: string, onCandle: MqttCandleHandler): void;
546
+ /** Tear down the candle subscription for a pairId. */
547
+ unsubscribeCandle(pairId: string): void;
548
548
  /**
549
549
  * Subscribe to the public per-pair stats stream for an agency, topic
550
- * `public/agency/{aid}/pair/{gamePairId}/stats`. `aid` and `gamePairId` are
551
- * used raw (no slash escaping) to byte-match the backend publisher.
552
- * Idempotent: multiple calls for the same `(aid, gamePairId)` are deduplicated.
550
+ * `public/agency/{aid}/pair/{pairId}/stats`. `aid` and `pairId` are used raw
551
+ * (no slash escaping — pairId is slash-free) to byte-match the backend publisher.
552
+ * Idempotent: multiple calls for the same `(aid, pairId)` are deduplicated.
553
553
  */
554
- subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
555
- /** Tear down the stats subscription for an `(aid, gamePairId)`. */
556
- unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
554
+ subscribeAgencyPairStats(aid: string, pairId: string, onStats: MqttAgencyPairStatsHandler): void;
555
+ /** Tear down the stats subscription for an `(aid, pairId)`. */
556
+ unsubscribeAgencyPairStats(aid: string, pairId: string): void;
557
557
  /**
558
558
  * Subscribe to the user-scoped wallet balance stream, topic
559
559
  * `wallet/users/{userId}/balance`. Independent of any game subscription — NOT
@@ -667,7 +667,7 @@ interface MqttIdealConfigEvent {
667
667
  }
668
668
  /**
669
669
  * Live per-pair stats tick from the public stream
670
- * `public/agency/{aid}/pair/{gamePairId}/stats`. The backend suppresses
670
+ * `public/agency/{aid}/pair/{pairId}/stats`. The backend suppresses
671
671
  * unchanged ticks, so a subscriber may not receive an event until price or
672
672
  * vol24h actually moves — seed initial values from `agencyPairs.list()`.
673
673
  */
@@ -755,20 +755,21 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
755
755
  */
756
756
  unsubscribeWallet(userId: string): void;
757
757
  /**
758
- * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
759
- * Independent of game/agency — candle is market-wide public data.
758
+ * Subscribe to public market candle data for a pair, keyed by pairId
759
+ * (the #2 game_pairs.id, e.g. "grid-ETH-USD"). Independent of game/agency —
760
+ * candle is market-wide public data.
760
761
  */
761
- subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
762
- /** Unsubscribe from candle data for a pair. */
763
- unsubscribeCandle(pair: string): void;
762
+ subscribeCandle(pairId: string, onCandle: MqttCandleHandler): void;
763
+ /** Unsubscribe from candle data for a pairId. */
764
+ unsubscribeCandle(pairId: string): void;
764
765
  /**
765
766
  * Subscribe to the live per-pair stats stream for the client's own agency.
766
767
  * Scoped to the agency the client was constructed with — there is no argument
767
768
  * to target another agency by design.
768
769
  */
769
- subscribeAgencyPairStats(gamePairId: string, handler: (event: MqttAgencyPairStatsEvent) => void): void;
770
+ subscribeAgencyPairStats(pairId: string, handler: (event: MqttAgencyPairStatsEvent) => void): void;
770
771
  /** Unsubscribe from the per-pair stats stream for the client's own agency. */
771
- unsubscribeAgencyPairStats(gamePairId: string): void;
772
+ unsubscribeAgencyPairStats(pairId: string): void;
772
773
  disconnect(): void;
773
774
  }
774
775
 
package/dist/index.js CHANGED
@@ -74,7 +74,7 @@ function normaliseAgencyPairs(rows) {
74
74
  });
75
75
  }
76
76
  return rows.map((row) => ({
77
- gamePairId: row.gamePairId,
77
+ pairId: row.pairId,
78
78
  pair: row.pair,
79
79
  thumb: row.thumb,
80
80
  ordering: Number(row.ordering),
@@ -88,7 +88,7 @@ function normaliseAgencyPairs(rows) {
88
88
  // src/modules/agencyPair/queries.ts
89
89
  var FETCH_AGENCY_PAIRS_QUERY = `query FetchAgencyPairs($filter: AgencyPairFilter) {
90
90
  fetchAgencyPairs(filter: $filter) {
91
- gamePairId pair thumb ordering status maxCoef currentPrice currentVol24h
91
+ pairId pair thumb ordering status maxCoef currentPrice currentVol24h
92
92
  }
93
93
  }`;
94
94
 
@@ -414,8 +414,8 @@ var PLACE_BID_MUTATION = `mutation PlaceBid($input: PlaceBidInput!) {
414
414
  id user_id game_id currency amount coefficient time1 time2 price1 price2 status payout slippage created_at
415
415
  }
416
416
  }`;
417
- var MY_BIDS_QUERY = `query MyBids($statuses: [BidStatus!], $limit: Int, $offset: Int, $gameId: ID) {
418
- myBids(statuses: $statuses, limit: $limit, offset: $offset, gameId: $gameId) {
417
+ var MY_BIDS_QUERY = `query MyBids($statuses: [BidStatus!], $limit: Int, $offset: Int, $pairId: ID) {
418
+ myBids(statuses: $statuses, limit: $limit, offset: $offset, pairId: $pairId) {
419
419
  id user_id game_id currency amount coefficient time1 time2 price1 price2 status payout slippage created_at
420
420
  }
421
421
  }`;
@@ -443,7 +443,9 @@ var BidModule = class {
443
443
  async placeBid(input, opts) {
444
444
  const variables = {
445
445
  input: {
446
- game_id: input.gameId,
446
+ // REVIEW[bid-260602-v2]: input field pairId (was: game_id) — the #2 game_pairs.id.
447
+ // Server composes the agencyPairId composite and stores it in bids.game_id (frozen).
448
+ pairId: input.pairId,
447
449
  // wallet_id is forwarded as-is — grid-api enforces ownership +
448
450
  // currency validation. See openspec/bid-260518-wallet-id D1/D8.
449
451
  wallet_id: input.walletId,
@@ -467,7 +469,7 @@ var BidModule = class {
467
469
  if (err instanceof TaphubValidationError && err.code === "NotExist") {
468
470
  throw new TaphubValidationError("Game not found", {
469
471
  code: "GameNotFound",
470
- details: { gameId: input.gameId }
472
+ details: { pairId: input.pairId }
471
473
  });
472
474
  }
473
475
  throw err;
@@ -498,8 +500,8 @@ var BidModule = class {
498
500
  if (opts?.offset !== void 0) {
499
501
  variables.offset = opts.offset;
500
502
  }
501
- if (opts?.gameId !== void 0) {
502
- variables.gameId = opts.gameId;
503
+ if (opts?.pairId !== void 0) {
504
+ variables.pairId = opts.pairId;
503
505
  }
504
506
  const body = await this.#graphql.request(
505
507
  MY_BIDS_QUERY,
@@ -729,8 +731,8 @@ function normalisePairInfo(node) {
729
731
  }
730
732
 
731
733
  // src/modules/pair/queries.ts
732
- var PAIR_QUERY = `query Pair($pair: String!, $gameplaySlug: String) {
733
- pair(pair: $pair, gameplaySlug: $gameplaySlug) {
734
+ var PAIR_QUERY = `query Pair($pairId: ID!) {
735
+ pair(pairId: $pairId) {
734
736
  id pair status created_at
735
737
  config {
736
738
  gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
@@ -739,8 +741,8 @@ var PAIR_QUERY = `query Pair($pair: String!, $gameplaySlug: String) {
739
741
  }
740
742
  }
741
743
  }`;
742
- var CHART_HISTORY_QUERY = `query ChartHistory($agencyPairId: ID!, $limit: Int) {
743
- chartHistory(agencyPairId: $agencyPairId, limit: $limit) {
744
+ var CHART_HISTORY_QUERY = `query ChartHistory($pairId: ID!, $limit: Int) {
745
+ chartHistory(pairId: $pairId, limit: $limit) {
744
746
  time o h l c volatility coefMults
745
747
  }
746
748
  }`;
@@ -761,9 +763,11 @@ var PairModule = class {
761
763
  constructor(deps) {
762
764
  this.#graphql = deps.graphql;
763
765
  }
764
- async get(pair, opts) {
765
- const variables = { pair };
766
- if (opts?.gameplaySlug) variables.gameplaySlug = opts.gameplaySlug;
766
+ // REVIEW[bid-260602-v2]: keyed by pairId (the #2 game_pairs.id, e.g. "grid-ETH-USD").
767
+ // Dropped the `pair` symbol arg + `gameplaySlug` option — the gameplay is already
768
+ // encoded in pairId, and the server composes agencyPairId from agency context.
769
+ async get(pairId, opts) {
770
+ const variables = { pairId };
767
771
  let body;
768
772
  try {
769
773
  body = await this.#graphql.request(PAIR_QUERY, variables, opts);
@@ -771,7 +775,7 @@ var PairModule = class {
771
775
  if (err instanceof TaphubValidationError && err.code === "NotExist") {
772
776
  throw new TaphubValidationError("Game not found for pair", {
773
777
  code: "GameNotFound",
774
- details: { pair }
778
+ details: { pairId }
775
779
  });
776
780
  }
777
781
  throw err;
@@ -779,7 +783,7 @@ var PairModule = class {
779
783
  if (body.pair === null) {
780
784
  throw new TaphubValidationError("Game not found for pair", {
781
785
  code: "GameNotFound",
782
- details: { pair }
786
+ details: { pairId }
783
787
  });
784
788
  }
785
789
  return normalisePair(body.pair);
@@ -792,8 +796,8 @@ var PairModule = class {
792
796
  *
793
797
  * @example
794
798
  * const pairs = await client.pair.availableGamePairs({ gameplayId: 'taptrading' });
795
- * const game = await client.pair.get(pairs[0].pair, { gameplaySlug: pairs[0].gameplayId });
796
- * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? game.id, userId);
799
+ * const pair = await client.pair.get(pairs[0].id);
800
+ * const ch = client.realtime?.subscribe(pairs[0].agencyPairId ?? pair.id, userId);
797
801
  */
798
802
  async availableGamePairs(opts) {
799
803
  const variables = {};
@@ -805,9 +809,9 @@ var PairModule = class {
805
809
  );
806
810
  return body.builderAvailableGamePairs.map(normalisePairInfo);
807
811
  }
808
- // REVIEW[bid-260602]: chartHistory param/variable renamed agencyPairId (was: gameId)
809
- async chartHistory(agencyPairId, limit, opts) {
810
- const variables = { agencyPairId };
812
+ // REVIEW[bid-260602-v2]: chartHistory param/variable renamed pairId (was: agencyPairId/gameId)
813
+ async chartHistory(pairId, limit, opts) {
814
+ const variables = { pairId };
811
815
  if (limit !== void 0) {
812
816
  variables.limit = limit;
813
817
  }
@@ -834,17 +838,18 @@ var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
834
838
  var TOPIC_PREFIX = "game";
835
839
  var MARKET_TOPIC_PREFIX = "market";
836
840
  var WALLET_TOPIC_PREFIX = "wallet";
841
+ var PUBLIC_PREFIX = "public";
837
842
  function walletBalanceTopic(userId) {
838
843
  return `${WALLET_TOPIC_PREFIX}/users/${userId}/balance`;
839
844
  }
840
- function agencyPairStatsTopic(aid, gamePairId) {
841
- return `public/agency/${aid}/pair/${gamePairId}/stats`;
845
+ function agencyPairStatsTopic(aid, pairId) {
846
+ return `public/agency/${aid}/pair/${pairId}/stats`;
842
847
  }
843
848
  function topicFor(gameId, suffix, userId) {
844
849
  if (USER_SCOPED_SUFFIXES.includes(suffix)) {
845
850
  return `${TOPIC_PREFIX}/${gameId}/user/${userId}/${suffix}`;
846
851
  }
847
- return `${TOPIC_PREFIX}/${gameId}/${suffix}`;
852
+ return `${PUBLIC_PREFIX}/${TOPIC_PREFIX}/${gameId}/${suffix}`;
848
853
  }
849
854
  function normaliseUserId(userId) {
850
855
  return userId && userId !== "" ? userId : null;
@@ -998,28 +1003,28 @@ function createMqttTransport(endpoint, opts = {}) {
998
1003
  mqttClient.subscribe(t, { qos: 1 });
999
1004
  }
1000
1005
  },
1001
- subscribeCandle(pair, onCandle) {
1002
- const topic = `${MARKET_TOPIC_PREFIX}/${pair.replace(/\//g, "_")}/candle`;
1003
- if (candleSubscriptions.has(pair)) return;
1006
+ subscribeCandle(pairId, onCandle) {
1007
+ const topic = `${PUBLIC_PREFIX}/${MARKET_TOPIC_PREFIX}/${pairId}/candle`;
1008
+ if (candleSubscriptions.has(pairId)) return;
1004
1009
  const mqttClient = ensureConnected();
1005
- candleSubscriptions.set(pair, { topic, onCandle });
1010
+ candleSubscriptions.set(pairId, { topic, onCandle });
1006
1011
  mqttClient.subscribe(topic, { qos: 0 });
1007
1012
  },
1008
- unsubscribeCandle(pair) {
1009
- const entry = candleSubscriptions.get(pair);
1013
+ unsubscribeCandle(pairId) {
1014
+ const entry = candleSubscriptions.get(pairId);
1010
1015
  if (!entry) return;
1011
1016
  if (client) client.unsubscribe(entry.topic);
1012
- candleSubscriptions.delete(pair);
1017
+ candleSubscriptions.delete(pairId);
1013
1018
  },
1014
- subscribeAgencyPairStats(aid, gamePairId, onStats) {
1015
- const topic = agencyPairStatsTopic(aid, gamePairId);
1019
+ subscribeAgencyPairStats(aid, pairId, onStats) {
1020
+ const topic = agencyPairStatsTopic(aid, pairId);
1016
1021
  if (statsSubscriptions.has(topic)) return;
1017
1022
  const mqttClient = ensureConnected();
1018
1023
  statsSubscriptions.set(topic, { topic, onStats });
1019
1024
  mqttClient.subscribe(topic, { qos: 0 });
1020
1025
  },
1021
- unsubscribeAgencyPairStats(aid, gamePairId) {
1022
- const topic = agencyPairStatsTopic(aid, gamePairId);
1026
+ unsubscribeAgencyPairStats(aid, pairId) {
1027
+ const topic = agencyPairStatsTopic(aid, pairId);
1023
1028
  const entry = statsSubscriptions.get(topic);
1024
1029
  if (!entry) return;
1025
1030
  if (client) client.unsubscribe(entry.topic);
@@ -1355,22 +1360,25 @@ var RealtimeModule = class extends EventEmitter3 {
1355
1360
  this.#transport.unsubscribeWallet(cleanUserId);
1356
1361
  }
1357
1362
  /**
1358
- * Subscribe to public market candle data for a pair (e.g. "ETH/USD").
1359
- * Independent of game/agency — candle is market-wide public data.
1363
+ * Subscribe to public market candle data for a pair, keyed by pairId
1364
+ * (the #2 game_pairs.id, e.g. "grid-ETH-USD"). Independent of game/agency —
1365
+ * candle is market-wide public data.
1360
1366
  */
1361
- subscribeCandle(pair, onCandle) {
1362
- this.#transport.subscribeCandle(pair, onCandle);
1367
+ // REVIEW[bid-260602-v2]: param pairId (was: pair) — slash-free #2 game_pairs.id.
1368
+ subscribeCandle(pairId, onCandle) {
1369
+ this.#transport.subscribeCandle(pairId, onCandle);
1363
1370
  }
1364
- /** Unsubscribe from candle data for a pair. */
1365
- unsubscribeCandle(pair) {
1366
- this.#transport.unsubscribeCandle(pair);
1371
+ /** Unsubscribe from candle data for a pairId. */
1372
+ unsubscribeCandle(pairId) {
1373
+ this.#transport.unsubscribeCandle(pairId);
1367
1374
  }
1368
1375
  /**
1369
1376
  * Subscribe to the live per-pair stats stream for the client's own agency.
1370
1377
  * Scoped to the agency the client was constructed with — there is no argument
1371
1378
  * to target another agency by design.
1372
1379
  */
1373
- subscribeAgencyPairStats(gamePairId, handler) {
1380
+ // REVIEW[bid-260602-v2]: param pairId (was: gamePairId) — the #2 game_pairs.id.
1381
+ subscribeAgencyPairStats(pairId, handler) {
1374
1382
  if (!this.#agencyId) {
1375
1383
  throw new TaphubError("agencyId is required to subscribe to agency pair stats", {
1376
1384
  code: "AgencyIdRequired"
@@ -1385,12 +1393,13 @@ var RealtimeModule = class extends EventEmitter3 {
1385
1393
  ts: Number(p.ts)
1386
1394
  });
1387
1395
  };
1388
- this.#transport.subscribeAgencyPairStats(this.#agencyId, gamePairId, onStats);
1396
+ this.#transport.subscribeAgencyPairStats(this.#agencyId, pairId, onStats);
1389
1397
  }
1390
1398
  /** Unsubscribe from the per-pair stats stream for the client's own agency. */
1391
- unsubscribeAgencyPairStats(gamePairId) {
1399
+ // REVIEW[bid-260602-v2]: param pairId (was: gamePairId).
1400
+ unsubscribeAgencyPairStats(pairId) {
1392
1401
  if (!this.#agencyId) return;
1393
- this.#transport.unsubscribeAgencyPairStats(this.#agencyId, gamePairId);
1402
+ this.#transport.unsubscribeAgencyPairStats(this.#agencyId, pairId);
1394
1403
  }
1395
1404
  disconnect() {
1396
1405
  for (const entry of this.#entries.values()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taphubhq/sdk-core",
3
- "version": "0.16.2",
3
+ "version": "0.17.1",
4
4
  "description": "Core SDK for building on the TabHub platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",