@hyve-sdk/js 2.13.0 → 2.14.0-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/dist/index.d.mts +138 -2
- package/dist/index.d.ts +138 -2
- package/dist/index.js +409 -45
- package/dist/index.mjs +408 -45
- package/dist/react.d.mts +49 -1
- package/dist/react.d.ts +49 -1
- package/dist/react.js +407 -45
- package/dist/react.mjs +407 -45
- package/package.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -1177,6 +1177,7 @@ var PlaygamaService = class {
|
|
|
1177
1177
|
var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v2.js";
|
|
1178
1178
|
var CrazyGamesService = class {
|
|
1179
1179
|
initialized = false;
|
|
1180
|
+
environment = null;
|
|
1180
1181
|
/**
|
|
1181
1182
|
* Detects if the game is running on the CrazyGames platform.
|
|
1182
1183
|
* Games on CrazyGames run inside an iframe, so we check document.referrer
|
|
@@ -1218,6 +1219,7 @@ var CrazyGamesService = class {
|
|
|
1218
1219
|
logger.warn("[CrazyGamesService] Unexpected environment:", env);
|
|
1219
1220
|
return false;
|
|
1220
1221
|
}
|
|
1222
|
+
this.environment = env;
|
|
1221
1223
|
this.initialized = true;
|
|
1222
1224
|
return true;
|
|
1223
1225
|
} catch (error) {
|
|
@@ -1328,6 +1330,88 @@ var CrazyGamesService = class {
|
|
|
1328
1330
|
if (!this.initialized) return;
|
|
1329
1331
|
window.CrazyGames?.SDK.game.happytime();
|
|
1330
1332
|
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Returns the resolved CrazyGames environment ('crazygames' | 'local' |
|
|
1335
|
+
* 'disabled'), or null if the SDK has not initialized yet. Used to
|
|
1336
|
+
* auto-select the CrazyGames storage adapter.
|
|
1337
|
+
*/
|
|
1338
|
+
getEnvironment() {
|
|
1339
|
+
return this.environment;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Whether the CrazyGames account system is present (sync). When false,
|
|
1343
|
+
* there is no logged-in user concept and telemetry falls back to guests.
|
|
1344
|
+
*/
|
|
1345
|
+
isUserAccountAvailable() {
|
|
1346
|
+
if (!this.initialized) return false;
|
|
1347
|
+
return window.CrazyGames?.SDK.user.isUserAccountAvailable ?? false;
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Fetches the current CrazyGames user, or null if the player is not logged
|
|
1351
|
+
* in (guest). The returned `__dangerousUserId` is client-asserted and used
|
|
1352
|
+
* only as a telemetry attribution label.
|
|
1353
|
+
*/
|
|
1354
|
+
async getUser() {
|
|
1355
|
+
const sdk = window.CrazyGames?.SDK;
|
|
1356
|
+
if (!this.initialized || !sdk || !this.isUserAccountAvailable()) {
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
try {
|
|
1360
|
+
return await sdk.user.getUser() ?? null;
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
logger.warn("[CrazyGamesService] getUser failed:", error);
|
|
1363
|
+
return null;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Registers a listener that fires when a guest logs in mid-session.
|
|
1368
|
+
* No-op if the SDK or account system is unavailable.
|
|
1369
|
+
*/
|
|
1370
|
+
addAuthListener(callback) {
|
|
1371
|
+
const sdk = window.CrazyGames?.SDK;
|
|
1372
|
+
if (!this.initialized || !sdk || !this.isUserAccountAvailable()) return;
|
|
1373
|
+
try {
|
|
1374
|
+
sdk.user.addAuthListener(callback);
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
logger.warn("[CrazyGamesService] addAuthListener failed:", error);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
/** Removes a previously registered auth listener. */
|
|
1380
|
+
removeAuthListener(callback) {
|
|
1381
|
+
const sdk = window.CrazyGames?.SDK;
|
|
1382
|
+
if (!this.initialized || !sdk) return;
|
|
1383
|
+
try {
|
|
1384
|
+
sdk.user.removeAuthListener(callback);
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
logger.warn("[CrazyGamesService] removeAuthListener failed:", error);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Reads a value from the CrazyGames data store. Returns null when the SDK
|
|
1391
|
+
* is unavailable or the key is absent.
|
|
1392
|
+
*/
|
|
1393
|
+
async dataGetItem(key) {
|
|
1394
|
+
const sdk = window.CrazyGames?.SDK;
|
|
1395
|
+
if (!this.initialized || !sdk) return null;
|
|
1396
|
+
const value = await sdk.data.getItem(key);
|
|
1397
|
+
return value ?? null;
|
|
1398
|
+
}
|
|
1399
|
+
/** Writes a value to the CrazyGames data store. */
|
|
1400
|
+
async dataSetItem(key, value) {
|
|
1401
|
+
const sdk = window.CrazyGames?.SDK;
|
|
1402
|
+
if (!this.initialized || !sdk) {
|
|
1403
|
+
throw new Error("CrazyGames SDK not initialized");
|
|
1404
|
+
}
|
|
1405
|
+
await sdk.data.setItem(key, value);
|
|
1406
|
+
}
|
|
1407
|
+
/** Removes a value from the CrazyGames data store. */
|
|
1408
|
+
async dataRemoveItem(key) {
|
|
1409
|
+
const sdk = window.CrazyGames?.SDK;
|
|
1410
|
+
if (!this.initialized || !sdk) {
|
|
1411
|
+
throw new Error("CrazyGames SDK not initialized");
|
|
1412
|
+
}
|
|
1413
|
+
await sdk.data.removeItem(key);
|
|
1414
|
+
}
|
|
1331
1415
|
loadScript() {
|
|
1332
1416
|
return new Promise((resolve, reject) => {
|
|
1333
1417
|
if (window.CrazyGames?.SDK) {
|
|
@@ -1989,6 +2073,162 @@ var LocalStorageAdapter = class {
|
|
|
1989
2073
|
return count;
|
|
1990
2074
|
}
|
|
1991
2075
|
};
|
|
2076
|
+
function isGameDataObject(value) {
|
|
2077
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2078
|
+
}
|
|
2079
|
+
function toNumber(value) {
|
|
2080
|
+
return typeof value === "number" ? value : 0;
|
|
2081
|
+
}
|
|
2082
|
+
function applyOperation(operation, current, operand) {
|
|
2083
|
+
switch (operation) {
|
|
2084
|
+
case "set":
|
|
2085
|
+
return operand;
|
|
2086
|
+
case "add":
|
|
2087
|
+
return toNumber(current) + toNumber(operand);
|
|
2088
|
+
case "subtract":
|
|
2089
|
+
return toNumber(current) - toNumber(operand);
|
|
2090
|
+
case "multiply":
|
|
2091
|
+
return toNumber(current) * toNumber(operand);
|
|
2092
|
+
case "divide": {
|
|
2093
|
+
const divisor = toNumber(operand);
|
|
2094
|
+
if (divisor === 0) throw new Error("Cannot divide by zero");
|
|
2095
|
+
return toNumber(current) / divisor;
|
|
2096
|
+
}
|
|
2097
|
+
case "modulo": {
|
|
2098
|
+
const divisor = toNumber(operand);
|
|
2099
|
+
if (divisor === 0) throw new Error("Cannot modulo by zero");
|
|
2100
|
+
return toNumber(current) % divisor;
|
|
2101
|
+
}
|
|
2102
|
+
// For min/max, a missing current has nothing to compare against — adopt the operand.
|
|
2103
|
+
case "min":
|
|
2104
|
+
return typeof current === "number" ? Math.min(current, toNumber(operand)) : operand;
|
|
2105
|
+
case "max":
|
|
2106
|
+
return typeof current === "number" ? Math.max(current, toNumber(operand)) : operand;
|
|
2107
|
+
case "append": {
|
|
2108
|
+
const base = Array.isArray(current) ? [...current] : current === void 0 || current === null ? [] : [current];
|
|
2109
|
+
if (Array.isArray(operand)) {
|
|
2110
|
+
base.push(...operand);
|
|
2111
|
+
} else {
|
|
2112
|
+
base.push(operand);
|
|
2113
|
+
}
|
|
2114
|
+
return base;
|
|
2115
|
+
}
|
|
2116
|
+
default:
|
|
2117
|
+
return operand;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
function getAtPath(value, path) {
|
|
2121
|
+
let current = value;
|
|
2122
|
+
for (const segment of path.split(".")) {
|
|
2123
|
+
if (!isGameDataObject(current)) return void 0;
|
|
2124
|
+
current = current[segment];
|
|
2125
|
+
}
|
|
2126
|
+
return current;
|
|
2127
|
+
}
|
|
2128
|
+
function setAtPath(value, path, newValue) {
|
|
2129
|
+
const segments = path.split(".");
|
|
2130
|
+
const root = isGameDataObject(value) ? { ...value } : {};
|
|
2131
|
+
let cursor = root;
|
|
2132
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
2133
|
+
const segment = segments[i];
|
|
2134
|
+
const existing = cursor[segment];
|
|
2135
|
+
const next = isGameDataObject(existing) ? { ...existing } : {};
|
|
2136
|
+
cursor[segment] = next;
|
|
2137
|
+
cursor = next;
|
|
2138
|
+
}
|
|
2139
|
+
cursor[segments[segments.length - 1]] = newValue;
|
|
2140
|
+
return root;
|
|
2141
|
+
}
|
|
2142
|
+
var CrazyGamesStorageAdapter = class {
|
|
2143
|
+
constructor(service) {
|
|
2144
|
+
this.service = service;
|
|
2145
|
+
}
|
|
2146
|
+
getStorageKey(gameId, key) {
|
|
2147
|
+
return `${gameId}:${key}`;
|
|
2148
|
+
}
|
|
2149
|
+
async saveGameData(gameId, key, value, operation, path) {
|
|
2150
|
+
const storageKey = this.getStorageKey(gameId, key);
|
|
2151
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2152
|
+
const existingRaw = await this.service.dataGetItem(storageKey);
|
|
2153
|
+
const existing = existingRaw ? JSON.parse(existingRaw) : null;
|
|
2154
|
+
const createdAt = existing?.created_at ?? now;
|
|
2155
|
+
const useOperation = operation !== void 0;
|
|
2156
|
+
let storedValue;
|
|
2157
|
+
let opResult;
|
|
2158
|
+
if (path) {
|
|
2159
|
+
const current = getAtPath(existing?.value, path);
|
|
2160
|
+
const next = useOperation ? applyOperation(operation, current, value) : value;
|
|
2161
|
+
storedValue = setAtPath(existing?.value, path, next);
|
|
2162
|
+
opResult = next;
|
|
2163
|
+
} else {
|
|
2164
|
+
const current = existing?.value;
|
|
2165
|
+
const next = useOperation ? applyOperation(operation, current, value) : value;
|
|
2166
|
+
storedValue = next;
|
|
2167
|
+
opResult = next;
|
|
2168
|
+
}
|
|
2169
|
+
const item = {
|
|
2170
|
+
key,
|
|
2171
|
+
value: storedValue,
|
|
2172
|
+
created_at: createdAt,
|
|
2173
|
+
updated_at: now
|
|
2174
|
+
};
|
|
2175
|
+
await this.service.dataSetItem(storageKey, JSON.stringify(item));
|
|
2176
|
+
const response = { success: true, message: "Data saved successfully" };
|
|
2177
|
+
if (useOperation && operation !== "set") {
|
|
2178
|
+
response.result = opResult;
|
|
2179
|
+
}
|
|
2180
|
+
return response;
|
|
2181
|
+
}
|
|
2182
|
+
async batchSaveGameData(gameId, items) {
|
|
2183
|
+
const results = [];
|
|
2184
|
+
let usedOperation = false;
|
|
2185
|
+
for (const item of items) {
|
|
2186
|
+
if (item.operation !== void 0) usedOperation = true;
|
|
2187
|
+
try {
|
|
2188
|
+
const saved = await this.saveGameData(gameId, item.key, item.value, item.operation, item.path);
|
|
2189
|
+
results.push({ key: item.key, success: true, ...saved.result !== void 0 && { result: saved.result } });
|
|
2190
|
+
} catch (error) {
|
|
2191
|
+
results.push({
|
|
2192
|
+
key: item.key,
|
|
2193
|
+
success: false,
|
|
2194
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
const allSucceeded = results.every((r) => r.success);
|
|
2199
|
+
return {
|
|
2200
|
+
success: allSucceeded,
|
|
2201
|
+
message: `${results.filter((r) => r.success).length}/${items.length} items saved`,
|
|
2202
|
+
...usedOperation && { results }
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2205
|
+
async getGameData(gameId, key) {
|
|
2206
|
+
const raw = await this.service.dataGetItem(this.getStorageKey(gameId, key));
|
|
2207
|
+
return raw ? JSON.parse(raw) : null;
|
|
2208
|
+
}
|
|
2209
|
+
async getMultipleGameData(gameId, keys) {
|
|
2210
|
+
const items = [];
|
|
2211
|
+
for (const key of keys) {
|
|
2212
|
+
const item = await this.getGameData(gameId, key);
|
|
2213
|
+
if (item) items.push(item);
|
|
2214
|
+
}
|
|
2215
|
+
return items;
|
|
2216
|
+
}
|
|
2217
|
+
async deleteGameData(gameId, key) {
|
|
2218
|
+
const storageKey = this.getStorageKey(gameId, key);
|
|
2219
|
+
const existing = await this.service.dataGetItem(storageKey);
|
|
2220
|
+
if (existing === null) return false;
|
|
2221
|
+
await this.service.dataRemoveItem(storageKey);
|
|
2222
|
+
return true;
|
|
2223
|
+
}
|
|
2224
|
+
async deleteMultipleGameData(gameId, keys) {
|
|
2225
|
+
let count = 0;
|
|
2226
|
+
for (const key of keys) {
|
|
2227
|
+
if (await this.deleteGameData(gameId, key)) count++;
|
|
2228
|
+
}
|
|
2229
|
+
return count;
|
|
2230
|
+
}
|
|
2231
|
+
};
|
|
1992
2232
|
|
|
1993
2233
|
// src/core/client.ts
|
|
1994
2234
|
function determineEnvironmentFromParentUrl() {
|
|
@@ -2035,6 +2275,11 @@ var HyveClient = class {
|
|
|
2035
2275
|
storageMode;
|
|
2036
2276
|
cloudStorageAdapter;
|
|
2037
2277
|
localStorageAdapter;
|
|
2278
|
+
crazyGamesStorageAdapter = null;
|
|
2279
|
+
partnerApiKey = null;
|
|
2280
|
+
partnerApiBaseUrl = null;
|
|
2281
|
+
/** Raw (unprefixed) CrazyGames __dangerousUserId, seeded at init and on login. */
|
|
2282
|
+
crazyGamesUserId = null;
|
|
2038
2283
|
/**
|
|
2039
2284
|
* Creates a new HyveClient instance
|
|
2040
2285
|
* @param config Optional configuration including telemetry and ads
|
|
@@ -2062,10 +2307,16 @@ var HyveClient = class {
|
|
|
2062
2307
|
return success;
|
|
2063
2308
|
});
|
|
2064
2309
|
}
|
|
2310
|
+
this.partnerApiKey = config?.partnerApiKey ?? null;
|
|
2311
|
+
this.partnerApiBaseUrl = config?.partnerApiBaseUrl ?? null;
|
|
2065
2312
|
if (typeof window !== "undefined" && CrazyGamesService.isCrazyGamesDomain()) {
|
|
2066
2313
|
this.crazyGamesService = new CrazyGamesService();
|
|
2067
|
-
this.
|
|
2314
|
+
this.crazyGamesStorageAdapter = new CrazyGamesStorageAdapter(this.crazyGamesService);
|
|
2315
|
+
this.crazyGamesInitPromise = this.crazyGamesService.initialize().then(async (success) => {
|
|
2068
2316
|
logger.info("CrazyGames SDK initialized:", success);
|
|
2317
|
+
if (success) {
|
|
2318
|
+
await this.seedCrazyGamesUser();
|
|
2319
|
+
}
|
|
2069
2320
|
return success;
|
|
2070
2321
|
});
|
|
2071
2322
|
}
|
|
@@ -2161,6 +2412,16 @@ var HyveClient = class {
|
|
|
2161
2412
|
* @returns Promise resolving to boolean indicating success
|
|
2162
2413
|
*/
|
|
2163
2414
|
async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails, platformId) {
|
|
2415
|
+
if (this.partnerApiKey) {
|
|
2416
|
+
return this.sendPartnerTelemetry(
|
|
2417
|
+
eventLocation,
|
|
2418
|
+
eventCategory,
|
|
2419
|
+
eventAction,
|
|
2420
|
+
eventSubCategory,
|
|
2421
|
+
eventSubAction,
|
|
2422
|
+
eventDetails
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2164
2425
|
if (!this.jwtToken) {
|
|
2165
2426
|
logger.error("JWT token required. Ensure hyve-access and game-id are present in the URL.");
|
|
2166
2427
|
return false;
|
|
@@ -2170,42 +2431,11 @@ var HyveClient = class {
|
|
|
2170
2431
|
return false;
|
|
2171
2432
|
}
|
|
2172
2433
|
try {
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
JSON.parse(eventDetails);
|
|
2177
|
-
} else if (typeof eventDetails === "object") {
|
|
2178
|
-
JSON.stringify(eventDetails);
|
|
2179
|
-
}
|
|
2180
|
-
} catch (validationError) {
|
|
2181
|
-
logger.error("Invalid JSON in eventDetails:", validationError);
|
|
2182
|
-
logger.error("eventDetails value:", eventDetails);
|
|
2183
|
-
return false;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
let parsedEventDetails = {};
|
|
2187
|
-
if (eventDetails) {
|
|
2188
|
-
if (typeof eventDetails === "string") {
|
|
2189
|
-
try {
|
|
2190
|
-
parsedEventDetails = JSON.parse(eventDetails);
|
|
2191
|
-
} catch {
|
|
2192
|
-
}
|
|
2193
|
-
} else {
|
|
2194
|
-
parsedEventDetails = eventDetails;
|
|
2195
|
-
}
|
|
2434
|
+
const enrichedEventDetails = this.enrichEventDetails(eventDetails);
|
|
2435
|
+
if (enrichedEventDetails === null) {
|
|
2436
|
+
return false;
|
|
2196
2437
|
}
|
|
2197
2438
|
const attribution = getAttributionData();
|
|
2198
|
-
const enrichedEventDetails = {
|
|
2199
|
-
// Device info
|
|
2200
|
-
...getEssentialDeviceInfo(),
|
|
2201
|
-
// Attribution data
|
|
2202
|
-
...attribution,
|
|
2203
|
-
// Timestamp and session context
|
|
2204
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2205
|
-
session_id: this.sessionId,
|
|
2206
|
-
// Event-specific details can override any of the above
|
|
2207
|
-
...parsedEventDetails
|
|
2208
|
-
};
|
|
2209
2439
|
const telemetryEvent = {
|
|
2210
2440
|
game_id: this.gameId,
|
|
2211
2441
|
session_id: this.sessionId,
|
|
@@ -2244,6 +2474,122 @@ var HyveClient = class {
|
|
|
2244
2474
|
return false;
|
|
2245
2475
|
}
|
|
2246
2476
|
}
|
|
2477
|
+
/**
|
|
2478
|
+
* Validates and enriches user-provided event details with device info,
|
|
2479
|
+
* attribution data, and session context — matching the enrichment
|
|
2480
|
+
* platform-v2 applies in sendAnalyticsEvent. Shared by the JWT and partner
|
|
2481
|
+
* telemetry paths.
|
|
2482
|
+
* @returns Enriched details object, or null if eventDetails is not valid JSON.
|
|
2483
|
+
*/
|
|
2484
|
+
enrichEventDetails(eventDetails) {
|
|
2485
|
+
let parsedEventDetails = {};
|
|
2486
|
+
if (eventDetails) {
|
|
2487
|
+
try {
|
|
2488
|
+
if (typeof eventDetails === "string") {
|
|
2489
|
+
parsedEventDetails = JSON.parse(eventDetails);
|
|
2490
|
+
} else if (typeof eventDetails === "object") {
|
|
2491
|
+
JSON.stringify(eventDetails);
|
|
2492
|
+
parsedEventDetails = eventDetails;
|
|
2493
|
+
}
|
|
2494
|
+
} catch (validationError) {
|
|
2495
|
+
logger.error("Invalid JSON in eventDetails:", validationError);
|
|
2496
|
+
logger.error("eventDetails value:", eventDetails);
|
|
2497
|
+
return null;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
const attribution = getAttributionData();
|
|
2501
|
+
return {
|
|
2502
|
+
// Device info
|
|
2503
|
+
...getEssentialDeviceInfo(),
|
|
2504
|
+
// Attribution data
|
|
2505
|
+
...attribution,
|
|
2506
|
+
// Timestamp and session context
|
|
2507
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2508
|
+
session_id: this.sessionId,
|
|
2509
|
+
// Event-specific details can override any of the above
|
|
2510
|
+
...parsedEventDetails
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
/**
|
|
2514
|
+
* Sends a telemetry event via the partner analytics endpoint using a partner
|
|
2515
|
+
* API key (x-api-key) instead of a hyve JWT. Used by externally-hosted games
|
|
2516
|
+
* such as CrazyGames. game_id is derived from the API key server-side and is
|
|
2517
|
+
* therefore not sent. hyve_user_id carries the `cg:`-prefixed CrazyGames id,
|
|
2518
|
+
* or a guest sentinel when the player is not logged in.
|
|
2519
|
+
*/
|
|
2520
|
+
async sendPartnerTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails) {
|
|
2521
|
+
if (!this.partnerApiKey) {
|
|
2522
|
+
logger.error("Partner API key required for partner telemetry path.");
|
|
2523
|
+
return false;
|
|
2524
|
+
}
|
|
2525
|
+
if (this.crazyGamesService && this.crazyGamesInitPromise) {
|
|
2526
|
+
await this.crazyGamesInitPromise;
|
|
2527
|
+
}
|
|
2528
|
+
try {
|
|
2529
|
+
const enrichedEventDetails = this.enrichEventDetails(eventDetails);
|
|
2530
|
+
if (enrichedEventDetails === null) {
|
|
2531
|
+
return false;
|
|
2532
|
+
}
|
|
2533
|
+
const hyveUserId = this.crazyGamesUserId ? `cg:${this.crazyGamesUserId}` : "cg:guest";
|
|
2534
|
+
const partnerEvent = {
|
|
2535
|
+
session_id: this.sessionId,
|
|
2536
|
+
hyve_user_id: hyveUserId,
|
|
2537
|
+
event_location: eventLocation,
|
|
2538
|
+
event_category: eventCategory,
|
|
2539
|
+
event_sub_category: eventSubCategory || null,
|
|
2540
|
+
event_action: eventAction,
|
|
2541
|
+
event_sub_action: eventSubAction || null,
|
|
2542
|
+
event_details: enrichedEventDetails
|
|
2543
|
+
};
|
|
2544
|
+
logger.debug("Sending partner telemetry event:", partnerEvent);
|
|
2545
|
+
const base = this.partnerApiBaseUrl || this.apiBaseUrl;
|
|
2546
|
+
const telemetryUrl = `${base}/api/v1/partners/analytics/events`;
|
|
2547
|
+
const response = await fetch(telemetryUrl, {
|
|
2548
|
+
method: "POST",
|
|
2549
|
+
headers: {
|
|
2550
|
+
"Content-Type": "application/json",
|
|
2551
|
+
"x-api-key": this.partnerApiKey
|
|
2552
|
+
},
|
|
2553
|
+
body: JSON.stringify(partnerEvent)
|
|
2554
|
+
});
|
|
2555
|
+
if (response.ok) {
|
|
2556
|
+
logger.info("Partner telemetry event sent successfully:", response.status);
|
|
2557
|
+
return true;
|
|
2558
|
+
} else {
|
|
2559
|
+
const errorText = await response.text();
|
|
2560
|
+
logger.error(
|
|
2561
|
+
"Failed to send partner telemetry event:",
|
|
2562
|
+
response.status,
|
|
2563
|
+
errorText
|
|
2564
|
+
);
|
|
2565
|
+
return false;
|
|
2566
|
+
}
|
|
2567
|
+
} catch (error) {
|
|
2568
|
+
logger.error("Error sending partner telemetry event:", error);
|
|
2569
|
+
return false;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
/**
|
|
2573
|
+
* Seeds the CrazyGames user id used for telemetry attribution and registers
|
|
2574
|
+
* an auth listener so the id updates if a guest logs in mid-session.
|
|
2575
|
+
* The id is client-asserted (spoofable) and used only as an attribution label.
|
|
2576
|
+
*/
|
|
2577
|
+
async seedCrazyGamesUser() {
|
|
2578
|
+
if (!this.crazyGamesService) return;
|
|
2579
|
+
if (!this.crazyGamesService.isUserAccountAvailable()) {
|
|
2580
|
+
logger.info("CrazyGames account system unavailable \u2014 telemetry attributed by session only");
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
const user = await this.crazyGamesService.getUser();
|
|
2584
|
+
this.crazyGamesUserId = user?.__dangerousUserId ?? null;
|
|
2585
|
+
if (this.crazyGamesUserId) {
|
|
2586
|
+
logger.info("CrazyGames user id seeded for telemetry attribution");
|
|
2587
|
+
}
|
|
2588
|
+
this.crazyGamesService.addAuthListener((updatedUser) => {
|
|
2589
|
+
this.crazyGamesUserId = updatedUser?.__dangerousUserId ?? null;
|
|
2590
|
+
logger.info("CrazyGames auth state changed; telemetry user id updated");
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2247
2593
|
/**
|
|
2248
2594
|
* Required lifecycle telemetry — Session start.
|
|
2249
2595
|
* See https://docs.hyve.gg/docs/telemetry#required-lifecycle-events
|
|
@@ -2464,12 +2810,28 @@ var HyveClient = class {
|
|
|
2464
2810
|
logger.info("Client reset with new sessionId:", this.sessionId);
|
|
2465
2811
|
}
|
|
2466
2812
|
/**
|
|
2467
|
-
* Get the storage adapter based on mode
|
|
2813
|
+
* Get the storage adapter based on mode.
|
|
2814
|
+
*
|
|
2815
|
+
* Selection order:
|
|
2816
|
+
* 1. An explicit `mode` override ('cloud' | 'local') always wins.
|
|
2817
|
+
* 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
|
|
2818
|
+
* (D3 — public storageMode type is preserved; this is chosen internally).
|
|
2819
|
+
* 3. Otherwise the configured storageMode is used.
|
|
2820
|
+
*
|
|
2821
|
+
* Awaits CrazyGames SDK initialization so the data store is ready before use.
|
|
2468
2822
|
* @param mode Storage mode override (cloud or local)
|
|
2469
2823
|
*/
|
|
2470
|
-
getStorageAdapter(mode) {
|
|
2471
|
-
|
|
2472
|
-
|
|
2824
|
+
async getStorageAdapter(mode) {
|
|
2825
|
+
if (mode) {
|
|
2826
|
+
return mode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
|
|
2827
|
+
}
|
|
2828
|
+
if (this.crazyGamesService && this.crazyGamesStorageAdapter) {
|
|
2829
|
+
if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
|
|
2830
|
+
if (this.crazyGamesService.getEnvironment() === "crazygames") {
|
|
2831
|
+
return this.crazyGamesStorageAdapter;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
return this.storageMode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
|
|
2473
2835
|
}
|
|
2474
2836
|
/**
|
|
2475
2837
|
* Returns the current game ID or throws if not available.
|
|
@@ -2493,7 +2855,7 @@ var HyveClient = class {
|
|
|
2493
2855
|
const gameId = this.requireGameId();
|
|
2494
2856
|
const storageMode = storage || this.storageMode;
|
|
2495
2857
|
logger.debug(`Saving game data to ${storageMode}: ${gameId}/${key}`);
|
|
2496
|
-
const adapter = this.getStorageAdapter(storage);
|
|
2858
|
+
const adapter = await this.getStorageAdapter(storage);
|
|
2497
2859
|
const response = await adapter.saveGameData(gameId, key, value, operation, path);
|
|
2498
2860
|
logger.info(`Game data saved successfully to ${storageMode}: ${gameId}/${key}`);
|
|
2499
2861
|
return response;
|
|
@@ -2508,7 +2870,7 @@ var HyveClient = class {
|
|
|
2508
2870
|
const gameId = this.requireGameId();
|
|
2509
2871
|
const storageMode = storage || this.storageMode;
|
|
2510
2872
|
logger.debug(`Batch saving ${items.length} game data entries to ${storageMode} for game: ${gameId}`);
|
|
2511
|
-
const adapter = this.getStorageAdapter(storage);
|
|
2873
|
+
const adapter = await this.getStorageAdapter(storage);
|
|
2512
2874
|
const response = await adapter.batchSaveGameData(gameId, items);
|
|
2513
2875
|
logger.info(`Batch saved ${items.length} game data entries successfully to ${storageMode}`);
|
|
2514
2876
|
return response;
|
|
@@ -2523,7 +2885,7 @@ var HyveClient = class {
|
|
|
2523
2885
|
const gameId = this.requireGameId();
|
|
2524
2886
|
const storageMode = storage || this.storageMode;
|
|
2525
2887
|
logger.debug(`Getting game data from ${storageMode}: ${gameId}/${key}`);
|
|
2526
|
-
const adapter = this.getStorageAdapter(storage);
|
|
2888
|
+
const adapter = await this.getStorageAdapter(storage);
|
|
2527
2889
|
const data = await adapter.getGameData(gameId, key);
|
|
2528
2890
|
if (data) {
|
|
2529
2891
|
logger.info(`Game data retrieved successfully from ${storageMode}: ${gameId}/${key}`);
|
|
@@ -2542,7 +2904,7 @@ var HyveClient = class {
|
|
|
2542
2904
|
const gameId = this.requireGameId();
|
|
2543
2905
|
const storageMode = storage || this.storageMode;
|
|
2544
2906
|
logger.debug(`Getting ${keys.length} game data entries from ${storageMode} for game: ${gameId}`);
|
|
2545
|
-
const adapter = this.getStorageAdapter(storage);
|
|
2907
|
+
const adapter = await this.getStorageAdapter(storage);
|
|
2546
2908
|
const data = await adapter.getMultipleGameData(gameId, keys);
|
|
2547
2909
|
logger.info(`Retrieved ${data.length} game data entries from ${storageMode}`);
|
|
2548
2910
|
return data;
|
|
@@ -2557,7 +2919,7 @@ var HyveClient = class {
|
|
|
2557
2919
|
const gameId = this.requireGameId();
|
|
2558
2920
|
const storageMode = storage || this.storageMode;
|
|
2559
2921
|
logger.debug(`Deleting game data from ${storageMode}: ${gameId}/${key}`);
|
|
2560
|
-
const adapter = this.getStorageAdapter(storage);
|
|
2922
|
+
const adapter = await this.getStorageAdapter(storage);
|
|
2561
2923
|
const deleted = await adapter.deleteGameData(gameId, key);
|
|
2562
2924
|
if (deleted) {
|
|
2563
2925
|
logger.info(`Game data deleted successfully from ${storageMode}: ${gameId}/${key}`);
|
|
@@ -2576,7 +2938,7 @@ var HyveClient = class {
|
|
|
2576
2938
|
const gameId = this.requireGameId();
|
|
2577
2939
|
const storageMode = storage || this.storageMode;
|
|
2578
2940
|
logger.debug(`Deleting ${keys.length} game data entries from ${storageMode} for game: ${gameId}`);
|
|
2579
|
-
const adapter = this.getStorageAdapter(storage);
|
|
2941
|
+
const adapter = await this.getStorageAdapter(storage);
|
|
2580
2942
|
const deletedCount = await adapter.deleteMultipleGameData(gameId, keys);
|
|
2581
2943
|
logger.info(`Deleted ${deletedCount} game data entries from ${storageMode}`);
|
|
2582
2944
|
return deletedCount;
|