@taphubhq/sdk-core 0.13.0 → 0.14.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 +52 -10
- package/dist/index.d.mts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +52 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -818,9 +818,17 @@ var LocaleModule = class {
|
|
|
818
818
|
|
|
819
819
|
// src/transport/mqtt/index.ts
|
|
820
820
|
var import_mqtt = __toESM(require("mqtt"));
|
|
821
|
-
|
|
821
|
+
|
|
822
|
+
// src/transport/mqtt/utils.ts
|
|
823
|
+
function subKey(gameId, userId) {
|
|
824
|
+
return `${gameId}::${userId ?? ""}`;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// src/transport/mqtt/index.ts
|
|
828
|
+
var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
|
|
822
829
|
var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
|
|
823
830
|
var TOPIC_PREFIX = "game";
|
|
831
|
+
var MARKET_TOPIC_PREFIX = "market";
|
|
824
832
|
function topicFor(gameId, suffix, userId) {
|
|
825
833
|
if (USER_SCOPED_SUFFIXES.includes(suffix)) {
|
|
826
834
|
return `${TOPIC_PREFIX}/${gameId}/user/${userId}/${suffix}`;
|
|
@@ -833,12 +841,10 @@ function normaliseUserId(userId) {
|
|
|
833
841
|
function userScopedTopicsFor(gameId, userId) {
|
|
834
842
|
return USER_SCOPED_SUFFIXES.map((s) => topicFor(gameId, s, userId));
|
|
835
843
|
}
|
|
836
|
-
function subKey(gameId, userId) {
|
|
837
|
-
return `${gameId}::${userId ?? ""}`;
|
|
838
|
-
}
|
|
839
844
|
function createMqttTransport(endpoint, opts = {}) {
|
|
840
845
|
let client = null;
|
|
841
846
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
847
|
+
const candleSubscriptions = /* @__PURE__ */ new Map();
|
|
842
848
|
const { onLifecycle } = opts;
|
|
843
849
|
let connectStartedAt = 0;
|
|
844
850
|
function fireLifecycle(event) {
|
|
@@ -885,6 +891,17 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
885
891
|
fireLifecycle({ kind: "error", err });
|
|
886
892
|
});
|
|
887
893
|
client.on("message", (receivedTopic, message) => {
|
|
894
|
+
const candleSub = [...candleSubscriptions.values()].find((s) => s.topic === receivedTopic);
|
|
895
|
+
if (candleSub) {
|
|
896
|
+
let payload2;
|
|
897
|
+
try {
|
|
898
|
+
payload2 = JSON.parse(message.toString());
|
|
899
|
+
} catch {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
candleSub.onCandle(receivedTopic, payload2);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
888
905
|
let matched;
|
|
889
906
|
for (const sub of subscriptions.values()) {
|
|
890
907
|
if (sub.topics.includes(receivedTopic)) {
|
|
@@ -929,9 +946,22 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
929
946
|
onError
|
|
930
947
|
});
|
|
931
948
|
for (const t of fullTopics) {
|
|
932
|
-
mqttClient.subscribe(t, { qos:
|
|
949
|
+
mqttClient.subscribe(t, { qos: 1 });
|
|
933
950
|
}
|
|
934
951
|
},
|
|
952
|
+
subscribeCandle(pair, onCandle) {
|
|
953
|
+
const topic = `${MARKET_TOPIC_PREFIX}/${pair.replace(/\//g, "_")}/candle`;
|
|
954
|
+
if (candleSubscriptions.has(pair)) return;
|
|
955
|
+
const mqttClient = ensureConnected();
|
|
956
|
+
candleSubscriptions.set(pair, { topic, onCandle });
|
|
957
|
+
mqttClient.subscribe(topic, { qos: 0 });
|
|
958
|
+
},
|
|
959
|
+
unsubscribeCandle(pair) {
|
|
960
|
+
const entry = candleSubscriptions.get(pair);
|
|
961
|
+
if (!entry) return;
|
|
962
|
+
if (client) client.unsubscribe(entry.topic);
|
|
963
|
+
candleSubscriptions.delete(pair);
|
|
964
|
+
},
|
|
935
965
|
unsubscribeAll(gameId, userId) {
|
|
936
966
|
const matches = entriesForGame(gameId);
|
|
937
967
|
if (matches.length === 0) return;
|
|
@@ -963,8 +993,12 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
963
993
|
client.unsubscribe(t);
|
|
964
994
|
}
|
|
965
995
|
}
|
|
996
|
+
for (const entry of candleSubscriptions.values()) {
|
|
997
|
+
client.unsubscribe(entry.topic);
|
|
998
|
+
}
|
|
966
999
|
}
|
|
967
1000
|
subscriptions.clear();
|
|
1001
|
+
candleSubscriptions.clear();
|
|
968
1002
|
if (client) {
|
|
969
1003
|
client.end(true);
|
|
970
1004
|
client = null;
|
|
@@ -1082,9 +1116,6 @@ function mapWireToEvent(topic, payload) {
|
|
|
1082
1116
|
function normaliseUserId2(userId) {
|
|
1083
1117
|
return userId && userId !== "" ? userId : null;
|
|
1084
1118
|
}
|
|
1085
|
-
function subKey2(gameId, userId) {
|
|
1086
|
-
return `${gameId}::${userId ?? ""}`;
|
|
1087
|
-
}
|
|
1088
1119
|
var RealtimeModule = class extends import_eventemitter32.default {
|
|
1089
1120
|
#transport;
|
|
1090
1121
|
#entries = /* @__PURE__ */ new Map();
|
|
@@ -1104,7 +1135,7 @@ var RealtimeModule = class extends import_eventemitter32.default {
|
|
|
1104
1135
|
}
|
|
1105
1136
|
subscribe(gameId, userId) {
|
|
1106
1137
|
const cleanUserId = normaliseUserId2(userId);
|
|
1107
|
-
const key =
|
|
1138
|
+
const key = subKey(gameId, cleanUserId);
|
|
1108
1139
|
const existing = this.#entries.get(key);
|
|
1109
1140
|
if (existing) {
|
|
1110
1141
|
existing.refcount += 1;
|
|
@@ -1160,11 +1191,22 @@ var RealtimeModule = class extends import_eventemitter32.default {
|
|
|
1160
1191
|
}
|
|
1161
1192
|
target.refcount -= 1;
|
|
1162
1193
|
if (target.refcount > 0) return;
|
|
1163
|
-
const key =
|
|
1194
|
+
const key = subKey(target.gameId, target.userId);
|
|
1164
1195
|
this.#entries.delete(key);
|
|
1165
1196
|
target.channel.removeAllListeners();
|
|
1166
1197
|
this.#transport.unsubscribeAll(gameId, target.userId);
|
|
1167
1198
|
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
1201
|
+
* Independent of game/agency — candle is market-wide public data.
|
|
1202
|
+
*/
|
|
1203
|
+
subscribeCandle(pair, onCandle) {
|
|
1204
|
+
this.#transport.subscribeCandle(pair, onCandle);
|
|
1205
|
+
}
|
|
1206
|
+
/** Unsubscribe from candle data for a pair. */
|
|
1207
|
+
unsubscribeCandle(pair) {
|
|
1208
|
+
this.#transport.unsubscribeCandle(pair);
|
|
1209
|
+
}
|
|
1168
1210
|
disconnect() {
|
|
1169
1211
|
for (const entry of this.#entries.values()) {
|
|
1170
1212
|
entry.channel.removeAllListeners();
|
package/dist/index.d.mts
CHANGED
|
@@ -452,14 +452,13 @@ type MqttLifecycleEvent = {
|
|
|
452
452
|
rttMs: number;
|
|
453
453
|
};
|
|
454
454
|
type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
|
|
455
|
+
type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
455
456
|
interface MqttTransport {
|
|
456
457
|
/**
|
|
457
|
-
* Subscribe to a game's MQTT topics
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
* tuple are idempotent; calls with the same `gameId` but a different
|
|
462
|
-
* `userId` create an independent entry with its own user-scoped topics.
|
|
458
|
+
* Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
|
|
459
|
+
* keyed by the tuple `(gameId, userId)`. Does NOT subscribe to candle —
|
|
460
|
+
* use `subscribeCandle` for public market candle data.
|
|
461
|
+
* Two calls with the same `(gameId, userId)` tuple are idempotent.
|
|
463
462
|
*/
|
|
464
463
|
subscribe(gameId: string, userId: string | null | undefined, onMessage: MqttMessageHandler, onError: MqttErrorHandler): void;
|
|
465
464
|
/**
|
|
@@ -470,6 +469,14 @@ interface MqttTransport {
|
|
|
470
469
|
* `code='AmbiguousUnsubscribe'`.
|
|
471
470
|
*/
|
|
472
471
|
unsubscribeAll(gameId: string, userId?: string | null): void;
|
|
472
|
+
/**
|
|
473
|
+
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
474
|
+
* Independent of game/agency context — candle is market-wide public data.
|
|
475
|
+
* Idempotent: multiple calls for the same pair are deduplicated.
|
|
476
|
+
*/
|
|
477
|
+
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
478
|
+
/** Tear down the candle subscription for a pair. */
|
|
479
|
+
unsubscribeCandle(pair: string): void;
|
|
473
480
|
close(): void;
|
|
474
481
|
}
|
|
475
482
|
|
|
@@ -580,6 +587,13 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
|
|
|
580
587
|
get _transport(): MqttTransport;
|
|
581
588
|
subscribe(gameId: string, userId?: string | null): GameChannel;
|
|
582
589
|
unsubscribe(gameId: string, userId?: string | null): void;
|
|
590
|
+
/**
|
|
591
|
+
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
592
|
+
* Independent of game/agency — candle is market-wide public data.
|
|
593
|
+
*/
|
|
594
|
+
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
595
|
+
/** Unsubscribe from candle data for a pair. */
|
|
596
|
+
unsubscribeCandle(pair: string): void;
|
|
583
597
|
disconnect(): void;
|
|
584
598
|
}
|
|
585
599
|
|
package/dist/index.d.ts
CHANGED
|
@@ -452,14 +452,13 @@ type MqttLifecycleEvent = {
|
|
|
452
452
|
rttMs: number;
|
|
453
453
|
};
|
|
454
454
|
type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
|
|
455
|
+
type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
455
456
|
interface MqttTransport {
|
|
456
457
|
/**
|
|
457
|
-
* Subscribe to a game's MQTT topics
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
* tuple are idempotent; calls with the same `gameId` but a different
|
|
462
|
-
* `userId` create an independent entry with its own user-scoped topics.
|
|
458
|
+
* Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
|
|
459
|
+
* keyed by the tuple `(gameId, userId)`. Does NOT subscribe to candle —
|
|
460
|
+
* use `subscribeCandle` for public market candle data.
|
|
461
|
+
* Two calls with the same `(gameId, userId)` tuple are idempotent.
|
|
463
462
|
*/
|
|
464
463
|
subscribe(gameId: string, userId: string | null | undefined, onMessage: MqttMessageHandler, onError: MqttErrorHandler): void;
|
|
465
464
|
/**
|
|
@@ -470,6 +469,14 @@ interface MqttTransport {
|
|
|
470
469
|
* `code='AmbiguousUnsubscribe'`.
|
|
471
470
|
*/
|
|
472
471
|
unsubscribeAll(gameId: string, userId?: string | null): void;
|
|
472
|
+
/**
|
|
473
|
+
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
474
|
+
* Independent of game/agency context — candle is market-wide public data.
|
|
475
|
+
* Idempotent: multiple calls for the same pair are deduplicated.
|
|
476
|
+
*/
|
|
477
|
+
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
478
|
+
/** Tear down the candle subscription for a pair. */
|
|
479
|
+
unsubscribeCandle(pair: string): void;
|
|
473
480
|
close(): void;
|
|
474
481
|
}
|
|
475
482
|
|
|
@@ -580,6 +587,13 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
|
|
|
580
587
|
get _transport(): MqttTransport;
|
|
581
588
|
subscribe(gameId: string, userId?: string | null): GameChannel;
|
|
582
589
|
unsubscribe(gameId: string, userId?: string | null): void;
|
|
590
|
+
/**
|
|
591
|
+
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
592
|
+
* Independent of game/agency — candle is market-wide public data.
|
|
593
|
+
*/
|
|
594
|
+
subscribeCandle(pair: string, onCandle: MqttCandleHandler): void;
|
|
595
|
+
/** Unsubscribe from candle data for a pair. */
|
|
596
|
+
unsubscribeCandle(pair: string): void;
|
|
583
597
|
disconnect(): void;
|
|
584
598
|
}
|
|
585
599
|
|
package/dist/index.js
CHANGED
|
@@ -754,9 +754,17 @@ var LocaleModule = class {
|
|
|
754
754
|
|
|
755
755
|
// src/transport/mqtt/index.ts
|
|
756
756
|
import mqtt from "mqtt";
|
|
757
|
-
|
|
757
|
+
|
|
758
|
+
// src/transport/mqtt/utils.ts
|
|
759
|
+
function subKey(gameId, userId) {
|
|
760
|
+
return `${gameId}::${userId ?? ""}`;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/transport/mqtt/index.ts
|
|
764
|
+
var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
|
|
758
765
|
var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
|
|
759
766
|
var TOPIC_PREFIX = "game";
|
|
767
|
+
var MARKET_TOPIC_PREFIX = "market";
|
|
760
768
|
function topicFor(gameId, suffix, userId) {
|
|
761
769
|
if (USER_SCOPED_SUFFIXES.includes(suffix)) {
|
|
762
770
|
return `${TOPIC_PREFIX}/${gameId}/user/${userId}/${suffix}`;
|
|
@@ -769,12 +777,10 @@ function normaliseUserId(userId) {
|
|
|
769
777
|
function userScopedTopicsFor(gameId, userId) {
|
|
770
778
|
return USER_SCOPED_SUFFIXES.map((s) => topicFor(gameId, s, userId));
|
|
771
779
|
}
|
|
772
|
-
function subKey(gameId, userId) {
|
|
773
|
-
return `${gameId}::${userId ?? ""}`;
|
|
774
|
-
}
|
|
775
780
|
function createMqttTransport(endpoint, opts = {}) {
|
|
776
781
|
let client = null;
|
|
777
782
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
783
|
+
const candleSubscriptions = /* @__PURE__ */ new Map();
|
|
778
784
|
const { onLifecycle } = opts;
|
|
779
785
|
let connectStartedAt = 0;
|
|
780
786
|
function fireLifecycle(event) {
|
|
@@ -821,6 +827,17 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
821
827
|
fireLifecycle({ kind: "error", err });
|
|
822
828
|
});
|
|
823
829
|
client.on("message", (receivedTopic, message) => {
|
|
830
|
+
const candleSub = [...candleSubscriptions.values()].find((s) => s.topic === receivedTopic);
|
|
831
|
+
if (candleSub) {
|
|
832
|
+
let payload2;
|
|
833
|
+
try {
|
|
834
|
+
payload2 = JSON.parse(message.toString());
|
|
835
|
+
} catch {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
candleSub.onCandle(receivedTopic, payload2);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
824
841
|
let matched;
|
|
825
842
|
for (const sub of subscriptions.values()) {
|
|
826
843
|
if (sub.topics.includes(receivedTopic)) {
|
|
@@ -865,9 +882,22 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
865
882
|
onError
|
|
866
883
|
});
|
|
867
884
|
for (const t of fullTopics) {
|
|
868
|
-
mqttClient.subscribe(t, { qos:
|
|
885
|
+
mqttClient.subscribe(t, { qos: 1 });
|
|
869
886
|
}
|
|
870
887
|
},
|
|
888
|
+
subscribeCandle(pair, onCandle) {
|
|
889
|
+
const topic = `${MARKET_TOPIC_PREFIX}/${pair.replace(/\//g, "_")}/candle`;
|
|
890
|
+
if (candleSubscriptions.has(pair)) return;
|
|
891
|
+
const mqttClient = ensureConnected();
|
|
892
|
+
candleSubscriptions.set(pair, { topic, onCandle });
|
|
893
|
+
mqttClient.subscribe(topic, { qos: 0 });
|
|
894
|
+
},
|
|
895
|
+
unsubscribeCandle(pair) {
|
|
896
|
+
const entry = candleSubscriptions.get(pair);
|
|
897
|
+
if (!entry) return;
|
|
898
|
+
if (client) client.unsubscribe(entry.topic);
|
|
899
|
+
candleSubscriptions.delete(pair);
|
|
900
|
+
},
|
|
871
901
|
unsubscribeAll(gameId, userId) {
|
|
872
902
|
const matches = entriesForGame(gameId);
|
|
873
903
|
if (matches.length === 0) return;
|
|
@@ -899,8 +929,12 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
899
929
|
client.unsubscribe(t);
|
|
900
930
|
}
|
|
901
931
|
}
|
|
932
|
+
for (const entry of candleSubscriptions.values()) {
|
|
933
|
+
client.unsubscribe(entry.topic);
|
|
934
|
+
}
|
|
902
935
|
}
|
|
903
936
|
subscriptions.clear();
|
|
937
|
+
candleSubscriptions.clear();
|
|
904
938
|
if (client) {
|
|
905
939
|
client.end(true);
|
|
906
940
|
client = null;
|
|
@@ -1018,9 +1052,6 @@ function mapWireToEvent(topic, payload) {
|
|
|
1018
1052
|
function normaliseUserId2(userId) {
|
|
1019
1053
|
return userId && userId !== "" ? userId : null;
|
|
1020
1054
|
}
|
|
1021
|
-
function subKey2(gameId, userId) {
|
|
1022
|
-
return `${gameId}::${userId ?? ""}`;
|
|
1023
|
-
}
|
|
1024
1055
|
var RealtimeModule = class extends EventEmitter2 {
|
|
1025
1056
|
#transport;
|
|
1026
1057
|
#entries = /* @__PURE__ */ new Map();
|
|
@@ -1040,7 +1071,7 @@ var RealtimeModule = class extends EventEmitter2 {
|
|
|
1040
1071
|
}
|
|
1041
1072
|
subscribe(gameId, userId) {
|
|
1042
1073
|
const cleanUserId = normaliseUserId2(userId);
|
|
1043
|
-
const key =
|
|
1074
|
+
const key = subKey(gameId, cleanUserId);
|
|
1044
1075
|
const existing = this.#entries.get(key);
|
|
1045
1076
|
if (existing) {
|
|
1046
1077
|
existing.refcount += 1;
|
|
@@ -1096,11 +1127,22 @@ var RealtimeModule = class extends EventEmitter2 {
|
|
|
1096
1127
|
}
|
|
1097
1128
|
target.refcount -= 1;
|
|
1098
1129
|
if (target.refcount > 0) return;
|
|
1099
|
-
const key =
|
|
1130
|
+
const key = subKey(target.gameId, target.userId);
|
|
1100
1131
|
this.#entries.delete(key);
|
|
1101
1132
|
target.channel.removeAllListeners();
|
|
1102
1133
|
this.#transport.unsubscribeAll(gameId, target.userId);
|
|
1103
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
1137
|
+
* Independent of game/agency — candle is market-wide public data.
|
|
1138
|
+
*/
|
|
1139
|
+
subscribeCandle(pair, onCandle) {
|
|
1140
|
+
this.#transport.subscribeCandle(pair, onCandle);
|
|
1141
|
+
}
|
|
1142
|
+
/** Unsubscribe from candle data for a pair. */
|
|
1143
|
+
unsubscribeCandle(pair) {
|
|
1144
|
+
this.#transport.unsubscribeCandle(pair);
|
|
1145
|
+
}
|
|
1104
1146
|
disconnect() {
|
|
1105
1147
|
for (const entry of this.#entries.values()) {
|
|
1106
1148
|
entry.channel.removeAllListeners();
|