@hyve-sdk/js 1.2.2 → 1.3.1-canary.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/README.md +191 -0
- package/dist/index.d.mts +354 -8
- package/dist/index.d.ts +354 -8
- package/dist/index.js +617 -38
- package/dist/index.mjs +612 -38
- package/package.json +13 -12
package/dist/index.js
CHANGED
|
@@ -20,10 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AdsService: () => AdsService,
|
|
23
24
|
HyveClient: () => HyveClient,
|
|
25
|
+
Logger: () => Logger,
|
|
26
|
+
NativeBridge: () => NativeBridge,
|
|
27
|
+
NativeMessageType: () => NativeMessageType,
|
|
24
28
|
generateUUID: () => generateUUID,
|
|
25
29
|
handleVerifyMessage: () => handleVerifyMessage,
|
|
26
30
|
isDomainAllowed: () => isDomainAllowed,
|
|
31
|
+
logger: () => logger,
|
|
27
32
|
parseUrlParams: () => parseUrlParams,
|
|
28
33
|
validateSignature: () => validateSignature,
|
|
29
34
|
verifyAuthentication: () => verifyAuthentication,
|
|
@@ -32,8 +37,132 @@ __export(index_exports, {
|
|
|
32
37
|
module.exports = __toCommonJS(index_exports);
|
|
33
38
|
|
|
34
39
|
// src/utils/index.ts
|
|
35
|
-
var import_ethers = require("ethers");
|
|
36
40
|
var import_uuid = require("uuid");
|
|
41
|
+
|
|
42
|
+
// src/utils/logger.ts
|
|
43
|
+
var Logger = class _Logger {
|
|
44
|
+
config;
|
|
45
|
+
constructor() {
|
|
46
|
+
this.config = this.initializeConfig();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Initialize logger configuration based on NODE_ENV
|
|
50
|
+
*/
|
|
51
|
+
initializeConfig() {
|
|
52
|
+
const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
53
|
+
let enabled = false;
|
|
54
|
+
let levels = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
55
|
+
if (isNode) {
|
|
56
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
57
|
+
enabled = nodeEnv !== "production";
|
|
58
|
+
const logLevelEnv = process.env.HYVE_SDK_LOG_LEVEL;
|
|
59
|
+
if (logLevelEnv) {
|
|
60
|
+
const configuredLevels = logLevelEnv.split(",").map((l) => l.trim());
|
|
61
|
+
levels = new Set(configuredLevels);
|
|
62
|
+
}
|
|
63
|
+
} else if (typeof window !== "undefined") {
|
|
64
|
+
try {
|
|
65
|
+
enabled = process.env.NODE_ENV !== "production";
|
|
66
|
+
} catch (e) {
|
|
67
|
+
enabled = true;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const localStorageLogLevel = localStorage.getItem("HYVE_SDK_LOG_LEVEL");
|
|
71
|
+
if (localStorageLogLevel) {
|
|
72
|
+
const configuredLevels = localStorageLogLevel.split(",").map((l) => l.trim());
|
|
73
|
+
levels = new Set(configuredLevels);
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
enabled,
|
|
80
|
+
prefix: "[Hyve SDK]",
|
|
81
|
+
levels
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Set which log levels to display
|
|
86
|
+
*/
|
|
87
|
+
setLevels(levels) {
|
|
88
|
+
this.config.levels = new Set(levels);
|
|
89
|
+
if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
|
|
90
|
+
try {
|
|
91
|
+
localStorage.setItem("HYVE_SDK_LOG_LEVEL", levels.join(","));
|
|
92
|
+
} catch (e) {
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if logging is enabled
|
|
98
|
+
*/
|
|
99
|
+
isEnabled() {
|
|
100
|
+
return this.config.enabled;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Internal log method
|
|
104
|
+
*/
|
|
105
|
+
log(level, ...args) {
|
|
106
|
+
if (!this.config.enabled || !this.config.levels.has(level)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
110
|
+
const prefix = `${this.config.prefix} [${level.toUpperCase()}] [${timestamp}]`;
|
|
111
|
+
switch (level) {
|
|
112
|
+
case "debug":
|
|
113
|
+
console.debug(prefix, ...args);
|
|
114
|
+
break;
|
|
115
|
+
case "info":
|
|
116
|
+
console.info(prefix, ...args);
|
|
117
|
+
break;
|
|
118
|
+
case "warn":
|
|
119
|
+
console.warn(prefix, ...args);
|
|
120
|
+
break;
|
|
121
|
+
case "error":
|
|
122
|
+
console.error(prefix, ...args);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Log a debug message
|
|
128
|
+
*/
|
|
129
|
+
debug(...args) {
|
|
130
|
+
this.log("debug", ...args);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Log an info message
|
|
134
|
+
*/
|
|
135
|
+
info(...args) {
|
|
136
|
+
this.log("info", ...args);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Log a warning message
|
|
140
|
+
*/
|
|
141
|
+
warn(...args) {
|
|
142
|
+
this.log("warn", ...args);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Log an error message
|
|
146
|
+
*/
|
|
147
|
+
error(...args) {
|
|
148
|
+
this.log("error", ...args);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create a child logger with a specific prefix
|
|
152
|
+
*/
|
|
153
|
+
child(prefix) {
|
|
154
|
+
const childLogger = new _Logger();
|
|
155
|
+
childLogger.config = {
|
|
156
|
+
...this.config,
|
|
157
|
+
prefix: `${this.config.prefix} [${prefix}]`
|
|
158
|
+
};
|
|
159
|
+
return childLogger;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var logger = new Logger();
|
|
163
|
+
|
|
164
|
+
// src/utils/auth.ts
|
|
165
|
+
var import_ethers = require("ethers");
|
|
37
166
|
function parseUrlParams(searchParams) {
|
|
38
167
|
const params = typeof searchParams === "string" ? new URLSearchParams(searchParams) : searchParams;
|
|
39
168
|
return {
|
|
@@ -51,7 +180,7 @@ function validateSignature(signature, message) {
|
|
|
51
180
|
const recoveredAddress = import_ethers.ethers.verifyMessage(message, signature);
|
|
52
181
|
return !!recoveredAddress;
|
|
53
182
|
} catch (error) {
|
|
54
|
-
|
|
183
|
+
logger.error("Signature validation error:", error);
|
|
55
184
|
return false;
|
|
56
185
|
}
|
|
57
186
|
}
|
|
@@ -78,7 +207,7 @@ function handleVerifyMessage(signature, message) {
|
|
|
78
207
|
}
|
|
79
208
|
return userAddress;
|
|
80
209
|
} catch (error) {
|
|
81
|
-
|
|
210
|
+
logger.error("Error verifying message:", error);
|
|
82
211
|
return false;
|
|
83
212
|
}
|
|
84
213
|
}
|
|
@@ -86,7 +215,7 @@ function verifyHyveToken(hyveToken, maxAgeSec = 600) {
|
|
|
86
215
|
try {
|
|
87
216
|
const parts = hyveToken.split(".");
|
|
88
217
|
if (parts.length !== 4) {
|
|
89
|
-
|
|
218
|
+
logger.error(
|
|
90
219
|
"Invalid hyve-token format: expected 4 parts, got",
|
|
91
220
|
parts.length
|
|
92
221
|
);
|
|
@@ -94,30 +223,30 @@ function verifyHyveToken(hyveToken, maxAgeSec = 600) {
|
|
|
94
223
|
}
|
|
95
224
|
const [signature, address, randomBase64, timestampStr] = parts;
|
|
96
225
|
if (!signature || !address || !randomBase64 || !timestampStr) {
|
|
97
|
-
|
|
226
|
+
logger.error("Missing hyve-token components");
|
|
98
227
|
return false;
|
|
99
228
|
}
|
|
100
229
|
const message = `${address}.${randomBase64}.${timestampStr}`;
|
|
101
230
|
const recoveredAddress = import_ethers.ethers.verifyMessage(message, signature);
|
|
102
231
|
if (recoveredAddress.toLowerCase() !== address.toLowerCase()) {
|
|
103
|
-
|
|
232
|
+
logger.error("Hyve-token signature verification failed");
|
|
104
233
|
return false;
|
|
105
234
|
}
|
|
106
235
|
const timestamp = parseInt(timestampStr, 10);
|
|
107
236
|
if (!Number.isFinite(timestamp)) {
|
|
108
|
-
|
|
237
|
+
logger.error("Invalid hyve-token timestamp");
|
|
109
238
|
return false;
|
|
110
239
|
}
|
|
111
240
|
const now = Math.floor(Date.now() / 1e3);
|
|
112
241
|
if (now - timestamp > maxAgeSec) {
|
|
113
|
-
|
|
242
|
+
logger.error(
|
|
114
243
|
`Hyve-token expired (age: ${now - timestamp}s, max: ${maxAgeSec}s)`
|
|
115
244
|
);
|
|
116
245
|
return false;
|
|
117
246
|
}
|
|
118
247
|
return address;
|
|
119
248
|
} catch (error) {
|
|
120
|
-
|
|
249
|
+
logger.error("Hyve-token verification error:", error);
|
|
121
250
|
return false;
|
|
122
251
|
}
|
|
123
252
|
}
|
|
@@ -163,11 +292,8 @@ function verifyAuthentication(params, maxAgeSec = 600) {
|
|
|
163
292
|
error: "No authentication tokens provided"
|
|
164
293
|
};
|
|
165
294
|
}
|
|
166
|
-
function generateUUID() {
|
|
167
|
-
return (0, import_uuid.v4)();
|
|
168
|
-
}
|
|
169
295
|
function isDomainAllowed(allowedDomains, hostname) {
|
|
170
|
-
|
|
296
|
+
logger.debug("Checking hostname:", hostname);
|
|
171
297
|
if (!allowedDomains) return true;
|
|
172
298
|
const targetHostname = hostname || "";
|
|
173
299
|
if (!targetHostname) return false;
|
|
@@ -185,6 +311,403 @@ function isDomainAllowed(allowedDomains, hostname) {
|
|
|
185
311
|
});
|
|
186
312
|
}
|
|
187
313
|
|
|
314
|
+
// src/utils/native-bridge.ts
|
|
315
|
+
var NativeMessageType = /* @__PURE__ */ ((NativeMessageType2) => {
|
|
316
|
+
NativeMessageType2["CHECK_IAP_AVAILABILITY"] = "CHECK_IAP_AVAILABILITY";
|
|
317
|
+
NativeMessageType2["REQUEST_NOTIFICATION_PERMISSION"] = "REQUEST_NOTIFICATION_PERMISSION";
|
|
318
|
+
NativeMessageType2["GET_PRODUCTS"] = "GET_PRODUCTS";
|
|
319
|
+
NativeMessageType2["PURCHASE"] = "PURCHASE";
|
|
320
|
+
NativeMessageType2["IAP_AVAILABILITY_RESULT"] = "IAP_AVAILABILITY_RESULT";
|
|
321
|
+
NativeMessageType2["PUSH_PERMISSION_GRANTED"] = "PUSH_PERMISSION_GRANTED";
|
|
322
|
+
NativeMessageType2["PUSH_PERMISSION_DENIED"] = "PUSH_PERMISSION_DENIED";
|
|
323
|
+
NativeMessageType2["PRODUCTS_RESULT"] = "PRODUCTS_RESULT";
|
|
324
|
+
NativeMessageType2["PURCHASE_COMPLETE"] = "PURCHASE_COMPLETE";
|
|
325
|
+
NativeMessageType2["PURCHASE_ERROR"] = "PURCHASE_ERROR";
|
|
326
|
+
return NativeMessageType2;
|
|
327
|
+
})(NativeMessageType || {});
|
|
328
|
+
var NativeBridge = class {
|
|
329
|
+
static handlers = /* @__PURE__ */ new Map();
|
|
330
|
+
static isInitialized = false;
|
|
331
|
+
/**
|
|
332
|
+
* Checks if the app is running inside a React Native WebView
|
|
333
|
+
*/
|
|
334
|
+
static isNativeContext() {
|
|
335
|
+
return typeof window !== "undefined" && "ReactNativeWebView" in window;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Initializes the native bridge message listener
|
|
339
|
+
* Call this once when your app starts
|
|
340
|
+
*/
|
|
341
|
+
static initialize() {
|
|
342
|
+
if (this.isInitialized) {
|
|
343
|
+
logger.debug("[NativeBridge] Already initialized");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (typeof window === "undefined") {
|
|
347
|
+
logger.warn("[NativeBridge] Window not available, skipping initialization");
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const boundHandler = this.handleNativeMessage.bind(this);
|
|
351
|
+
window.addEventListener("message", boundHandler);
|
|
352
|
+
document.addEventListener("message", boundHandler);
|
|
353
|
+
this.isInitialized = true;
|
|
354
|
+
logger.info("[NativeBridge] Initialized and listening for native messages");
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Handles incoming messages from React Native
|
|
358
|
+
*/
|
|
359
|
+
static handleNativeMessage(event) {
|
|
360
|
+
try {
|
|
361
|
+
let data;
|
|
362
|
+
if (typeof event.data === "string") {
|
|
363
|
+
try {
|
|
364
|
+
data = JSON.parse(event.data);
|
|
365
|
+
logger.debug("[NativeBridge] Parsed message string:", data);
|
|
366
|
+
} catch (parseError) {
|
|
367
|
+
logger.debug("[NativeBridge] Failed to parse message, not JSON:", event.data);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
} else if (typeof event.data === "object" && event.data !== null) {
|
|
371
|
+
data = event.data;
|
|
372
|
+
logger.debug("[NativeBridge] Received message object:", data);
|
|
373
|
+
} else {
|
|
374
|
+
logger.debug("[NativeBridge] Received invalid message type:", typeof event.data);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (!data || !data.type) {
|
|
378
|
+
logger.debug("[NativeBridge] Received message without type, ignoring");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const handler = this.handlers.get(data.type);
|
|
382
|
+
if (handler) {
|
|
383
|
+
logger.info(`[NativeBridge] Handling message: ${data.type}`, data.payload);
|
|
384
|
+
handler(data.payload);
|
|
385
|
+
} else {
|
|
386
|
+
logger.warn(`[NativeBridge] No handler registered for: ${data.type}`);
|
|
387
|
+
}
|
|
388
|
+
} catch (error) {
|
|
389
|
+
logger.error("[NativeBridge] Error handling native message:", error);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Sends a message to React Native
|
|
394
|
+
* @param type Message type
|
|
395
|
+
* @param payload Optional payload data
|
|
396
|
+
*/
|
|
397
|
+
static send(type, payload) {
|
|
398
|
+
if (!this.isNativeContext()) {
|
|
399
|
+
logger.debug(
|
|
400
|
+
`[NativeBridge] Not in native context, skipping message: ${type}`
|
|
401
|
+
);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
const message = {
|
|
406
|
+
type,
|
|
407
|
+
payload,
|
|
408
|
+
timestamp: Date.now()
|
|
409
|
+
};
|
|
410
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
411
|
+
logger.debug(`[NativeBridge] Sent message to native: ${type}`, payload);
|
|
412
|
+
} catch (error) {
|
|
413
|
+
logger.error(`[NativeBridge] Error sending message to native:`, error);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Registers a handler for messages from React Native
|
|
418
|
+
* @param type Message type to listen for
|
|
419
|
+
* @param handler Function to call when message is received
|
|
420
|
+
*/
|
|
421
|
+
static on(type, handler) {
|
|
422
|
+
this.handlers.set(type, handler);
|
|
423
|
+
logger.debug(`[NativeBridge] Registered handler for: ${type}`);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Unregisters a handler for a specific message type
|
|
427
|
+
* @param type Message type to stop listening for
|
|
428
|
+
*/
|
|
429
|
+
static off(type) {
|
|
430
|
+
this.handlers.delete(type);
|
|
431
|
+
logger.debug(`[NativeBridge] Unregistered handler for: ${type}`);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Clears all registered handlers
|
|
435
|
+
*/
|
|
436
|
+
static clearHandlers() {
|
|
437
|
+
this.handlers.clear();
|
|
438
|
+
logger.debug("[NativeBridge] Cleared all handlers");
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Checks if In-App Purchases are available on the device
|
|
442
|
+
* The native app will respond with a message containing availability status
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* // Listen for the response
|
|
446
|
+
* NativeBridge.on("IAP_AVAILABILITY_RESULT", (payload) => {
|
|
447
|
+
* console.log("IAP available:", payload.available);
|
|
448
|
+
* });
|
|
449
|
+
*
|
|
450
|
+
* // Send the request
|
|
451
|
+
* NativeBridge.checkIAPAvailability();
|
|
452
|
+
*/
|
|
453
|
+
static checkIAPAvailability() {
|
|
454
|
+
this.send("CHECK_IAP_AVAILABILITY" /* CHECK_IAP_AVAILABILITY */);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Requests notification permission from the native app
|
|
458
|
+
* The native app will respond with PUSH_PERMISSION_GRANTED or PUSH_PERMISSION_DENIED
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* // Listen for the response
|
|
462
|
+
* NativeBridge.on("PUSH_PERMISSION_GRANTED", () => {
|
|
463
|
+
* console.log("Permission granted");
|
|
464
|
+
* });
|
|
465
|
+
*
|
|
466
|
+
* NativeBridge.on("PUSH_PERMISSION_DENIED", () => {
|
|
467
|
+
* console.log("Permission denied");
|
|
468
|
+
* });
|
|
469
|
+
*
|
|
470
|
+
* // Send the request
|
|
471
|
+
* NativeBridge.requestNotificationPermission();
|
|
472
|
+
*/
|
|
473
|
+
static requestNotificationPermission() {
|
|
474
|
+
this.send("REQUEST_NOTIFICATION_PERMISSION" /* REQUEST_NOTIFICATION_PERMISSION */);
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Requests available products for a specific game
|
|
478
|
+
* The native app will respond with PRODUCTS_RESULT containing product details
|
|
479
|
+
*
|
|
480
|
+
* @param gameId - The game ID to fetch products for
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* // Listen for the response
|
|
484
|
+
* NativeBridge.on("PRODUCTS_RESULT", (payload) => {
|
|
485
|
+
* console.log("Products:", payload.products);
|
|
486
|
+
* });
|
|
487
|
+
*
|
|
488
|
+
* // Send the request
|
|
489
|
+
* NativeBridge.getProducts(123);
|
|
490
|
+
*/
|
|
491
|
+
static getProducts(gameId) {
|
|
492
|
+
this.send("GET_PRODUCTS" /* GET_PRODUCTS */, { gameId });
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Initiates a purchase for a specific product
|
|
496
|
+
* The native app will respond with PURCHASE_COMPLETE or PURCHASE_ERROR
|
|
497
|
+
*
|
|
498
|
+
* @param productId - The product ID to purchase
|
|
499
|
+
* @param userId - The user ID making the purchase
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* // Listen for responses
|
|
503
|
+
* NativeBridge.on("PURCHASE_COMPLETE", (payload) => {
|
|
504
|
+
* console.log("Purchase successful:", payload.productId);
|
|
505
|
+
* });
|
|
506
|
+
*
|
|
507
|
+
* NativeBridge.on("PURCHASE_ERROR", (payload) => {
|
|
508
|
+
* console.error("Purchase failed:", payload.error);
|
|
509
|
+
* });
|
|
510
|
+
*
|
|
511
|
+
* // Initiate purchase
|
|
512
|
+
* NativeBridge.purchase("product_123", "user_456");
|
|
513
|
+
*/
|
|
514
|
+
static purchase(productId, userId) {
|
|
515
|
+
this.send("PURCHASE" /* PURCHASE */, { productId, userId });
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// src/utils/index.ts
|
|
520
|
+
function generateUUID() {
|
|
521
|
+
return (0, import_uuid.v4)();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/services/ads.ts
|
|
525
|
+
var AdsService = class {
|
|
526
|
+
config = {
|
|
527
|
+
enabled: false,
|
|
528
|
+
sound: "on",
|
|
529
|
+
debug: false,
|
|
530
|
+
onBeforeAd: () => {
|
|
531
|
+
},
|
|
532
|
+
onAfterAd: () => {
|
|
533
|
+
},
|
|
534
|
+
onRewardEarned: () => {
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
initialized = false;
|
|
538
|
+
ready = false;
|
|
539
|
+
/**
|
|
540
|
+
* Configure the ads service
|
|
541
|
+
* Must set enabled: true to activate ads
|
|
542
|
+
*/
|
|
543
|
+
configure(config) {
|
|
544
|
+
this.config = {
|
|
545
|
+
...this.config,
|
|
546
|
+
...config,
|
|
547
|
+
onBeforeAd: config.onBeforeAd || this.config.onBeforeAd,
|
|
548
|
+
onAfterAd: config.onAfterAd || this.config.onAfterAd,
|
|
549
|
+
onRewardEarned: config.onRewardEarned || this.config.onRewardEarned
|
|
550
|
+
};
|
|
551
|
+
if (this.config.debug) {
|
|
552
|
+
console.log("[AdsService] Configuration updated:", {
|
|
553
|
+
enabled: this.config.enabled,
|
|
554
|
+
sound: this.config.sound
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (this.config.enabled && !this.initialized) {
|
|
558
|
+
this.initialize();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Initialize the ads system
|
|
563
|
+
*/
|
|
564
|
+
initialize() {
|
|
565
|
+
if (this.initialized) return;
|
|
566
|
+
if (!this.config.enabled) {
|
|
567
|
+
if (this.config.debug) {
|
|
568
|
+
console.log("[AdsService] Ads disabled, skipping initialization");
|
|
569
|
+
}
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (!window.adConfig || !window.adBreak) {
|
|
573
|
+
console.warn("[AdsService] Google Ads SDK not found. Ads will not be available.");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (this.config.debug) {
|
|
577
|
+
console.log("[AdsService] Initializing ads system...");
|
|
578
|
+
}
|
|
579
|
+
const googleConfig = {
|
|
580
|
+
sound: this.config.sound,
|
|
581
|
+
preloadAdBreaks: "on",
|
|
582
|
+
onReady: () => {
|
|
583
|
+
this.ready = true;
|
|
584
|
+
if (this.config.debug) {
|
|
585
|
+
console.log("[AdsService] Ads ready");
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
window.adConfig(googleConfig);
|
|
590
|
+
this.initialized = true;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Show an ad
|
|
594
|
+
* Returns immediately if ads are disabled
|
|
595
|
+
*/
|
|
596
|
+
async show(type) {
|
|
597
|
+
const requestedAt = Date.now();
|
|
598
|
+
if (!this.config.enabled) {
|
|
599
|
+
if (this.config.debug) {
|
|
600
|
+
console.log("[AdsService] Ads disabled, skipping ad request");
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
success: false,
|
|
604
|
+
type,
|
|
605
|
+
error: new Error("Ads are disabled"),
|
|
606
|
+
requestedAt,
|
|
607
|
+
completedAt: Date.now()
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (!this.initialized) {
|
|
611
|
+
this.initialize();
|
|
612
|
+
}
|
|
613
|
+
if (!this.ready || !window.adBreak) {
|
|
614
|
+
return {
|
|
615
|
+
success: false,
|
|
616
|
+
type,
|
|
617
|
+
error: new Error("Ads not ready"),
|
|
618
|
+
requestedAt,
|
|
619
|
+
completedAt: Date.now()
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
return this.showAdBreak(type);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Show an ad break
|
|
626
|
+
*/
|
|
627
|
+
async showAdBreak(type) {
|
|
628
|
+
const requestedAt = Date.now();
|
|
629
|
+
return new Promise((resolve) => {
|
|
630
|
+
const googleType = type === "rewarded" ? "reward" : type === "preroll" ? "start" : "next";
|
|
631
|
+
const adName = `${type}-ad-${Date.now()}`;
|
|
632
|
+
if (this.config.debug) {
|
|
633
|
+
console.log(`[AdsService] Showing ${type} ad`);
|
|
634
|
+
}
|
|
635
|
+
this.config.onBeforeAd(type);
|
|
636
|
+
const adBreakConfig = {
|
|
637
|
+
type: googleType,
|
|
638
|
+
name: adName,
|
|
639
|
+
beforeAd: () => {
|
|
640
|
+
if (this.config.debug) {
|
|
641
|
+
console.log("[AdsService] Ad started");
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
afterAd: () => {
|
|
645
|
+
if (this.config.debug) {
|
|
646
|
+
console.log("[AdsService] Ad finished");
|
|
647
|
+
}
|
|
648
|
+
this.config.onAfterAd(type);
|
|
649
|
+
},
|
|
650
|
+
adBreakDone: (info) => {
|
|
651
|
+
const completedAt = Date.now();
|
|
652
|
+
let success = false;
|
|
653
|
+
if (type === "rewarded") {
|
|
654
|
+
success = info?.breakStatus === "viewed";
|
|
655
|
+
} else {
|
|
656
|
+
success = info?.breakStatus !== "error";
|
|
657
|
+
}
|
|
658
|
+
const error = info?.breakStatus === "error" && info?.error ? new Error(info.error) : void 0;
|
|
659
|
+
if (this.config.debug) {
|
|
660
|
+
console.log("[AdsService] Ad break done:", {
|
|
661
|
+
success,
|
|
662
|
+
status: info?.breakStatus
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
const result = {
|
|
666
|
+
success,
|
|
667
|
+
type,
|
|
668
|
+
error,
|
|
669
|
+
requestedAt,
|
|
670
|
+
completedAt
|
|
671
|
+
};
|
|
672
|
+
resolve(result);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
if (type === "rewarded") {
|
|
676
|
+
adBreakConfig.beforeReward = (showAdFn) => {
|
|
677
|
+
if (this.config.debug) {
|
|
678
|
+
console.log("[AdsService] beforeReward callback");
|
|
679
|
+
}
|
|
680
|
+
showAdFn();
|
|
681
|
+
};
|
|
682
|
+
adBreakConfig.adViewed = () => {
|
|
683
|
+
if (this.config.debug) {
|
|
684
|
+
console.log("[AdsService] Rewarded ad watched successfully");
|
|
685
|
+
}
|
|
686
|
+
this.config.onRewardEarned();
|
|
687
|
+
};
|
|
688
|
+
adBreakConfig.adDismissed = () => {
|
|
689
|
+
if (this.config.debug) {
|
|
690
|
+
console.log("[AdsService] Rewarded ad dismissed");
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
window.adBreak(adBreakConfig);
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Check if ads are enabled
|
|
699
|
+
*/
|
|
700
|
+
isEnabled() {
|
|
701
|
+
return this.config.enabled;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Check if ads are ready to show
|
|
705
|
+
*/
|
|
706
|
+
isReady() {
|
|
707
|
+
return this.config.enabled && this.ready;
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
188
711
|
// src/core/client.ts
|
|
189
712
|
var HyveClient = class {
|
|
190
713
|
telemetryConfig;
|
|
@@ -193,9 +716,10 @@ var HyveClient = class {
|
|
|
193
716
|
userId = null;
|
|
194
717
|
jwtToken = null;
|
|
195
718
|
gameId = null;
|
|
719
|
+
adsService;
|
|
196
720
|
/**
|
|
197
721
|
* Creates a new HyveClient instance
|
|
198
|
-
* @param config Optional telemetry
|
|
722
|
+
* @param config Optional configuration including telemetry and ads
|
|
199
723
|
*/
|
|
200
724
|
constructor(config) {
|
|
201
725
|
this.telemetryConfig = {
|
|
@@ -209,9 +733,14 @@ var HyveClient = class {
|
|
|
209
733
|
this.apiBaseUrl = this.telemetryConfig.isDev ? "https://product-api.dev.hyve.gg" : "https://product-api.prod.hyve.gg";
|
|
210
734
|
}
|
|
211
735
|
this.sessionId = generateUUID();
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
736
|
+
this.adsService = new AdsService();
|
|
737
|
+
if (config?.ads) {
|
|
738
|
+
this.adsService.configure(config.ads);
|
|
739
|
+
}
|
|
740
|
+
logger.info("Client initialized with sessionId:", this.sessionId);
|
|
741
|
+
logger.info("API Base URL:", this.apiBaseUrl);
|
|
742
|
+
logger.info("Environment:", this.telemetryConfig.isDev ? "dev" : "prod");
|
|
743
|
+
logger.info("Ads enabled:", this.adsService.isEnabled());
|
|
215
744
|
}
|
|
216
745
|
/**
|
|
217
746
|
* Authenticates a user from URL parameters
|
|
@@ -223,14 +752,22 @@ var HyveClient = class {
|
|
|
223
752
|
const params = urlParams ? parseUrlParams(urlParams) : parseUrlParams(window.location.search);
|
|
224
753
|
if (params.hyveAccess) {
|
|
225
754
|
this.jwtToken = params.hyveAccess;
|
|
226
|
-
|
|
755
|
+
logger.info("JWT token extracted from hyve-access parameter");
|
|
227
756
|
}
|
|
228
757
|
if (params.gameId) {
|
|
229
758
|
this.gameId = params.gameId;
|
|
230
|
-
|
|
759
|
+
logger.info("Game ID extracted from game-id parameter:", this.gameId);
|
|
231
760
|
}
|
|
232
761
|
if (this.jwtToken) {
|
|
233
|
-
|
|
762
|
+
logger.info("Authentication successful via JWT");
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
if (params.gameId) {
|
|
766
|
+
this.gameId = params.gameId;
|
|
767
|
+
logger.info("Game ID extracted from game-id parameter:", this.gameId);
|
|
768
|
+
}
|
|
769
|
+
if (this.jwtToken) {
|
|
770
|
+
logger.info("Authentication successful via JWT");
|
|
234
771
|
return true;
|
|
235
772
|
}
|
|
236
773
|
const authResult = verifyAuthentication({
|
|
@@ -240,16 +777,16 @@ var HyveClient = class {
|
|
|
240
777
|
});
|
|
241
778
|
if (authResult.isValid && authResult.address) {
|
|
242
779
|
this.userId = authResult.address;
|
|
243
|
-
|
|
244
|
-
|
|
780
|
+
logger.info("Authentication successful:", authResult.address);
|
|
781
|
+
logger.info("Authentication method:", authResult.method);
|
|
245
782
|
return true;
|
|
246
783
|
} else {
|
|
247
|
-
|
|
784
|
+
logger.error("Authentication failed:", authResult.error);
|
|
248
785
|
this.userId = null;
|
|
249
786
|
return false;
|
|
250
787
|
}
|
|
251
788
|
} catch (error) {
|
|
252
|
-
|
|
789
|
+
logger.error("Authentication error:", error);
|
|
253
790
|
this.userId = null;
|
|
254
791
|
return false;
|
|
255
792
|
}
|
|
@@ -269,11 +806,11 @@ var HyveClient = class {
|
|
|
269
806
|
*/
|
|
270
807
|
async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails, customData, platformId) {
|
|
271
808
|
if (!this.jwtToken) {
|
|
272
|
-
|
|
809
|
+
logger.error("JWT token required. Call authenticateFromUrl first.");
|
|
273
810
|
return false;
|
|
274
811
|
}
|
|
275
812
|
if (!this.gameId) {
|
|
276
|
-
|
|
813
|
+
logger.error("Game ID required. Ensure game-id URL parameter is set.");
|
|
277
814
|
return false;
|
|
278
815
|
}
|
|
279
816
|
try {
|
|
@@ -293,26 +830,30 @@ var HyveClient = class {
|
|
|
293
830
|
event_details: toJsonString(eventDetails),
|
|
294
831
|
custom_data: toJsonString(customData)
|
|
295
832
|
};
|
|
296
|
-
|
|
833
|
+
logger.debug("Sending telemetry event:", telemetryEvent);
|
|
297
834
|
const telemetryUrl = `${this.apiBaseUrl}/api/v1/telemetry/send`;
|
|
298
835
|
const response = await fetch(telemetryUrl, {
|
|
299
836
|
method: "POST",
|
|
300
837
|
headers: {
|
|
301
838
|
"Content-Type": "application/json",
|
|
302
|
-
|
|
839
|
+
Authorization: `Bearer ${this.jwtToken}`
|
|
303
840
|
},
|
|
304
841
|
body: JSON.stringify(telemetryEvent)
|
|
305
842
|
});
|
|
306
843
|
if (response.ok) {
|
|
307
|
-
|
|
844
|
+
logger.info("Telemetry event sent successfully:", response.status);
|
|
308
845
|
return true;
|
|
309
846
|
} else {
|
|
310
847
|
const errorText = await response.text();
|
|
311
|
-
|
|
848
|
+
logger.error(
|
|
849
|
+
"Failed to send telemetry event:",
|
|
850
|
+
response.status,
|
|
851
|
+
errorText
|
|
852
|
+
);
|
|
312
853
|
return false;
|
|
313
854
|
}
|
|
314
855
|
} catch (error) {
|
|
315
|
-
|
|
856
|
+
logger.error("Error sending telemetry event:", error);
|
|
316
857
|
return false;
|
|
317
858
|
}
|
|
318
859
|
}
|
|
@@ -324,15 +865,18 @@ var HyveClient = class {
|
|
|
324
865
|
*/
|
|
325
866
|
async callApi(endpoint, options = {}) {
|
|
326
867
|
if (!this.jwtToken) {
|
|
327
|
-
throw new Error(
|
|
868
|
+
throw new Error(
|
|
869
|
+
"No JWT token available. Call authenticateFromUrl first."
|
|
870
|
+
);
|
|
328
871
|
}
|
|
329
872
|
try {
|
|
330
873
|
const url = `${this.apiBaseUrl}${endpoint.startsWith("/") ? endpoint : `/${endpoint}`}`;
|
|
874
|
+
logger.debug("Making API call to:", url);
|
|
331
875
|
const response = await fetch(url, {
|
|
332
876
|
...options,
|
|
333
877
|
headers: {
|
|
334
878
|
"Content-Type": "application/json",
|
|
335
|
-
|
|
879
|
+
Authorization: `Bearer ${this.jwtToken}`,
|
|
336
880
|
...options.headers
|
|
337
881
|
}
|
|
338
882
|
});
|
|
@@ -342,7 +886,7 @@ var HyveClient = class {
|
|
|
342
886
|
}
|
|
343
887
|
return await response.json();
|
|
344
888
|
} catch (error) {
|
|
345
|
-
|
|
889
|
+
logger.error("API call failed:", error);
|
|
346
890
|
throw error;
|
|
347
891
|
}
|
|
348
892
|
}
|
|
@@ -375,8 +919,8 @@ var HyveClient = class {
|
|
|
375
919
|
} else if (config.isDev !== void 0) {
|
|
376
920
|
this.apiBaseUrl = config.isDev ? "https://product-api.dev.hyve.gg" : "https://product-api.prod.hyve.gg";
|
|
377
921
|
}
|
|
378
|
-
|
|
379
|
-
|
|
922
|
+
logger.info("Config updated");
|
|
923
|
+
logger.info("API Base URL:", this.apiBaseUrl);
|
|
380
924
|
}
|
|
381
925
|
/**
|
|
382
926
|
* Gets the current user ID
|
|
@@ -434,7 +978,7 @@ var HyveClient = class {
|
|
|
434
978
|
this.userId = null;
|
|
435
979
|
this.jwtToken = null;
|
|
436
980
|
this.gameId = null;
|
|
437
|
-
|
|
981
|
+
logger.info("User logged out");
|
|
438
982
|
}
|
|
439
983
|
/**
|
|
440
984
|
* Resets the client state
|
|
@@ -442,15 +986,50 @@ var HyveClient = class {
|
|
|
442
986
|
reset() {
|
|
443
987
|
this.logout();
|
|
444
988
|
this.sessionId = generateUUID();
|
|
445
|
-
|
|
989
|
+
logger.info("Client reset with new sessionId:", this.sessionId);
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Configure ads service
|
|
993
|
+
* @param config Ads configuration
|
|
994
|
+
*/
|
|
995
|
+
configureAds(config) {
|
|
996
|
+
this.adsService.configure(config);
|
|
997
|
+
logger.info("Ads configuration updated");
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Show an ad
|
|
1001
|
+
* @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
|
|
1002
|
+
* @returns Promise resolving to ad result
|
|
1003
|
+
*/
|
|
1004
|
+
async showAd(type) {
|
|
1005
|
+
return this.adsService.show(type);
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Check if ads are enabled
|
|
1009
|
+
* @returns Boolean indicating if ads are enabled
|
|
1010
|
+
*/
|
|
1011
|
+
areAdsEnabled() {
|
|
1012
|
+
return this.adsService.isEnabled();
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Check if ads are ready to show
|
|
1016
|
+
* @returns Boolean indicating if ads are ready
|
|
1017
|
+
*/
|
|
1018
|
+
areAdsReady() {
|
|
1019
|
+
return this.adsService.isReady();
|
|
446
1020
|
}
|
|
447
1021
|
};
|
|
448
1022
|
// Annotate the CommonJS export names for ESM import in node:
|
|
449
1023
|
0 && (module.exports = {
|
|
1024
|
+
AdsService,
|
|
450
1025
|
HyveClient,
|
|
1026
|
+
Logger,
|
|
1027
|
+
NativeBridge,
|
|
1028
|
+
NativeMessageType,
|
|
451
1029
|
generateUUID,
|
|
452
1030
|
handleVerifyMessage,
|
|
453
1031
|
isDomainAllowed,
|
|
1032
|
+
logger,
|
|
454
1033
|
parseUrlParams,
|
|
455
1034
|
validateSignature,
|
|
456
1035
|
verifyAuthentication,
|