@hyve-sdk/js 1.1.2 → 1.3.1-canary.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/README.md +137 -22
- package/dist/index.d.mts +250 -25
- package/dist/index.d.ts +250 -25
- package/dist/index.js +426 -51
- package/dist/index.mjs +422 -51
- package/package.json +13 -12
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,130 @@
|
|
|
1
1
|
// src/utils/index.ts
|
|
2
|
-
import { ethers } from "ethers";
|
|
3
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
|
|
4
|
+
// src/utils/logger.ts
|
|
5
|
+
var Logger = class _Logger {
|
|
6
|
+
config;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.config = this.initializeConfig();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Initialize logger configuration based on NODE_ENV
|
|
12
|
+
*/
|
|
13
|
+
initializeConfig() {
|
|
14
|
+
const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
15
|
+
let enabled = false;
|
|
16
|
+
let levels = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
17
|
+
if (isNode) {
|
|
18
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
19
|
+
enabled = nodeEnv !== "production";
|
|
20
|
+
const logLevelEnv = process.env.HYVE_SDK_LOG_LEVEL;
|
|
21
|
+
if (logLevelEnv) {
|
|
22
|
+
const configuredLevels = logLevelEnv.split(",").map((l) => l.trim());
|
|
23
|
+
levels = new Set(configuredLevels);
|
|
24
|
+
}
|
|
25
|
+
} else if (typeof window !== "undefined") {
|
|
26
|
+
try {
|
|
27
|
+
enabled = process.env.NODE_ENV !== "production";
|
|
28
|
+
} catch (e) {
|
|
29
|
+
enabled = true;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const localStorageLogLevel = localStorage.getItem("HYVE_SDK_LOG_LEVEL");
|
|
33
|
+
if (localStorageLogLevel) {
|
|
34
|
+
const configuredLevels = localStorageLogLevel.split(",").map((l) => l.trim());
|
|
35
|
+
levels = new Set(configuredLevels);
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
enabled,
|
|
42
|
+
prefix: "[Hyve SDK]",
|
|
43
|
+
levels
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Set which log levels to display
|
|
48
|
+
*/
|
|
49
|
+
setLevels(levels) {
|
|
50
|
+
this.config.levels = new Set(levels);
|
|
51
|
+
if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
|
|
52
|
+
try {
|
|
53
|
+
localStorage.setItem("HYVE_SDK_LOG_LEVEL", levels.join(","));
|
|
54
|
+
} catch (e) {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if logging is enabled
|
|
60
|
+
*/
|
|
61
|
+
isEnabled() {
|
|
62
|
+
return this.config.enabled;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Internal log method
|
|
66
|
+
*/
|
|
67
|
+
log(level, ...args) {
|
|
68
|
+
if (!this.config.enabled || !this.config.levels.has(level)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
72
|
+
const prefix = `${this.config.prefix} [${level.toUpperCase()}] [${timestamp}]`;
|
|
73
|
+
switch (level) {
|
|
74
|
+
case "debug":
|
|
75
|
+
console.debug(prefix, ...args);
|
|
76
|
+
break;
|
|
77
|
+
case "info":
|
|
78
|
+
console.info(prefix, ...args);
|
|
79
|
+
break;
|
|
80
|
+
case "warn":
|
|
81
|
+
console.warn(prefix, ...args);
|
|
82
|
+
break;
|
|
83
|
+
case "error":
|
|
84
|
+
console.error(prefix, ...args);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Log a debug message
|
|
90
|
+
*/
|
|
91
|
+
debug(...args) {
|
|
92
|
+
this.log("debug", ...args);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Log an info message
|
|
96
|
+
*/
|
|
97
|
+
info(...args) {
|
|
98
|
+
this.log("info", ...args);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Log a warning message
|
|
102
|
+
*/
|
|
103
|
+
warn(...args) {
|
|
104
|
+
this.log("warn", ...args);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Log an error message
|
|
108
|
+
*/
|
|
109
|
+
error(...args) {
|
|
110
|
+
this.log("error", ...args);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a child logger with a specific prefix
|
|
114
|
+
*/
|
|
115
|
+
child(prefix) {
|
|
116
|
+
const childLogger = new _Logger();
|
|
117
|
+
childLogger.config = {
|
|
118
|
+
...this.config,
|
|
119
|
+
prefix: `${this.config.prefix} [${prefix}]`
|
|
120
|
+
};
|
|
121
|
+
return childLogger;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var logger = new Logger();
|
|
125
|
+
|
|
126
|
+
// src/utils/auth.ts
|
|
127
|
+
import { ethers } from "ethers";
|
|
4
128
|
function parseUrlParams(searchParams) {
|
|
5
129
|
const params = typeof searchParams === "string" ? new URLSearchParams(searchParams) : searchParams;
|
|
6
130
|
return {
|
|
@@ -9,7 +133,8 @@ function parseUrlParams(searchParams) {
|
|
|
9
133
|
gameStartTab: params.get("game_start_tab") || "",
|
|
10
134
|
hyveToken: params.get("hyve-token") || "",
|
|
11
135
|
platform: params.get("platform") || "",
|
|
12
|
-
hyveAccess: params.get("hyve-access") || ""
|
|
136
|
+
hyveAccess: params.get("hyve-access") || "",
|
|
137
|
+
gameId: params.get("game-id") || ""
|
|
13
138
|
};
|
|
14
139
|
}
|
|
15
140
|
function validateSignature(signature, message) {
|
|
@@ -17,7 +142,7 @@ function validateSignature(signature, message) {
|
|
|
17
142
|
const recoveredAddress = ethers.verifyMessage(message, signature);
|
|
18
143
|
return !!recoveredAddress;
|
|
19
144
|
} catch (error) {
|
|
20
|
-
|
|
145
|
+
logger.error("Signature validation error:", error);
|
|
21
146
|
return false;
|
|
22
147
|
}
|
|
23
148
|
}
|
|
@@ -44,7 +169,7 @@ function handleVerifyMessage(signature, message) {
|
|
|
44
169
|
}
|
|
45
170
|
return userAddress;
|
|
46
171
|
} catch (error) {
|
|
47
|
-
|
|
172
|
+
logger.error("Error verifying message:", error);
|
|
48
173
|
return false;
|
|
49
174
|
}
|
|
50
175
|
}
|
|
@@ -52,7 +177,7 @@ function verifyHyveToken(hyveToken, maxAgeSec = 600) {
|
|
|
52
177
|
try {
|
|
53
178
|
const parts = hyveToken.split(".");
|
|
54
179
|
if (parts.length !== 4) {
|
|
55
|
-
|
|
180
|
+
logger.error(
|
|
56
181
|
"Invalid hyve-token format: expected 4 parts, got",
|
|
57
182
|
parts.length
|
|
58
183
|
);
|
|
@@ -60,30 +185,30 @@ function verifyHyveToken(hyveToken, maxAgeSec = 600) {
|
|
|
60
185
|
}
|
|
61
186
|
const [signature, address, randomBase64, timestampStr] = parts;
|
|
62
187
|
if (!signature || !address || !randomBase64 || !timestampStr) {
|
|
63
|
-
|
|
188
|
+
logger.error("Missing hyve-token components");
|
|
64
189
|
return false;
|
|
65
190
|
}
|
|
66
191
|
const message = `${address}.${randomBase64}.${timestampStr}`;
|
|
67
192
|
const recoveredAddress = ethers.verifyMessage(message, signature);
|
|
68
193
|
if (recoveredAddress.toLowerCase() !== address.toLowerCase()) {
|
|
69
|
-
|
|
194
|
+
logger.error("Hyve-token signature verification failed");
|
|
70
195
|
return false;
|
|
71
196
|
}
|
|
72
197
|
const timestamp = parseInt(timestampStr, 10);
|
|
73
198
|
if (!Number.isFinite(timestamp)) {
|
|
74
|
-
|
|
199
|
+
logger.error("Invalid hyve-token timestamp");
|
|
75
200
|
return false;
|
|
76
201
|
}
|
|
77
202
|
const now = Math.floor(Date.now() / 1e3);
|
|
78
203
|
if (now - timestamp > maxAgeSec) {
|
|
79
|
-
|
|
204
|
+
logger.error(
|
|
80
205
|
`Hyve-token expired (age: ${now - timestamp}s, max: ${maxAgeSec}s)`
|
|
81
206
|
);
|
|
82
207
|
return false;
|
|
83
208
|
}
|
|
84
209
|
return address;
|
|
85
210
|
} catch (error) {
|
|
86
|
-
|
|
211
|
+
logger.error("Hyve-token verification error:", error);
|
|
87
212
|
return false;
|
|
88
213
|
}
|
|
89
214
|
}
|
|
@@ -129,11 +254,8 @@ function verifyAuthentication(params, maxAgeSec = 600) {
|
|
|
129
254
|
error: "No authentication tokens provided"
|
|
130
255
|
};
|
|
131
256
|
}
|
|
132
|
-
function generateUUID() {
|
|
133
|
-
return uuidv4();
|
|
134
|
-
}
|
|
135
257
|
function isDomainAllowed(allowedDomains, hostname) {
|
|
136
|
-
|
|
258
|
+
logger.debug("Checking hostname:", hostname);
|
|
137
259
|
if (!allowedDomains) return true;
|
|
138
260
|
const targetHostname = hostname || "";
|
|
139
261
|
if (!targetHostname) return false;
|
|
@@ -151,6 +273,216 @@ function isDomainAllowed(allowedDomains, hostname) {
|
|
|
151
273
|
});
|
|
152
274
|
}
|
|
153
275
|
|
|
276
|
+
// src/utils/native-bridge.ts
|
|
277
|
+
var NativeMessageType = /* @__PURE__ */ ((NativeMessageType2) => {
|
|
278
|
+
NativeMessageType2["CHECK_IAP_AVAILABILITY"] = "CHECK_IAP_AVAILABILITY";
|
|
279
|
+
NativeMessageType2["REQUEST_NOTIFICATION_PERMISSION"] = "REQUEST_NOTIFICATION_PERMISSION";
|
|
280
|
+
NativeMessageType2["GET_PRODUCTS"] = "GET_PRODUCTS";
|
|
281
|
+
NativeMessageType2["PURCHASE"] = "PURCHASE";
|
|
282
|
+
NativeMessageType2["IAP_AVAILABILITY_RESULT"] = "IAP_AVAILABILITY_RESULT";
|
|
283
|
+
NativeMessageType2["PUSH_PERMISSION_GRANTED"] = "PUSH_PERMISSION_GRANTED";
|
|
284
|
+
NativeMessageType2["PUSH_PERMISSION_DENIED"] = "PUSH_PERMISSION_DENIED";
|
|
285
|
+
NativeMessageType2["PRODUCTS_RESULT"] = "PRODUCTS_RESULT";
|
|
286
|
+
NativeMessageType2["PURCHASE_COMPLETE"] = "PURCHASE_COMPLETE";
|
|
287
|
+
NativeMessageType2["PURCHASE_ERROR"] = "PURCHASE_ERROR";
|
|
288
|
+
return NativeMessageType2;
|
|
289
|
+
})(NativeMessageType || {});
|
|
290
|
+
var NativeBridge = class {
|
|
291
|
+
static handlers = /* @__PURE__ */ new Map();
|
|
292
|
+
static isInitialized = false;
|
|
293
|
+
/**
|
|
294
|
+
* Checks if the app is running inside a React Native WebView
|
|
295
|
+
*/
|
|
296
|
+
static isNativeContext() {
|
|
297
|
+
return typeof window !== "undefined" && "ReactNativeWebView" in window;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Initializes the native bridge message listener
|
|
301
|
+
* Call this once when your app starts
|
|
302
|
+
*/
|
|
303
|
+
static initialize() {
|
|
304
|
+
if (this.isInitialized) {
|
|
305
|
+
logger.debug("[NativeBridge] Already initialized");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (typeof window === "undefined") {
|
|
309
|
+
logger.warn("[NativeBridge] Window not available, skipping initialization");
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const boundHandler = this.handleNativeMessage.bind(this);
|
|
313
|
+
window.addEventListener("message", boundHandler);
|
|
314
|
+
document.addEventListener("message", boundHandler);
|
|
315
|
+
this.isInitialized = true;
|
|
316
|
+
logger.info("[NativeBridge] Initialized and listening for native messages");
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Handles incoming messages from React Native
|
|
320
|
+
*/
|
|
321
|
+
static handleNativeMessage(event) {
|
|
322
|
+
try {
|
|
323
|
+
let data;
|
|
324
|
+
if (typeof event.data === "string") {
|
|
325
|
+
try {
|
|
326
|
+
data = JSON.parse(event.data);
|
|
327
|
+
logger.debug("[NativeBridge] Parsed message string:", data);
|
|
328
|
+
} catch (parseError) {
|
|
329
|
+
logger.debug("[NativeBridge] Failed to parse message, not JSON:", event.data);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
} else if (typeof event.data === "object" && event.data !== null) {
|
|
333
|
+
data = event.data;
|
|
334
|
+
logger.debug("[NativeBridge] Received message object:", data);
|
|
335
|
+
} else {
|
|
336
|
+
logger.debug("[NativeBridge] Received invalid message type:", typeof event.data);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!data || !data.type) {
|
|
340
|
+
logger.debug("[NativeBridge] Received message without type, ignoring");
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const handler = this.handlers.get(data.type);
|
|
344
|
+
if (handler) {
|
|
345
|
+
logger.info(`[NativeBridge] Handling message: ${data.type}`, data.payload);
|
|
346
|
+
handler(data.payload);
|
|
347
|
+
} else {
|
|
348
|
+
logger.warn(`[NativeBridge] No handler registered for: ${data.type}`);
|
|
349
|
+
}
|
|
350
|
+
} catch (error) {
|
|
351
|
+
logger.error("[NativeBridge] Error handling native message:", error);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Sends a message to React Native
|
|
356
|
+
* @param type Message type
|
|
357
|
+
* @param payload Optional payload data
|
|
358
|
+
*/
|
|
359
|
+
static send(type, payload) {
|
|
360
|
+
if (!this.isNativeContext()) {
|
|
361
|
+
logger.debug(
|
|
362
|
+
`[NativeBridge] Not in native context, skipping message: ${type}`
|
|
363
|
+
);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
const message = {
|
|
368
|
+
type,
|
|
369
|
+
payload,
|
|
370
|
+
timestamp: Date.now()
|
|
371
|
+
};
|
|
372
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
373
|
+
logger.debug(`[NativeBridge] Sent message to native: ${type}`, payload);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
logger.error(`[NativeBridge] Error sending message to native:`, error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Registers a handler for messages from React Native
|
|
380
|
+
* @param type Message type to listen for
|
|
381
|
+
* @param handler Function to call when message is received
|
|
382
|
+
*/
|
|
383
|
+
static on(type, handler) {
|
|
384
|
+
this.handlers.set(type, handler);
|
|
385
|
+
logger.debug(`[NativeBridge] Registered handler for: ${type}`);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Unregisters a handler for a specific message type
|
|
389
|
+
* @param type Message type to stop listening for
|
|
390
|
+
*/
|
|
391
|
+
static off(type) {
|
|
392
|
+
this.handlers.delete(type);
|
|
393
|
+
logger.debug(`[NativeBridge] Unregistered handler for: ${type}`);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Clears all registered handlers
|
|
397
|
+
*/
|
|
398
|
+
static clearHandlers() {
|
|
399
|
+
this.handlers.clear();
|
|
400
|
+
logger.debug("[NativeBridge] Cleared all handlers");
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Checks if In-App Purchases are available on the device
|
|
404
|
+
* The native app will respond with a message containing availability status
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* // Listen for the response
|
|
408
|
+
* NativeBridge.on("IAP_AVAILABILITY_RESULT", (payload) => {
|
|
409
|
+
* console.log("IAP available:", payload.available);
|
|
410
|
+
* });
|
|
411
|
+
*
|
|
412
|
+
* // Send the request
|
|
413
|
+
* NativeBridge.checkIAPAvailability();
|
|
414
|
+
*/
|
|
415
|
+
static checkIAPAvailability() {
|
|
416
|
+
this.send("CHECK_IAP_AVAILABILITY" /* CHECK_IAP_AVAILABILITY */);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Requests notification permission from the native app
|
|
420
|
+
* The native app will respond with PUSH_PERMISSION_GRANTED or PUSH_PERMISSION_DENIED
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* // Listen for the response
|
|
424
|
+
* NativeBridge.on("PUSH_PERMISSION_GRANTED", () => {
|
|
425
|
+
* console.log("Permission granted");
|
|
426
|
+
* });
|
|
427
|
+
*
|
|
428
|
+
* NativeBridge.on("PUSH_PERMISSION_DENIED", () => {
|
|
429
|
+
* console.log("Permission denied");
|
|
430
|
+
* });
|
|
431
|
+
*
|
|
432
|
+
* // Send the request
|
|
433
|
+
* NativeBridge.requestNotificationPermission();
|
|
434
|
+
*/
|
|
435
|
+
static requestNotificationPermission() {
|
|
436
|
+
this.send("REQUEST_NOTIFICATION_PERMISSION" /* REQUEST_NOTIFICATION_PERMISSION */);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Requests available products for a specific game
|
|
440
|
+
* The native app will respond with PRODUCTS_RESULT containing product details
|
|
441
|
+
*
|
|
442
|
+
* @param gameId - The game ID to fetch products for
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* // Listen for the response
|
|
446
|
+
* NativeBridge.on("PRODUCTS_RESULT", (payload) => {
|
|
447
|
+
* console.log("Products:", payload.products);
|
|
448
|
+
* });
|
|
449
|
+
*
|
|
450
|
+
* // Send the request
|
|
451
|
+
* NativeBridge.getProducts(123);
|
|
452
|
+
*/
|
|
453
|
+
static getProducts(gameId) {
|
|
454
|
+
this.send("GET_PRODUCTS" /* GET_PRODUCTS */, { gameId });
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Initiates a purchase for a specific product
|
|
458
|
+
* The native app will respond with PURCHASE_COMPLETE or PURCHASE_ERROR
|
|
459
|
+
*
|
|
460
|
+
* @param productId - The product ID to purchase
|
|
461
|
+
* @param userId - The user ID making the purchase
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* // Listen for responses
|
|
465
|
+
* NativeBridge.on("PURCHASE_COMPLETE", (payload) => {
|
|
466
|
+
* console.log("Purchase successful:", payload.productId);
|
|
467
|
+
* });
|
|
468
|
+
*
|
|
469
|
+
* NativeBridge.on("PURCHASE_ERROR", (payload) => {
|
|
470
|
+
* console.error("Purchase failed:", payload.error);
|
|
471
|
+
* });
|
|
472
|
+
*
|
|
473
|
+
* // Initiate purchase
|
|
474
|
+
* NativeBridge.purchase("product_123", "user_456");
|
|
475
|
+
*/
|
|
476
|
+
static purchase(productId, userId) {
|
|
477
|
+
this.send("PURCHASE" /* PURCHASE */, { productId, userId });
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/utils/index.ts
|
|
482
|
+
function generateUUID() {
|
|
483
|
+
return uuidv4();
|
|
484
|
+
}
|
|
485
|
+
|
|
154
486
|
// src/core/client.ts
|
|
155
487
|
var HyveClient = class {
|
|
156
488
|
telemetryConfig;
|
|
@@ -158,6 +490,7 @@ var HyveClient = class {
|
|
|
158
490
|
sessionId;
|
|
159
491
|
userId = null;
|
|
160
492
|
jwtToken = null;
|
|
493
|
+
gameId = null;
|
|
161
494
|
/**
|
|
162
495
|
* Creates a new HyveClient instance
|
|
163
496
|
* @param config Optional telemetry configuration
|
|
@@ -174,9 +507,9 @@ var HyveClient = class {
|
|
|
174
507
|
this.apiBaseUrl = this.telemetryConfig.isDev ? "https://product-api.dev.hyve.gg" : "https://product-api.prod.hyve.gg";
|
|
175
508
|
}
|
|
176
509
|
this.sessionId = generateUUID();
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
510
|
+
logger.info("Client initialized with sessionId:", this.sessionId);
|
|
511
|
+
logger.info("API Base URL:", this.apiBaseUrl);
|
|
512
|
+
logger.info("Environment:", this.telemetryConfig.isDev ? "dev" : "prod");
|
|
180
513
|
}
|
|
181
514
|
/**
|
|
182
515
|
* Authenticates a user from URL parameters
|
|
@@ -188,7 +521,23 @@ var HyveClient = class {
|
|
|
188
521
|
const params = urlParams ? parseUrlParams(urlParams) : parseUrlParams(window.location.search);
|
|
189
522
|
if (params.hyveAccess) {
|
|
190
523
|
this.jwtToken = params.hyveAccess;
|
|
191
|
-
|
|
524
|
+
logger.info("JWT token extracted from hyve-access parameter");
|
|
525
|
+
}
|
|
526
|
+
if (params.gameId) {
|
|
527
|
+
this.gameId = params.gameId;
|
|
528
|
+
logger.info("Game ID extracted from game-id parameter:", this.gameId);
|
|
529
|
+
}
|
|
530
|
+
if (this.jwtToken) {
|
|
531
|
+
logger.info("Authentication successful via JWT");
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
if (params.gameId) {
|
|
535
|
+
this.gameId = params.gameId;
|
|
536
|
+
logger.info("Game ID extracted from game-id parameter:", this.gameId);
|
|
537
|
+
}
|
|
538
|
+
if (this.jwtToken) {
|
|
539
|
+
logger.info("Authentication successful via JWT");
|
|
540
|
+
return true;
|
|
192
541
|
}
|
|
193
542
|
const authResult = verifyAuthentication({
|
|
194
543
|
hyveToken: params.hyveToken,
|
|
@@ -197,76 +546,83 @@ var HyveClient = class {
|
|
|
197
546
|
});
|
|
198
547
|
if (authResult.isValid && authResult.address) {
|
|
199
548
|
this.userId = authResult.address;
|
|
200
|
-
|
|
201
|
-
|
|
549
|
+
logger.info("Authentication successful:", authResult.address);
|
|
550
|
+
logger.info("Authentication method:", authResult.method);
|
|
202
551
|
return true;
|
|
203
552
|
} else {
|
|
204
|
-
|
|
553
|
+
logger.error("Authentication failed:", authResult.error);
|
|
205
554
|
this.userId = null;
|
|
206
555
|
return false;
|
|
207
556
|
}
|
|
208
557
|
} catch (error) {
|
|
209
|
-
|
|
558
|
+
logger.error("Authentication error:", error);
|
|
210
559
|
this.userId = null;
|
|
211
560
|
return false;
|
|
212
561
|
}
|
|
213
562
|
}
|
|
214
563
|
/**
|
|
215
|
-
* Sends a telemetry event
|
|
564
|
+
* Sends a user-level telemetry event using JWT authentication
|
|
565
|
+
* Requires JWT token, authenticated user, and game ID from URL parameters
|
|
216
566
|
* @param eventLocation Location where the event occurred
|
|
217
567
|
* @param eventCategory Main category of the event
|
|
218
568
|
* @param eventAction Primary action taken
|
|
219
569
|
* @param eventSubCategory Optional sub-category
|
|
220
570
|
* @param eventSubAction Optional sub-action
|
|
221
|
-
* @param eventDetails Optional event details
|
|
222
|
-
* @param
|
|
571
|
+
* @param eventDetails Optional event details (object or JSON string)
|
|
572
|
+
* @param customData Optional custom data (object or JSON string)
|
|
573
|
+
* @param platformId Optional platform identifier
|
|
223
574
|
* @returns Promise resolving to boolean indicating success
|
|
224
575
|
*/
|
|
225
|
-
async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails,
|
|
226
|
-
if (!this.
|
|
227
|
-
|
|
576
|
+
async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails, customData, platformId) {
|
|
577
|
+
if (!this.jwtToken) {
|
|
578
|
+
logger.error("JWT token required. Call authenticateFromUrl first.");
|
|
228
579
|
return false;
|
|
229
580
|
}
|
|
230
|
-
if (!this.
|
|
231
|
-
|
|
581
|
+
if (!this.gameId) {
|
|
582
|
+
logger.error("Game ID required. Ensure game-id URL parameter is set.");
|
|
583
|
+
return false;
|
|
232
584
|
}
|
|
233
585
|
try {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
finalEventDetails = eventDetails;
|
|
239
|
-
}
|
|
586
|
+
const toJsonString = (data) => {
|
|
587
|
+
if (!data) return null;
|
|
588
|
+
return typeof data === "string" ? data : JSON.stringify(data);
|
|
589
|
+
};
|
|
240
590
|
const telemetryEvent = {
|
|
591
|
+
game_id: this.gameId,
|
|
241
592
|
session_id: this.sessionId,
|
|
242
|
-
|
|
593
|
+
platform_id: platformId || null,
|
|
243
594
|
event_location: eventLocation,
|
|
244
595
|
event_category: eventCategory,
|
|
245
596
|
event_action: eventAction,
|
|
246
597
|
event_sub_category: eventSubCategory || null,
|
|
247
598
|
event_sub_action: eventSubAction || null,
|
|
248
|
-
event_details:
|
|
599
|
+
event_details: toJsonString(eventDetails),
|
|
600
|
+
custom_data: toJsonString(customData)
|
|
249
601
|
};
|
|
250
|
-
|
|
251
|
-
const telemetryUrl = `${this.apiBaseUrl}/api/v1/
|
|
602
|
+
logger.debug("Sending telemetry event:", telemetryEvent);
|
|
603
|
+
const telemetryUrl = `${this.apiBaseUrl}/api/v1/telemetry/send`;
|
|
252
604
|
const response = await fetch(telemetryUrl, {
|
|
253
605
|
method: "POST",
|
|
254
606
|
headers: {
|
|
255
607
|
"Content-Type": "application/json",
|
|
256
|
-
|
|
608
|
+
Authorization: `Bearer ${this.jwtToken}`
|
|
257
609
|
},
|
|
258
610
|
body: JSON.stringify(telemetryEvent)
|
|
259
611
|
});
|
|
260
612
|
if (response.ok) {
|
|
261
|
-
|
|
613
|
+
logger.info("Telemetry event sent successfully:", response.status);
|
|
262
614
|
return true;
|
|
263
615
|
} else {
|
|
264
616
|
const errorText = await response.text();
|
|
265
|
-
|
|
617
|
+
logger.error(
|
|
618
|
+
"Failed to send telemetry event:",
|
|
619
|
+
response.status,
|
|
620
|
+
errorText
|
|
621
|
+
);
|
|
266
622
|
return false;
|
|
267
623
|
}
|
|
268
624
|
} catch (error) {
|
|
269
|
-
|
|
625
|
+
logger.error("Error sending telemetry event:", error);
|
|
270
626
|
return false;
|
|
271
627
|
}
|
|
272
628
|
}
|
|
@@ -278,15 +634,18 @@ var HyveClient = class {
|
|
|
278
634
|
*/
|
|
279
635
|
async callApi(endpoint, options = {}) {
|
|
280
636
|
if (!this.jwtToken) {
|
|
281
|
-
throw new Error(
|
|
637
|
+
throw new Error(
|
|
638
|
+
"No JWT token available. Call authenticateFromUrl first."
|
|
639
|
+
);
|
|
282
640
|
}
|
|
283
641
|
try {
|
|
284
642
|
const url = `${this.apiBaseUrl}${endpoint.startsWith("/") ? endpoint : `/${endpoint}`}`;
|
|
643
|
+
logger.debug("Making API call to:", url);
|
|
285
644
|
const response = await fetch(url, {
|
|
286
645
|
...options,
|
|
287
646
|
headers: {
|
|
288
647
|
"Content-Type": "application/json",
|
|
289
|
-
|
|
648
|
+
Authorization: `Bearer ${this.jwtToken}`,
|
|
290
649
|
...options.headers
|
|
291
650
|
}
|
|
292
651
|
});
|
|
@@ -296,7 +655,7 @@ var HyveClient = class {
|
|
|
296
655
|
}
|
|
297
656
|
return await response.json();
|
|
298
657
|
} catch (error) {
|
|
299
|
-
|
|
658
|
+
logger.error("API call failed:", error);
|
|
300
659
|
throw error;
|
|
301
660
|
}
|
|
302
661
|
}
|
|
@@ -329,8 +688,8 @@ var HyveClient = class {
|
|
|
329
688
|
} else if (config.isDev !== void 0) {
|
|
330
689
|
this.apiBaseUrl = config.isDev ? "https://product-api.dev.hyve.gg" : "https://product-api.prod.hyve.gg";
|
|
331
690
|
}
|
|
332
|
-
|
|
333
|
-
|
|
691
|
+
logger.info("Config updated");
|
|
692
|
+
logger.info("API Base URL:", this.apiBaseUrl);
|
|
334
693
|
}
|
|
335
694
|
/**
|
|
336
695
|
* Gets the current user ID
|
|
@@ -353,6 +712,13 @@ var HyveClient = class {
|
|
|
353
712
|
getJwtToken() {
|
|
354
713
|
return this.jwtToken;
|
|
355
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Gets the current game ID
|
|
717
|
+
* @returns Current game ID or null if not available
|
|
718
|
+
*/
|
|
719
|
+
getGameId() {
|
|
720
|
+
return this.gameId;
|
|
721
|
+
}
|
|
356
722
|
/**
|
|
357
723
|
* Gets the API base URL
|
|
358
724
|
* @returns API base URL
|
|
@@ -380,7 +746,8 @@ var HyveClient = class {
|
|
|
380
746
|
logout() {
|
|
381
747
|
this.userId = null;
|
|
382
748
|
this.jwtToken = null;
|
|
383
|
-
|
|
749
|
+
this.gameId = null;
|
|
750
|
+
logger.info("User logged out");
|
|
384
751
|
}
|
|
385
752
|
/**
|
|
386
753
|
* Resets the client state
|
|
@@ -388,14 +755,18 @@ var HyveClient = class {
|
|
|
388
755
|
reset() {
|
|
389
756
|
this.logout();
|
|
390
757
|
this.sessionId = generateUUID();
|
|
391
|
-
|
|
758
|
+
logger.info("Client reset with new sessionId:", this.sessionId);
|
|
392
759
|
}
|
|
393
760
|
};
|
|
394
761
|
export {
|
|
395
762
|
HyveClient,
|
|
763
|
+
Logger,
|
|
764
|
+
NativeBridge,
|
|
765
|
+
NativeMessageType,
|
|
396
766
|
generateUUID,
|
|
397
767
|
handleVerifyMessage,
|
|
398
768
|
isDomainAllowed,
|
|
769
|
+
logger,
|
|
399
770
|
parseUrlParams,
|
|
400
771
|
validateSignature,
|
|
401
772
|
verifyAuthentication,
|