@taphubhq/sdk-core 0.14.0 → 0.14.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 +129 -0
- package/dist/index.d.mts +90 -2
- package/dist/index.d.ts +90 -2
- package/dist/index.js +128 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AgencyPairModule: () => AgencyPairModule,
|
|
33
34
|
AuthModule: () => AuthModule,
|
|
34
35
|
BidModule: () => BidModule,
|
|
35
36
|
GameModule: () => GameModule,
|
|
@@ -130,6 +131,61 @@ var TaphubEventBus = class {
|
|
|
130
131
|
}
|
|
131
132
|
};
|
|
132
133
|
|
|
134
|
+
// src/modules/agencyPair/normalise.ts
|
|
135
|
+
function normaliseAgencyPairs(rows) {
|
|
136
|
+
if (!Array.isArray(rows)) {
|
|
137
|
+
throw new TaphubServerError("Invalid response from server", {
|
|
138
|
+
code: "InvalidResponse"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return rows.map((row) => ({
|
|
142
|
+
gamePairId: row.gamePairId,
|
|
143
|
+
pair: row.pair,
|
|
144
|
+
thumb: row.thumb,
|
|
145
|
+
ordering: Number(row.ordering),
|
|
146
|
+
maxCoef: Number(row.maxCoef),
|
|
147
|
+
currentPrice: Number(row.currentPrice),
|
|
148
|
+
currentVol24h: Number(row.currentVol24h)
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/modules/agencyPair/queries.ts
|
|
153
|
+
var FETCH_AGENCY_PAIRS_QUERY = `query FetchAgencyPairs($filter: AgencyPairFilter) {
|
|
154
|
+
fetchAgencyPairs(filter: $filter) {
|
|
155
|
+
gamePairId pair thumb ordering maxCoef currentPrice currentVol24h
|
|
156
|
+
}
|
|
157
|
+
}`;
|
|
158
|
+
|
|
159
|
+
// src/modules/agencyPair/index.ts
|
|
160
|
+
var AgencyPairModule = class {
|
|
161
|
+
#graphql;
|
|
162
|
+
constructor(deps) {
|
|
163
|
+
this.#graphql = deps.graphql;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* List the agency-in-context's pairs with live derived stats.
|
|
167
|
+
*
|
|
168
|
+
* The agency is resolved server-side from the `x-builder-code` header the SDK
|
|
169
|
+
* sends on every request (no JWT required — Tier 0 anon-readable). There is no
|
|
170
|
+
* argument to target another agency by design.
|
|
171
|
+
*/
|
|
172
|
+
async list(args) {
|
|
173
|
+
const variables = {};
|
|
174
|
+
if (args?.filter) {
|
|
175
|
+
const filter = {};
|
|
176
|
+
if (args.filter.pair !== void 0) filter.pair = args.filter.pair;
|
|
177
|
+
if (args.filter.status !== void 0) filter.status = args.filter.status;
|
|
178
|
+
variables.filter = filter;
|
|
179
|
+
}
|
|
180
|
+
const body = await this.#graphql.request(
|
|
181
|
+
FETCH_AGENCY_PAIRS_QUERY,
|
|
182
|
+
variables,
|
|
183
|
+
args?.signal ? { signal: args.signal } : void 0
|
|
184
|
+
);
|
|
185
|
+
return normaliseAgencyPairs(body.fetchAgencyPairs);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
133
189
|
// src/modules/auth/normalise.ts
|
|
134
190
|
function normaliseGoogleResponse(body) {
|
|
135
191
|
return {
|
|
@@ -829,6 +885,9 @@ var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
|
|
|
829
885
|
var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
|
|
830
886
|
var TOPIC_PREFIX = "game";
|
|
831
887
|
var MARKET_TOPIC_PREFIX = "market";
|
|
888
|
+
function agencyPairStatsTopic(aid, gamePairId) {
|
|
889
|
+
return `public/agency/${aid}/pair/${gamePairId}/stats`;
|
|
890
|
+
}
|
|
832
891
|
function topicFor(gameId, suffix, userId) {
|
|
833
892
|
if (USER_SCOPED_SUFFIXES.includes(suffix)) {
|
|
834
893
|
return `${TOPIC_PREFIX}/${gameId}/user/${userId}/${suffix}`;
|
|
@@ -845,6 +904,7 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
845
904
|
let client = null;
|
|
846
905
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
847
906
|
const candleSubscriptions = /* @__PURE__ */ new Map();
|
|
907
|
+
const statsSubscriptions = /* @__PURE__ */ new Map();
|
|
848
908
|
const { onLifecycle } = opts;
|
|
849
909
|
let connectStartedAt = 0;
|
|
850
910
|
function fireLifecycle(event) {
|
|
@@ -902,6 +962,17 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
902
962
|
candleSub.onCandle(receivedTopic, payload2);
|
|
903
963
|
return;
|
|
904
964
|
}
|
|
965
|
+
const statsSub = statsSubscriptions.get(receivedTopic);
|
|
966
|
+
if (statsSub) {
|
|
967
|
+
let payload2;
|
|
968
|
+
try {
|
|
969
|
+
payload2 = JSON.parse(message.toString());
|
|
970
|
+
} catch {
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
statsSub.onStats(receivedTopic, payload2);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
905
976
|
let matched;
|
|
906
977
|
for (const sub of subscriptions.values()) {
|
|
907
978
|
if (sub.topics.includes(receivedTopic)) {
|
|
@@ -962,6 +1033,20 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
962
1033
|
if (client) client.unsubscribe(entry.topic);
|
|
963
1034
|
candleSubscriptions.delete(pair);
|
|
964
1035
|
},
|
|
1036
|
+
subscribeAgencyPairStats(aid, gamePairId, onStats) {
|
|
1037
|
+
const topic = agencyPairStatsTopic(aid, gamePairId);
|
|
1038
|
+
if (statsSubscriptions.has(topic)) return;
|
|
1039
|
+
const mqttClient = ensureConnected();
|
|
1040
|
+
statsSubscriptions.set(topic, { topic, onStats });
|
|
1041
|
+
mqttClient.subscribe(topic, { qos: 0 });
|
|
1042
|
+
},
|
|
1043
|
+
unsubscribeAgencyPairStats(aid, gamePairId) {
|
|
1044
|
+
const topic = agencyPairStatsTopic(aid, gamePairId);
|
|
1045
|
+
const entry = statsSubscriptions.get(topic);
|
|
1046
|
+
if (!entry) return;
|
|
1047
|
+
if (client) client.unsubscribe(entry.topic);
|
|
1048
|
+
statsSubscriptions.delete(topic);
|
|
1049
|
+
},
|
|
965
1050
|
unsubscribeAll(gameId, userId) {
|
|
966
1051
|
const matches = entriesForGame(gameId);
|
|
967
1052
|
if (matches.length === 0) return;
|
|
@@ -996,9 +1081,13 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
996
1081
|
for (const entry of candleSubscriptions.values()) {
|
|
997
1082
|
client.unsubscribe(entry.topic);
|
|
998
1083
|
}
|
|
1084
|
+
for (const entry of statsSubscriptions.values()) {
|
|
1085
|
+
client.unsubscribe(entry.topic);
|
|
1086
|
+
}
|
|
999
1087
|
}
|
|
1000
1088
|
subscriptions.clear();
|
|
1001
1089
|
candleSubscriptions.clear();
|
|
1090
|
+
statsSubscriptions.clear();
|
|
1002
1091
|
if (client) {
|
|
1003
1092
|
client.end(true);
|
|
1004
1093
|
client = null;
|
|
@@ -1119,11 +1208,13 @@ function normaliseUserId2(userId) {
|
|
|
1119
1208
|
var RealtimeModule = class extends import_eventemitter32.default {
|
|
1120
1209
|
#transport;
|
|
1121
1210
|
#entries = /* @__PURE__ */ new Map();
|
|
1211
|
+
#agencyId;
|
|
1122
1212
|
constructor(mqttEndpointOrOptions) {
|
|
1123
1213
|
super();
|
|
1124
1214
|
if (typeof mqttEndpointOrOptions === "string") {
|
|
1125
1215
|
this.#transport = createMqttTransport(mqttEndpointOrOptions);
|
|
1126
1216
|
} else {
|
|
1217
|
+
this.#agencyId = mqttEndpointOrOptions.agencyId;
|
|
1127
1218
|
this.#transport = mqttEndpointOrOptions.transport ?? createMqttTransport(mqttEndpointOrOptions.mqttEndpoint, {
|
|
1128
1219
|
onLifecycle: mqttEndpointOrOptions.onMqttLifecycle
|
|
1129
1220
|
});
|
|
@@ -1207,6 +1298,33 @@ var RealtimeModule = class extends import_eventemitter32.default {
|
|
|
1207
1298
|
unsubscribeCandle(pair) {
|
|
1208
1299
|
this.#transport.unsubscribeCandle(pair);
|
|
1209
1300
|
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Subscribe to the live per-pair stats stream for the client's own agency.
|
|
1303
|
+
* Scoped to the agency the client was constructed with — there is no argument
|
|
1304
|
+
* to target another agency by design.
|
|
1305
|
+
*/
|
|
1306
|
+
subscribeAgencyPairStats(gamePairId, handler) {
|
|
1307
|
+
if (!this.#agencyId) {
|
|
1308
|
+
throw new TaphubError("agencyId is required to subscribe to agency pair stats", {
|
|
1309
|
+
code: "AgencyIdRequired"
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
const onStats = (_topic, raw) => {
|
|
1313
|
+
const p = raw;
|
|
1314
|
+
handler({
|
|
1315
|
+
price: Number(p.price),
|
|
1316
|
+
vol24h: Number(p.vol24h),
|
|
1317
|
+
maxCoef: Number(p.maxCoef),
|
|
1318
|
+
ts: Number(p.ts)
|
|
1319
|
+
});
|
|
1320
|
+
};
|
|
1321
|
+
this.#transport.subscribeAgencyPairStats(this.#agencyId, gamePairId, onStats);
|
|
1322
|
+
}
|
|
1323
|
+
/** Unsubscribe from the per-pair stats stream for the client's own agency. */
|
|
1324
|
+
unsubscribeAgencyPairStats(gamePairId) {
|
|
1325
|
+
if (!this.#agencyId) return;
|
|
1326
|
+
this.#transport.unsubscribeAgencyPairStats(this.#agencyId, gamePairId);
|
|
1327
|
+
}
|
|
1210
1328
|
disconnect() {
|
|
1211
1329
|
for (const entry of this.#entries.values()) {
|
|
1212
1330
|
entry.channel.removeAllListeners();
|
|
@@ -2271,6 +2389,8 @@ var TaphubClient = class {
|
|
|
2271
2389
|
bid;
|
|
2272
2390
|
/** @readonly Leaderboard module — reassignment has no effect at runtime. */
|
|
2273
2391
|
leaderboard;
|
|
2392
|
+
/** @readonly Agency pair module — reassignment has no effect at runtime. */
|
|
2393
|
+
agencyPairs;
|
|
2274
2394
|
/** @readonly Locale module — reassignment has no effect at runtime. */
|
|
2275
2395
|
locale;
|
|
2276
2396
|
/** @readonly Realtime module — undefined when mqttEndpoint not configured. */
|
|
@@ -2351,9 +2471,11 @@ var TaphubClient = class {
|
|
|
2351
2471
|
this.game = new GameModule({ graphql: this.#graphql });
|
|
2352
2472
|
this.bid = new BidModule({ graphql: this.#graphql });
|
|
2353
2473
|
this.leaderboard = new LeaderboardModule({ graphql: this.#graphql });
|
|
2474
|
+
this.agencyPairs = new AgencyPairModule({ graphql: this.#graphql });
|
|
2354
2475
|
this.locale = new LocaleModule({ graphql: this.#graphql });
|
|
2355
2476
|
this.realtime = config.mqttEndpoint ? new RealtimeModule({
|
|
2356
2477
|
mqttEndpoint: config.mqttEndpoint,
|
|
2478
|
+
agencyId: this.agencyId,
|
|
2357
2479
|
onMqttLifecycle: createMqttProbe(this.network)
|
|
2358
2480
|
}) : void 0;
|
|
2359
2481
|
attachConnectionProbe(this.network);
|
|
@@ -2387,6 +2509,12 @@ var TaphubClient = class {
|
|
|
2387
2509
|
enumerable: true,
|
|
2388
2510
|
configurable: false
|
|
2389
2511
|
});
|
|
2512
|
+
Object.defineProperty(this, "agencyPairs", {
|
|
2513
|
+
value: this.agencyPairs,
|
|
2514
|
+
writable: false,
|
|
2515
|
+
enumerable: true,
|
|
2516
|
+
configurable: false
|
|
2517
|
+
});
|
|
2390
2518
|
if (this.realtime) {
|
|
2391
2519
|
Object.defineProperty(this, "realtime", {
|
|
2392
2520
|
value: this.realtime,
|
|
@@ -2523,6 +2651,7 @@ function calculateProbWin_v2(time1, time2, _price1, price2, creationTime, curren
|
|
|
2523
2651
|
}
|
|
2524
2652
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2525
2653
|
0 && (module.exports = {
|
|
2654
|
+
AgencyPairModule,
|
|
2526
2655
|
AuthModule,
|
|
2527
2656
|
BidModule,
|
|
2528
2657
|
GameModule,
|
package/dist/index.d.mts
CHANGED
|
@@ -63,6 +63,49 @@ interface GraphQLTransport {
|
|
|
63
63
|
request<T>(query: string, variables?: Record<string, unknown>, opts?: GraphQLRequestOpts): Promise<T>;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
type AgencyPairStatus = 'active' | 'paused' | 'ended';
|
|
67
|
+
interface AgencyPairFilter {
|
|
68
|
+
/** Exact match on the display pair name, e.g. "ETH/USD". */
|
|
69
|
+
pair?: string;
|
|
70
|
+
/** Exact match on the agency pair status. */
|
|
71
|
+
status?: AgencyPairStatus;
|
|
72
|
+
}
|
|
73
|
+
interface AgencyPairStats {
|
|
74
|
+
/** game_pairs.id slug, e.g. "grid-ETH-USD". Use to build the stats MQTT topic. */
|
|
75
|
+
gamePairId: string;
|
|
76
|
+
/** Display name, e.g. "ETH/USD". */
|
|
77
|
+
pair: string;
|
|
78
|
+
/** Icon URL. */
|
|
79
|
+
thumb: string;
|
|
80
|
+
/** Lower = earlier in the displayed list. */
|
|
81
|
+
ordering: number;
|
|
82
|
+
/** config.constraints.maxCoef. */
|
|
83
|
+
maxCoef: number;
|
|
84
|
+
/** Latest candle close price; 0 when no candle exists. */
|
|
85
|
+
currentPrice: number;
|
|
86
|
+
/** Sliding 24h bid-amount sum; 0 when the worker snapshot is missing or stale. */
|
|
87
|
+
currentVol24h: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface AgencyPairModuleDeps {
|
|
91
|
+
graphql: GraphQLTransport;
|
|
92
|
+
}
|
|
93
|
+
declare class AgencyPairModule {
|
|
94
|
+
#private;
|
|
95
|
+
constructor(deps: AgencyPairModuleDeps);
|
|
96
|
+
/**
|
|
97
|
+
* List the agency-in-context's pairs with live derived stats.
|
|
98
|
+
*
|
|
99
|
+
* The agency is resolved server-side from the `x-builder-code` header the SDK
|
|
100
|
+
* sends on every request (no JWT required — Tier 0 anon-readable). There is no
|
|
101
|
+
* argument to target another agency by design.
|
|
102
|
+
*/
|
|
103
|
+
list(args?: {
|
|
104
|
+
filter?: AgencyPairFilter;
|
|
105
|
+
signal?: AbortSignal;
|
|
106
|
+
}): Promise<AgencyPairStats[]>;
|
|
107
|
+
}
|
|
108
|
+
|
|
66
109
|
interface RestRequestOpts {
|
|
67
110
|
signal?: AbortSignal;
|
|
68
111
|
}
|
|
@@ -433,7 +476,13 @@ interface MqttWireConfig {
|
|
|
433
476
|
max_bid_amount: number;
|
|
434
477
|
acceptable_bids: number[];
|
|
435
478
|
}
|
|
436
|
-
|
|
479
|
+
interface MqttWireAgencyPairStats {
|
|
480
|
+
price: number;
|
|
481
|
+
vol24h: number;
|
|
482
|
+
maxCoef: number;
|
|
483
|
+
ts: number;
|
|
484
|
+
}
|
|
485
|
+
type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats;
|
|
437
486
|
type MqttMessageHandler = (gameId: string, topic: string, payload: MqttWirePayload) => void;
|
|
438
487
|
type MqttErrorHandler = (err: Error) => void;
|
|
439
488
|
type MqttLifecycleEvent = {
|
|
@@ -453,6 +502,7 @@ type MqttLifecycleEvent = {
|
|
|
453
502
|
};
|
|
454
503
|
type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
|
|
455
504
|
type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
505
|
+
type MqttAgencyPairStatsHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
456
506
|
interface MqttTransport {
|
|
457
507
|
/**
|
|
458
508
|
* Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
|
|
@@ -477,6 +527,15 @@ interface MqttTransport {
|
|
|
477
527
|
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
478
528
|
/** Tear down the candle subscription for a pair. */
|
|
479
529
|
unsubscribeCandle(pair: string): void;
|
|
530
|
+
/**
|
|
531
|
+
* Subscribe to the public per-pair stats stream for an agency, topic
|
|
532
|
+
* `public/agency/{aid}/pair/{gamePairId}/stats`. `aid` and `gamePairId` are
|
|
533
|
+
* used raw (no slash escaping) to byte-match the backend publisher.
|
|
534
|
+
* Idempotent: multiple calls for the same `(aid, gamePairId)` are deduplicated.
|
|
535
|
+
*/
|
|
536
|
+
subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
|
|
537
|
+
/** Tear down the stats subscription for an `(aid, gamePairId)`. */
|
|
538
|
+
unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
|
|
480
539
|
close(): void;
|
|
481
540
|
}
|
|
482
541
|
|
|
@@ -549,6 +608,19 @@ interface MqttIdealConfigEvent {
|
|
|
549
608
|
currentPrice: number;
|
|
550
609
|
reason: string;
|
|
551
610
|
}
|
|
611
|
+
/**
|
|
612
|
+
* Live per-pair stats tick from the public stream
|
|
613
|
+
* `public/agency/{aid}/pair/{gamePairId}/stats`. The backend suppresses
|
|
614
|
+
* unchanged ticks, so a subscriber may not receive an event until price or
|
|
615
|
+
* vol24h actually moves — seed initial values from `agencyPairs.list()`.
|
|
616
|
+
*/
|
|
617
|
+
interface MqttAgencyPairStatsEvent {
|
|
618
|
+
price: number;
|
|
619
|
+
vol24h: number;
|
|
620
|
+
maxCoef: number;
|
|
621
|
+
/** Unix epoch **seconds** (matches the backend snapshot `ts`). */
|
|
622
|
+
ts: number;
|
|
623
|
+
}
|
|
552
624
|
|
|
553
625
|
interface GameChannelEvents {
|
|
554
626
|
candle: [MqttCandleEvent];
|
|
@@ -568,6 +640,12 @@ declare class GameChannel extends EventEmitter<GameChannelEvents> {
|
|
|
568
640
|
|
|
569
641
|
interface RealtimeModuleOptions {
|
|
570
642
|
mqttEndpoint: string;
|
|
643
|
+
/**
|
|
644
|
+
* The client's own agency id. Threaded so `subscribeAgencyPairStats` can
|
|
645
|
+
* scope the stats topic to this agency without accepting a target agency from
|
|
646
|
+
* the caller (tenant-scoping mirror of the GQL resolver).
|
|
647
|
+
*/
|
|
648
|
+
agencyId?: string;
|
|
571
649
|
/** @internal For testing only */
|
|
572
650
|
transport?: MqttTransport;
|
|
573
651
|
/**
|
|
@@ -594,6 +672,14 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
|
|
|
594
672
|
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
595
673
|
/** Unsubscribe from candle data for a pair. */
|
|
596
674
|
unsubscribeCandle(pair: string): void;
|
|
675
|
+
/**
|
|
676
|
+
* Subscribe to the live per-pair stats stream for the client's own agency.
|
|
677
|
+
* Scoped to the agency the client was constructed with — there is no argument
|
|
678
|
+
* to target another agency by design.
|
|
679
|
+
*/
|
|
680
|
+
subscribeAgencyPairStats(gamePairId: string, handler: (event: MqttAgencyPairStatsEvent) => void): void;
|
|
681
|
+
/** Unsubscribe from the per-pair stats stream for the client's own agency. */
|
|
682
|
+
unsubscribeAgencyPairStats(gamePairId: string): void;
|
|
597
683
|
disconnect(): void;
|
|
598
684
|
}
|
|
599
685
|
|
|
@@ -702,6 +788,8 @@ declare class TaphubClient {
|
|
|
702
788
|
bid: BidModule;
|
|
703
789
|
/** @readonly Leaderboard module — reassignment has no effect at runtime. */
|
|
704
790
|
leaderboard: LeaderboardModule;
|
|
791
|
+
/** @readonly Agency pair module — reassignment has no effect at runtime. */
|
|
792
|
+
agencyPairs: AgencyPairModule;
|
|
705
793
|
/** @readonly Locale module — reassignment has no effect at runtime. */
|
|
706
794
|
locale: LocaleModule;
|
|
707
795
|
/** @readonly Realtime module — undefined when mqttEndpoint not configured. */
|
|
@@ -791,4 +879,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
|
|
|
791
879
|
declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
792
880
|
declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
793
881
|
|
|
794
|
-
export { 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 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 };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -63,6 +63,49 @@ interface GraphQLTransport {
|
|
|
63
63
|
request<T>(query: string, variables?: Record<string, unknown>, opts?: GraphQLRequestOpts): Promise<T>;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
type AgencyPairStatus = 'active' | 'paused' | 'ended';
|
|
67
|
+
interface AgencyPairFilter {
|
|
68
|
+
/** Exact match on the display pair name, e.g. "ETH/USD". */
|
|
69
|
+
pair?: string;
|
|
70
|
+
/** Exact match on the agency pair status. */
|
|
71
|
+
status?: AgencyPairStatus;
|
|
72
|
+
}
|
|
73
|
+
interface AgencyPairStats {
|
|
74
|
+
/** game_pairs.id slug, e.g. "grid-ETH-USD". Use to build the stats MQTT topic. */
|
|
75
|
+
gamePairId: string;
|
|
76
|
+
/** Display name, e.g. "ETH/USD". */
|
|
77
|
+
pair: string;
|
|
78
|
+
/** Icon URL. */
|
|
79
|
+
thumb: string;
|
|
80
|
+
/** Lower = earlier in the displayed list. */
|
|
81
|
+
ordering: number;
|
|
82
|
+
/** config.constraints.maxCoef. */
|
|
83
|
+
maxCoef: number;
|
|
84
|
+
/** Latest candle close price; 0 when no candle exists. */
|
|
85
|
+
currentPrice: number;
|
|
86
|
+
/** Sliding 24h bid-amount sum; 0 when the worker snapshot is missing or stale. */
|
|
87
|
+
currentVol24h: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface AgencyPairModuleDeps {
|
|
91
|
+
graphql: GraphQLTransport;
|
|
92
|
+
}
|
|
93
|
+
declare class AgencyPairModule {
|
|
94
|
+
#private;
|
|
95
|
+
constructor(deps: AgencyPairModuleDeps);
|
|
96
|
+
/**
|
|
97
|
+
* List the agency-in-context's pairs with live derived stats.
|
|
98
|
+
*
|
|
99
|
+
* The agency is resolved server-side from the `x-builder-code` header the SDK
|
|
100
|
+
* sends on every request (no JWT required — Tier 0 anon-readable). There is no
|
|
101
|
+
* argument to target another agency by design.
|
|
102
|
+
*/
|
|
103
|
+
list(args?: {
|
|
104
|
+
filter?: AgencyPairFilter;
|
|
105
|
+
signal?: AbortSignal;
|
|
106
|
+
}): Promise<AgencyPairStats[]>;
|
|
107
|
+
}
|
|
108
|
+
|
|
66
109
|
interface RestRequestOpts {
|
|
67
110
|
signal?: AbortSignal;
|
|
68
111
|
}
|
|
@@ -433,7 +476,13 @@ interface MqttWireConfig {
|
|
|
433
476
|
max_bid_amount: number;
|
|
434
477
|
acceptable_bids: number[];
|
|
435
478
|
}
|
|
436
|
-
|
|
479
|
+
interface MqttWireAgencyPairStats {
|
|
480
|
+
price: number;
|
|
481
|
+
vol24h: number;
|
|
482
|
+
maxCoef: number;
|
|
483
|
+
ts: number;
|
|
484
|
+
}
|
|
485
|
+
type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats;
|
|
437
486
|
type MqttMessageHandler = (gameId: string, topic: string, payload: MqttWirePayload) => void;
|
|
438
487
|
type MqttErrorHandler = (err: Error) => void;
|
|
439
488
|
type MqttLifecycleEvent = {
|
|
@@ -453,6 +502,7 @@ type MqttLifecycleEvent = {
|
|
|
453
502
|
};
|
|
454
503
|
type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
|
|
455
504
|
type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
505
|
+
type MqttAgencyPairStatsHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
456
506
|
interface MqttTransport {
|
|
457
507
|
/**
|
|
458
508
|
* Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
|
|
@@ -477,6 +527,15 @@ interface MqttTransport {
|
|
|
477
527
|
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
478
528
|
/** Tear down the candle subscription for a pair. */
|
|
479
529
|
unsubscribeCandle(pair: string): void;
|
|
530
|
+
/**
|
|
531
|
+
* Subscribe to the public per-pair stats stream for an agency, topic
|
|
532
|
+
* `public/agency/{aid}/pair/{gamePairId}/stats`. `aid` and `gamePairId` are
|
|
533
|
+
* used raw (no slash escaping) to byte-match the backend publisher.
|
|
534
|
+
* Idempotent: multiple calls for the same `(aid, gamePairId)` are deduplicated.
|
|
535
|
+
*/
|
|
536
|
+
subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
|
|
537
|
+
/** Tear down the stats subscription for an `(aid, gamePairId)`. */
|
|
538
|
+
unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
|
|
480
539
|
close(): void;
|
|
481
540
|
}
|
|
482
541
|
|
|
@@ -549,6 +608,19 @@ interface MqttIdealConfigEvent {
|
|
|
549
608
|
currentPrice: number;
|
|
550
609
|
reason: string;
|
|
551
610
|
}
|
|
611
|
+
/**
|
|
612
|
+
* Live per-pair stats tick from the public stream
|
|
613
|
+
* `public/agency/{aid}/pair/{gamePairId}/stats`. The backend suppresses
|
|
614
|
+
* unchanged ticks, so a subscriber may not receive an event until price or
|
|
615
|
+
* vol24h actually moves — seed initial values from `agencyPairs.list()`.
|
|
616
|
+
*/
|
|
617
|
+
interface MqttAgencyPairStatsEvent {
|
|
618
|
+
price: number;
|
|
619
|
+
vol24h: number;
|
|
620
|
+
maxCoef: number;
|
|
621
|
+
/** Unix epoch **seconds** (matches the backend snapshot `ts`). */
|
|
622
|
+
ts: number;
|
|
623
|
+
}
|
|
552
624
|
|
|
553
625
|
interface GameChannelEvents {
|
|
554
626
|
candle: [MqttCandleEvent];
|
|
@@ -568,6 +640,12 @@ declare class GameChannel extends EventEmitter<GameChannelEvents> {
|
|
|
568
640
|
|
|
569
641
|
interface RealtimeModuleOptions {
|
|
570
642
|
mqttEndpoint: string;
|
|
643
|
+
/**
|
|
644
|
+
* The client's own agency id. Threaded so `subscribeAgencyPairStats` can
|
|
645
|
+
* scope the stats topic to this agency without accepting a target agency from
|
|
646
|
+
* the caller (tenant-scoping mirror of the GQL resolver).
|
|
647
|
+
*/
|
|
648
|
+
agencyId?: string;
|
|
571
649
|
/** @internal For testing only */
|
|
572
650
|
transport?: MqttTransport;
|
|
573
651
|
/**
|
|
@@ -594,6 +672,14 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
|
|
|
594
672
|
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
595
673
|
/** Unsubscribe from candle data for a pair. */
|
|
596
674
|
unsubscribeCandle(pair: string): void;
|
|
675
|
+
/**
|
|
676
|
+
* Subscribe to the live per-pair stats stream for the client's own agency.
|
|
677
|
+
* Scoped to the agency the client was constructed with — there is no argument
|
|
678
|
+
* to target another agency by design.
|
|
679
|
+
*/
|
|
680
|
+
subscribeAgencyPairStats(gamePairId: string, handler: (event: MqttAgencyPairStatsEvent) => void): void;
|
|
681
|
+
/** Unsubscribe from the per-pair stats stream for the client's own agency. */
|
|
682
|
+
unsubscribeAgencyPairStats(gamePairId: string): void;
|
|
597
683
|
disconnect(): void;
|
|
598
684
|
}
|
|
599
685
|
|
|
@@ -702,6 +788,8 @@ declare class TaphubClient {
|
|
|
702
788
|
bid: BidModule;
|
|
703
789
|
/** @readonly Leaderboard module — reassignment has no effect at runtime. */
|
|
704
790
|
leaderboard: LeaderboardModule;
|
|
791
|
+
/** @readonly Agency pair module — reassignment has no effect at runtime. */
|
|
792
|
+
agencyPairs: AgencyPairModule;
|
|
705
793
|
/** @readonly Locale module — reassignment has no effect at runtime. */
|
|
706
794
|
locale: LocaleModule;
|
|
707
795
|
/** @readonly Realtime module — undefined when mqttEndpoint not configured. */
|
|
@@ -791,4 +879,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
|
|
|
791
879
|
declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
792
880
|
declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
793
881
|
|
|
794
|
-
export { 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 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,61 @@ var TaphubEventBus = class {
|
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
// src/modules/agencyPair/normalise.ts
|
|
70
|
+
function normaliseAgencyPairs(rows) {
|
|
71
|
+
if (!Array.isArray(rows)) {
|
|
72
|
+
throw new TaphubServerError("Invalid response from server", {
|
|
73
|
+
code: "InvalidResponse"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return rows.map((row) => ({
|
|
77
|
+
gamePairId: row.gamePairId,
|
|
78
|
+
pair: row.pair,
|
|
79
|
+
thumb: row.thumb,
|
|
80
|
+
ordering: Number(row.ordering),
|
|
81
|
+
maxCoef: Number(row.maxCoef),
|
|
82
|
+
currentPrice: Number(row.currentPrice),
|
|
83
|
+
currentVol24h: Number(row.currentVol24h)
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/modules/agencyPair/queries.ts
|
|
88
|
+
var FETCH_AGENCY_PAIRS_QUERY = `query FetchAgencyPairs($filter: AgencyPairFilter) {
|
|
89
|
+
fetchAgencyPairs(filter: $filter) {
|
|
90
|
+
gamePairId pair thumb ordering maxCoef currentPrice currentVol24h
|
|
91
|
+
}
|
|
92
|
+
}`;
|
|
93
|
+
|
|
94
|
+
// src/modules/agencyPair/index.ts
|
|
95
|
+
var AgencyPairModule = class {
|
|
96
|
+
#graphql;
|
|
97
|
+
constructor(deps) {
|
|
98
|
+
this.#graphql = deps.graphql;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* List the agency-in-context's pairs with live derived stats.
|
|
102
|
+
*
|
|
103
|
+
* The agency is resolved server-side from the `x-builder-code` header the SDK
|
|
104
|
+
* sends on every request (no JWT required — Tier 0 anon-readable). There is no
|
|
105
|
+
* argument to target another agency by design.
|
|
106
|
+
*/
|
|
107
|
+
async list(args) {
|
|
108
|
+
const variables = {};
|
|
109
|
+
if (args?.filter) {
|
|
110
|
+
const filter = {};
|
|
111
|
+
if (args.filter.pair !== void 0) filter.pair = args.filter.pair;
|
|
112
|
+
if (args.filter.status !== void 0) filter.status = args.filter.status;
|
|
113
|
+
variables.filter = filter;
|
|
114
|
+
}
|
|
115
|
+
const body = await this.#graphql.request(
|
|
116
|
+
FETCH_AGENCY_PAIRS_QUERY,
|
|
117
|
+
variables,
|
|
118
|
+
args?.signal ? { signal: args.signal } : void 0
|
|
119
|
+
);
|
|
120
|
+
return normaliseAgencyPairs(body.fetchAgencyPairs);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
69
124
|
// src/modules/auth/normalise.ts
|
|
70
125
|
function normaliseGoogleResponse(body) {
|
|
71
126
|
return {
|
|
@@ -765,6 +820,9 @@ var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
|
|
|
765
820
|
var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
|
|
766
821
|
var TOPIC_PREFIX = "game";
|
|
767
822
|
var MARKET_TOPIC_PREFIX = "market";
|
|
823
|
+
function agencyPairStatsTopic(aid, gamePairId) {
|
|
824
|
+
return `public/agency/${aid}/pair/${gamePairId}/stats`;
|
|
825
|
+
}
|
|
768
826
|
function topicFor(gameId, suffix, userId) {
|
|
769
827
|
if (USER_SCOPED_SUFFIXES.includes(suffix)) {
|
|
770
828
|
return `${TOPIC_PREFIX}/${gameId}/user/${userId}/${suffix}`;
|
|
@@ -781,6 +839,7 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
781
839
|
let client = null;
|
|
782
840
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
783
841
|
const candleSubscriptions = /* @__PURE__ */ new Map();
|
|
842
|
+
const statsSubscriptions = /* @__PURE__ */ new Map();
|
|
784
843
|
const { onLifecycle } = opts;
|
|
785
844
|
let connectStartedAt = 0;
|
|
786
845
|
function fireLifecycle(event) {
|
|
@@ -838,6 +897,17 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
838
897
|
candleSub.onCandle(receivedTopic, payload2);
|
|
839
898
|
return;
|
|
840
899
|
}
|
|
900
|
+
const statsSub = statsSubscriptions.get(receivedTopic);
|
|
901
|
+
if (statsSub) {
|
|
902
|
+
let payload2;
|
|
903
|
+
try {
|
|
904
|
+
payload2 = JSON.parse(message.toString());
|
|
905
|
+
} catch {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
statsSub.onStats(receivedTopic, payload2);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
841
911
|
let matched;
|
|
842
912
|
for (const sub of subscriptions.values()) {
|
|
843
913
|
if (sub.topics.includes(receivedTopic)) {
|
|
@@ -898,6 +968,20 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
898
968
|
if (client) client.unsubscribe(entry.topic);
|
|
899
969
|
candleSubscriptions.delete(pair);
|
|
900
970
|
},
|
|
971
|
+
subscribeAgencyPairStats(aid, gamePairId, onStats) {
|
|
972
|
+
const topic = agencyPairStatsTopic(aid, gamePairId);
|
|
973
|
+
if (statsSubscriptions.has(topic)) return;
|
|
974
|
+
const mqttClient = ensureConnected();
|
|
975
|
+
statsSubscriptions.set(topic, { topic, onStats });
|
|
976
|
+
mqttClient.subscribe(topic, { qos: 0 });
|
|
977
|
+
},
|
|
978
|
+
unsubscribeAgencyPairStats(aid, gamePairId) {
|
|
979
|
+
const topic = agencyPairStatsTopic(aid, gamePairId);
|
|
980
|
+
const entry = statsSubscriptions.get(topic);
|
|
981
|
+
if (!entry) return;
|
|
982
|
+
if (client) client.unsubscribe(entry.topic);
|
|
983
|
+
statsSubscriptions.delete(topic);
|
|
984
|
+
},
|
|
901
985
|
unsubscribeAll(gameId, userId) {
|
|
902
986
|
const matches = entriesForGame(gameId);
|
|
903
987
|
if (matches.length === 0) return;
|
|
@@ -932,9 +1016,13 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
932
1016
|
for (const entry of candleSubscriptions.values()) {
|
|
933
1017
|
client.unsubscribe(entry.topic);
|
|
934
1018
|
}
|
|
1019
|
+
for (const entry of statsSubscriptions.values()) {
|
|
1020
|
+
client.unsubscribe(entry.topic);
|
|
1021
|
+
}
|
|
935
1022
|
}
|
|
936
1023
|
subscriptions.clear();
|
|
937
1024
|
candleSubscriptions.clear();
|
|
1025
|
+
statsSubscriptions.clear();
|
|
938
1026
|
if (client) {
|
|
939
1027
|
client.end(true);
|
|
940
1028
|
client = null;
|
|
@@ -1055,11 +1143,13 @@ function normaliseUserId2(userId) {
|
|
|
1055
1143
|
var RealtimeModule = class extends EventEmitter2 {
|
|
1056
1144
|
#transport;
|
|
1057
1145
|
#entries = /* @__PURE__ */ new Map();
|
|
1146
|
+
#agencyId;
|
|
1058
1147
|
constructor(mqttEndpointOrOptions) {
|
|
1059
1148
|
super();
|
|
1060
1149
|
if (typeof mqttEndpointOrOptions === "string") {
|
|
1061
1150
|
this.#transport = createMqttTransport(mqttEndpointOrOptions);
|
|
1062
1151
|
} else {
|
|
1152
|
+
this.#agencyId = mqttEndpointOrOptions.agencyId;
|
|
1063
1153
|
this.#transport = mqttEndpointOrOptions.transport ?? createMqttTransport(mqttEndpointOrOptions.mqttEndpoint, {
|
|
1064
1154
|
onLifecycle: mqttEndpointOrOptions.onMqttLifecycle
|
|
1065
1155
|
});
|
|
@@ -1143,6 +1233,33 @@ var RealtimeModule = class extends EventEmitter2 {
|
|
|
1143
1233
|
unsubscribeCandle(pair) {
|
|
1144
1234
|
this.#transport.unsubscribeCandle(pair);
|
|
1145
1235
|
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Subscribe to the live per-pair stats stream for the client's own agency.
|
|
1238
|
+
* Scoped to the agency the client was constructed with — there is no argument
|
|
1239
|
+
* to target another agency by design.
|
|
1240
|
+
*/
|
|
1241
|
+
subscribeAgencyPairStats(gamePairId, handler) {
|
|
1242
|
+
if (!this.#agencyId) {
|
|
1243
|
+
throw new TaphubError("agencyId is required to subscribe to agency pair stats", {
|
|
1244
|
+
code: "AgencyIdRequired"
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
const onStats = (_topic, raw) => {
|
|
1248
|
+
const p = raw;
|
|
1249
|
+
handler({
|
|
1250
|
+
price: Number(p.price),
|
|
1251
|
+
vol24h: Number(p.vol24h),
|
|
1252
|
+
maxCoef: Number(p.maxCoef),
|
|
1253
|
+
ts: Number(p.ts)
|
|
1254
|
+
});
|
|
1255
|
+
};
|
|
1256
|
+
this.#transport.subscribeAgencyPairStats(this.#agencyId, gamePairId, onStats);
|
|
1257
|
+
}
|
|
1258
|
+
/** Unsubscribe from the per-pair stats stream for the client's own agency. */
|
|
1259
|
+
unsubscribeAgencyPairStats(gamePairId) {
|
|
1260
|
+
if (!this.#agencyId) return;
|
|
1261
|
+
this.#transport.unsubscribeAgencyPairStats(this.#agencyId, gamePairId);
|
|
1262
|
+
}
|
|
1146
1263
|
disconnect() {
|
|
1147
1264
|
for (const entry of this.#entries.values()) {
|
|
1148
1265
|
entry.channel.removeAllListeners();
|
|
@@ -2207,6 +2324,8 @@ var TaphubClient = class {
|
|
|
2207
2324
|
bid;
|
|
2208
2325
|
/** @readonly Leaderboard module — reassignment has no effect at runtime. */
|
|
2209
2326
|
leaderboard;
|
|
2327
|
+
/** @readonly Agency pair module — reassignment has no effect at runtime. */
|
|
2328
|
+
agencyPairs;
|
|
2210
2329
|
/** @readonly Locale module — reassignment has no effect at runtime. */
|
|
2211
2330
|
locale;
|
|
2212
2331
|
/** @readonly Realtime module — undefined when mqttEndpoint not configured. */
|
|
@@ -2287,9 +2406,11 @@ var TaphubClient = class {
|
|
|
2287
2406
|
this.game = new GameModule({ graphql: this.#graphql });
|
|
2288
2407
|
this.bid = new BidModule({ graphql: this.#graphql });
|
|
2289
2408
|
this.leaderboard = new LeaderboardModule({ graphql: this.#graphql });
|
|
2409
|
+
this.agencyPairs = new AgencyPairModule({ graphql: this.#graphql });
|
|
2290
2410
|
this.locale = new LocaleModule({ graphql: this.#graphql });
|
|
2291
2411
|
this.realtime = config.mqttEndpoint ? new RealtimeModule({
|
|
2292
2412
|
mqttEndpoint: config.mqttEndpoint,
|
|
2413
|
+
agencyId: this.agencyId,
|
|
2293
2414
|
onMqttLifecycle: createMqttProbe(this.network)
|
|
2294
2415
|
}) : void 0;
|
|
2295
2416
|
attachConnectionProbe(this.network);
|
|
@@ -2323,6 +2444,12 @@ var TaphubClient = class {
|
|
|
2323
2444
|
enumerable: true,
|
|
2324
2445
|
configurable: false
|
|
2325
2446
|
});
|
|
2447
|
+
Object.defineProperty(this, "agencyPairs", {
|
|
2448
|
+
value: this.agencyPairs,
|
|
2449
|
+
writable: false,
|
|
2450
|
+
enumerable: true,
|
|
2451
|
+
configurable: false
|
|
2452
|
+
});
|
|
2326
2453
|
if (this.realtime) {
|
|
2327
2454
|
Object.defineProperty(this, "realtime", {
|
|
2328
2455
|
value: this.realtime,
|
|
@@ -2458,6 +2585,7 @@ function calculateProbWin_v2(time1, time2, _price1, price2, creationTime, curren
|
|
|
2458
2585
|
return Math.max(0, Math.min(1, prob));
|
|
2459
2586
|
}
|
|
2460
2587
|
export {
|
|
2588
|
+
AgencyPairModule,
|
|
2461
2589
|
AuthModule,
|
|
2462
2590
|
BidModule,
|
|
2463
2591
|
GameModule,
|