@hyve-sdk/js 2.14.0-canary.1 → 2.14.0-canary.3

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 CHANGED
@@ -734,12 +734,16 @@ declare class HyveClient {
734
734
  */
735
735
  reset(): void;
736
736
  /**
737
- * Get the storage adapter based on mode.
737
+ * Get the storage adapter based on platform and mode.
738
738
  *
739
739
  * Selection order:
740
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
741
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
742
- * (D3 public storageMode type is preserved; this is chosen internally).
740
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
741
+ * 'cloud'/'local' are durability intents and the CrazyGames store
742
+ * satisfies both (account-synced when logged in, device-local for
743
+ * guests). This keeps games portable: per-call modes written for
744
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
745
+ * work anyway (no hyve JWT exists there).
746
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
743
747
  * 3. Otherwise the configured storageMode is used.
744
748
  *
745
749
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
@@ -956,8 +960,9 @@ declare class PlaygamaService {
956
960
  /**
957
961
  * CrazyGames SDK integration service
958
962
  *
959
- * Loads and initializes the CrazyGames SDK v2 when running on the CrazyGames platform.
960
- * The SDK is loaded from CDN and exposes a global `window.CrazyGames.SDK` object.
963
+ * Loads and initializes the CrazyGames SDK v3 when running on the CrazyGames platform.
964
+ * The SDK is loaded from CDN, exposes a global `window.CrazyGames.SDK` object, and
965
+ * requires an explicit `await SDK.init()` before any module is usable.
961
966
  *
962
967
  * Detection: CrazyGames games run in an iframe on crazygames.com, so detection
963
968
  * uses document.referrer and parent frame location checks.
@@ -997,23 +1002,26 @@ interface CrazyGamesUserModule {
997
1002
  removeAuthListener(callback: CrazyGamesAuthListener): void;
998
1003
  }
999
1004
  /**
1000
- * CrazyGames key/value data store. Treated as async (promise-based) per the
1001
- * SDK v2 docs; awaiting a synchronous return is harmless, so the wrappers
1002
- * `await` every call regardless.
1005
+ * CrazyGames key/value data store (SDK v3). Methods are synchronous
1006
+ * (localStorage-like); the SDK debounces persistence by ~1s internally.
1007
+ * Wrappers still `await` the calls, which is harmless and future-proof.
1003
1008
  */
1004
1009
  interface CrazyGamesDataModule {
1005
- getItem(key: string): Promise<string | null> | string | null;
1006
- setItem(key: string, value: string): Promise<void> | void;
1007
- removeItem(key: string): Promise<void> | void;
1008
- clear(): Promise<void> | void;
1009
- getKeys?(): Promise<string[]> | string[];
1010
+ getItem(key: string): string | null;
1011
+ setItem(key: string, value: string): void;
1012
+ removeItem(key: string): void;
1013
+ clear(): void;
1010
1014
  }
1011
1015
  interface CrazyGamesSDK {
1016
+ /** v3 requires init() to be awaited before any module is used. */
1017
+ init(): Promise<void>;
1018
+ /** Resolved environment — a plain property in v3 (getEnvironment() was removed). */
1019
+ environment: CrazyGamesEnvironment;
1012
1020
  ad: CrazyGamesAdModule;
1013
1021
  game: CrazyGamesGameModule;
1014
1022
  user: CrazyGamesUserModule;
1015
- data: CrazyGamesDataModule;
1016
- getEnvironment(): Promise<CrazyGamesEnvironment>;
1023
+ /** Absent unless the game submission has the "Progress Save" toggle enabled. */
1024
+ data?: CrazyGamesDataModule;
1017
1025
  }
1018
1026
  declare global {
1019
1027
  interface Window {
@@ -1032,7 +1040,8 @@ declare class CrazyGamesService {
1032
1040
  */
1033
1041
  static isCrazyGamesDomain(): boolean;
1034
1042
  /**
1035
- * Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
1043
+ * Loads the CrazyGames SDK v3 from CDN, runs the mandatory `SDK.init()`, and
1044
+ * confirms the environment is 'crazygames' (or 'local' for dev).
1036
1045
  * Safe to call multiple times — resolves immediately if already initialized.
1037
1046
  */
1038
1047
  initialize(): Promise<boolean>;
@@ -1093,8 +1102,13 @@ declare class CrazyGamesService {
1093
1102
  /** Removes a previously registered auth listener. */
1094
1103
  removeAuthListener(callback: CrazyGamesAuthListener): void;
1095
1104
  /**
1096
- * Reads a value from the CrazyGames data store. Returns null when the SDK
1097
- * is unavailable or the key is absent.
1105
+ * Returns the SDK v3 data module, throwing an actionable error when the SDK
1106
+ * is not initialized or the module is disabled for this game submission.
1107
+ */
1108
+ private getDataModule;
1109
+ /**
1110
+ * Reads a value from the CrazyGames data store. Returns null when the key
1111
+ * is absent.
1098
1112
  */
1099
1113
  dataGetItem(key: string): Promise<string | null>;
1100
1114
  /** Writes a value to the CrazyGames data store. */
package/dist/index.d.ts CHANGED
@@ -734,12 +734,16 @@ declare class HyveClient {
734
734
  */
735
735
  reset(): void;
736
736
  /**
737
- * Get the storage adapter based on mode.
737
+ * Get the storage adapter based on platform and mode.
738
738
  *
739
739
  * Selection order:
740
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
741
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
742
- * (D3 public storageMode type is preserved; this is chosen internally).
740
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
741
+ * 'cloud'/'local' are durability intents and the CrazyGames store
742
+ * satisfies both (account-synced when logged in, device-local for
743
+ * guests). This keeps games portable: per-call modes written for
744
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
745
+ * work anyway (no hyve JWT exists there).
746
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
743
747
  * 3. Otherwise the configured storageMode is used.
744
748
  *
745
749
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
@@ -956,8 +960,9 @@ declare class PlaygamaService {
956
960
  /**
957
961
  * CrazyGames SDK integration service
958
962
  *
959
- * Loads and initializes the CrazyGames SDK v2 when running on the CrazyGames platform.
960
- * The SDK is loaded from CDN and exposes a global `window.CrazyGames.SDK` object.
963
+ * Loads and initializes the CrazyGames SDK v3 when running on the CrazyGames platform.
964
+ * The SDK is loaded from CDN, exposes a global `window.CrazyGames.SDK` object, and
965
+ * requires an explicit `await SDK.init()` before any module is usable.
961
966
  *
962
967
  * Detection: CrazyGames games run in an iframe on crazygames.com, so detection
963
968
  * uses document.referrer and parent frame location checks.
@@ -997,23 +1002,26 @@ interface CrazyGamesUserModule {
997
1002
  removeAuthListener(callback: CrazyGamesAuthListener): void;
998
1003
  }
999
1004
  /**
1000
- * CrazyGames key/value data store. Treated as async (promise-based) per the
1001
- * SDK v2 docs; awaiting a synchronous return is harmless, so the wrappers
1002
- * `await` every call regardless.
1005
+ * CrazyGames key/value data store (SDK v3). Methods are synchronous
1006
+ * (localStorage-like); the SDK debounces persistence by ~1s internally.
1007
+ * Wrappers still `await` the calls, which is harmless and future-proof.
1003
1008
  */
1004
1009
  interface CrazyGamesDataModule {
1005
- getItem(key: string): Promise<string | null> | string | null;
1006
- setItem(key: string, value: string): Promise<void> | void;
1007
- removeItem(key: string): Promise<void> | void;
1008
- clear(): Promise<void> | void;
1009
- getKeys?(): Promise<string[]> | string[];
1010
+ getItem(key: string): string | null;
1011
+ setItem(key: string, value: string): void;
1012
+ removeItem(key: string): void;
1013
+ clear(): void;
1010
1014
  }
1011
1015
  interface CrazyGamesSDK {
1016
+ /** v3 requires init() to be awaited before any module is used. */
1017
+ init(): Promise<void>;
1018
+ /** Resolved environment — a plain property in v3 (getEnvironment() was removed). */
1019
+ environment: CrazyGamesEnvironment;
1012
1020
  ad: CrazyGamesAdModule;
1013
1021
  game: CrazyGamesGameModule;
1014
1022
  user: CrazyGamesUserModule;
1015
- data: CrazyGamesDataModule;
1016
- getEnvironment(): Promise<CrazyGamesEnvironment>;
1023
+ /** Absent unless the game submission has the "Progress Save" toggle enabled. */
1024
+ data?: CrazyGamesDataModule;
1017
1025
  }
1018
1026
  declare global {
1019
1027
  interface Window {
@@ -1032,7 +1040,8 @@ declare class CrazyGamesService {
1032
1040
  */
1033
1041
  static isCrazyGamesDomain(): boolean;
1034
1042
  /**
1035
- * Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
1043
+ * Loads the CrazyGames SDK v3 from CDN, runs the mandatory `SDK.init()`, and
1044
+ * confirms the environment is 'crazygames' (or 'local' for dev).
1036
1045
  * Safe to call multiple times — resolves immediately if already initialized.
1037
1046
  */
1038
1047
  initialize(): Promise<boolean>;
@@ -1093,8 +1102,13 @@ declare class CrazyGamesService {
1093
1102
  /** Removes a previously registered auth listener. */
1094
1103
  removeAuthListener(callback: CrazyGamesAuthListener): void;
1095
1104
  /**
1096
- * Reads a value from the CrazyGames data store. Returns null when the SDK
1097
- * is unavailable or the key is absent.
1105
+ * Returns the SDK v3 data module, throwing an actionable error when the SDK
1106
+ * is not initialized or the module is disabled for this game submission.
1107
+ */
1108
+ private getDataModule;
1109
+ /**
1110
+ * Reads a value from the CrazyGames data store. Returns null when the key
1111
+ * is absent.
1098
1112
  */
1099
1113
  dataGetItem(key: string): Promise<string | null>;
1100
1114
  /** Writes a value to the CrazyGames data store. */
package/dist/index.js CHANGED
@@ -1239,7 +1239,7 @@ var PlaygamaService = class {
1239
1239
  };
1240
1240
 
1241
1241
  // src/services/crazygames.ts
1242
- var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v2.js";
1242
+ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1243
1243
  var CrazyGamesService = class {
1244
1244
  initialized = false;
1245
1245
  environment = null;
@@ -1267,7 +1267,8 @@ var CrazyGamesService = class {
1267
1267
  }
1268
1268
  }
1269
1269
  /**
1270
- * Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
1270
+ * Loads the CrazyGames SDK v3 from CDN, runs the mandatory `SDK.init()`, and
1271
+ * confirms the environment is 'crazygames' (or 'local' for dev).
1271
1272
  * Safe to call multiple times — resolves immediately if already initialized.
1272
1273
  */
1273
1274
  async initialize() {
@@ -1279,13 +1280,19 @@ var CrazyGamesService = class {
1279
1280
  logger.warn("[CrazyGamesService] SDK not found after script load");
1280
1281
  return false;
1281
1282
  }
1282
- const env = await sdk.getEnvironment();
1283
+ await sdk.init();
1284
+ const env = sdk.environment;
1283
1285
  if (env !== "crazygames" && env !== "local") {
1284
1286
  logger.warn("[CrazyGamesService] Unexpected environment:", env);
1285
1287
  return false;
1286
1288
  }
1287
1289
  this.environment = env;
1288
1290
  this.initialized = true;
1291
+ if (!sdk.data) {
1292
+ logger.warn(
1293
+ '[CrazyGamesService] data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings to use persistent storage'
1294
+ );
1295
+ }
1289
1296
  return true;
1290
1297
  } catch (error) {
1291
1298
  logger.warn("[CrazyGamesService] Failed to initialize:", error);
@@ -1452,30 +1459,36 @@ var CrazyGamesService = class {
1452
1459
  }
1453
1460
  }
1454
1461
  /**
1455
- * Reads a value from the CrazyGames data store. Returns null when the SDK
1456
- * is unavailable or the key is absent.
1462
+ * Returns the SDK v3 data module, throwing an actionable error when the SDK
1463
+ * is not initialized or the module is disabled for this game submission.
1457
1464
  */
1458
- async dataGetItem(key) {
1465
+ getDataModule() {
1459
1466
  const sdk = window.CrazyGames?.SDK;
1460
- if (!this.initialized || !sdk) return null;
1461
- const value = await sdk.data.getItem(key);
1467
+ if (!this.initialized || !sdk) {
1468
+ throw new Error("CrazyGames SDK not initialized");
1469
+ }
1470
+ if (!sdk.data) {
1471
+ throw new Error(
1472
+ 'CrazyGames data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings'
1473
+ );
1474
+ }
1475
+ return sdk.data;
1476
+ }
1477
+ /**
1478
+ * Reads a value from the CrazyGames data store. Returns null when the key
1479
+ * is absent.
1480
+ */
1481
+ async dataGetItem(key) {
1482
+ const value = await this.getDataModule().getItem(key);
1462
1483
  return value ?? null;
1463
1484
  }
1464
1485
  /** Writes a value to the CrazyGames data store. */
1465
1486
  async dataSetItem(key, value) {
1466
- const sdk = window.CrazyGames?.SDK;
1467
- if (!this.initialized || !sdk) {
1468
- throw new Error("CrazyGames SDK not initialized");
1469
- }
1470
- await sdk.data.setItem(key, value);
1487
+ await this.getDataModule().setItem(key, value);
1471
1488
  }
1472
1489
  /** Removes a value from the CrazyGames data store. */
1473
1490
  async dataRemoveItem(key) {
1474
- const sdk = window.CrazyGames?.SDK;
1475
- if (!this.initialized || !sdk) {
1476
- throw new Error("CrazyGames SDK not initialized");
1477
- }
1478
- await sdk.data.removeItem(key);
1491
+ await this.getDataModule().removeItem(key);
1479
1492
  }
1480
1493
  loadScript() {
1481
1494
  return new Promise((resolve, reject) => {
@@ -2883,28 +2896,34 @@ var HyveClient = class {
2883
2896
  logger.info("Client reset with new sessionId:", this.sessionId);
2884
2897
  }
2885
2898
  /**
2886
- * Get the storage adapter based on mode.
2899
+ * Get the storage adapter based on platform and mode.
2887
2900
  *
2888
2901
  * Selection order:
2889
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
2890
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
2891
- * (D3 public storageMode type is preserved; this is chosen internally).
2902
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
2903
+ * 'cloud'/'local' are durability intents and the CrazyGames store
2904
+ * satisfies both (account-synced when logged in, device-local for
2905
+ * guests). This keeps games portable: per-call modes written for
2906
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
2907
+ * work anyway (no hyve JWT exists there).
2908
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
2892
2909
  * 3. Otherwise the configured storageMode is used.
2893
2910
  *
2894
2911
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
2895
2912
  * @param mode Storage mode override (cloud or local)
2896
2913
  */
2897
2914
  async getStorageAdapter(mode) {
2898
- if (mode) {
2899
- return mode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2900
- }
2901
2915
  if (this.crazyGamesService && this.crazyGamesStorageAdapter) {
2902
2916
  if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
2903
- if (this.crazyGamesService.getEnvironment() === "crazygames") {
2917
+ const env = this.crazyGamesService.getEnvironment();
2918
+ if (env === "crazygames" || env === "local") {
2919
+ if (mode) {
2920
+ logger.debug(`Storage mode '${mode}' ignored on CrazyGames \u2014 using the CrazyGames data store`);
2921
+ }
2904
2922
  return this.crazyGamesStorageAdapter;
2905
2923
  }
2906
2924
  }
2907
- return this.storageMode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2925
+ const selected = mode ?? this.storageMode;
2926
+ return selected === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2908
2927
  }
2909
2928
  /**
2910
2929
  * Returns the current game ID or throws if not available.
package/dist/index.mjs CHANGED
@@ -1198,7 +1198,7 @@ var PlaygamaService = class {
1198
1198
  };
1199
1199
 
1200
1200
  // src/services/crazygames.ts
1201
- var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v2.js";
1201
+ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1202
1202
  var CrazyGamesService = class {
1203
1203
  initialized = false;
1204
1204
  environment = null;
@@ -1226,7 +1226,8 @@ var CrazyGamesService = class {
1226
1226
  }
1227
1227
  }
1228
1228
  /**
1229
- * Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
1229
+ * Loads the CrazyGames SDK v3 from CDN, runs the mandatory `SDK.init()`, and
1230
+ * confirms the environment is 'crazygames' (or 'local' for dev).
1230
1231
  * Safe to call multiple times — resolves immediately if already initialized.
1231
1232
  */
1232
1233
  async initialize() {
@@ -1238,13 +1239,19 @@ var CrazyGamesService = class {
1238
1239
  logger.warn("[CrazyGamesService] SDK not found after script load");
1239
1240
  return false;
1240
1241
  }
1241
- const env = await sdk.getEnvironment();
1242
+ await sdk.init();
1243
+ const env = sdk.environment;
1242
1244
  if (env !== "crazygames" && env !== "local") {
1243
1245
  logger.warn("[CrazyGamesService] Unexpected environment:", env);
1244
1246
  return false;
1245
1247
  }
1246
1248
  this.environment = env;
1247
1249
  this.initialized = true;
1250
+ if (!sdk.data) {
1251
+ logger.warn(
1252
+ '[CrazyGamesService] data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings to use persistent storage'
1253
+ );
1254
+ }
1248
1255
  return true;
1249
1256
  } catch (error) {
1250
1257
  logger.warn("[CrazyGamesService] Failed to initialize:", error);
@@ -1411,30 +1418,36 @@ var CrazyGamesService = class {
1411
1418
  }
1412
1419
  }
1413
1420
  /**
1414
- * Reads a value from the CrazyGames data store. Returns null when the SDK
1415
- * is unavailable or the key is absent.
1421
+ * Returns the SDK v3 data module, throwing an actionable error when the SDK
1422
+ * is not initialized or the module is disabled for this game submission.
1416
1423
  */
1417
- async dataGetItem(key) {
1424
+ getDataModule() {
1418
1425
  const sdk = window.CrazyGames?.SDK;
1419
- if (!this.initialized || !sdk) return null;
1420
- const value = await sdk.data.getItem(key);
1426
+ if (!this.initialized || !sdk) {
1427
+ throw new Error("CrazyGames SDK not initialized");
1428
+ }
1429
+ if (!sdk.data) {
1430
+ throw new Error(
1431
+ 'CrazyGames data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings'
1432
+ );
1433
+ }
1434
+ return sdk.data;
1435
+ }
1436
+ /**
1437
+ * Reads a value from the CrazyGames data store. Returns null when the key
1438
+ * is absent.
1439
+ */
1440
+ async dataGetItem(key) {
1441
+ const value = await this.getDataModule().getItem(key);
1421
1442
  return value ?? null;
1422
1443
  }
1423
1444
  /** Writes a value to the CrazyGames data store. */
1424
1445
  async dataSetItem(key, value) {
1425
- const sdk = window.CrazyGames?.SDK;
1426
- if (!this.initialized || !sdk) {
1427
- throw new Error("CrazyGames SDK not initialized");
1428
- }
1429
- await sdk.data.setItem(key, value);
1446
+ await this.getDataModule().setItem(key, value);
1430
1447
  }
1431
1448
  /** Removes a value from the CrazyGames data store. */
1432
1449
  async dataRemoveItem(key) {
1433
- const sdk = window.CrazyGames?.SDK;
1434
- if (!this.initialized || !sdk) {
1435
- throw new Error("CrazyGames SDK not initialized");
1436
- }
1437
- await sdk.data.removeItem(key);
1450
+ await this.getDataModule().removeItem(key);
1438
1451
  }
1439
1452
  loadScript() {
1440
1453
  return new Promise((resolve, reject) => {
@@ -2842,28 +2855,34 @@ var HyveClient = class {
2842
2855
  logger.info("Client reset with new sessionId:", this.sessionId);
2843
2856
  }
2844
2857
  /**
2845
- * Get the storage adapter based on mode.
2858
+ * Get the storage adapter based on platform and mode.
2846
2859
  *
2847
2860
  * Selection order:
2848
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
2849
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
2850
- * (D3 public storageMode type is preserved; this is chosen internally).
2861
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
2862
+ * 'cloud'/'local' are durability intents and the CrazyGames store
2863
+ * satisfies both (account-synced when logged in, device-local for
2864
+ * guests). This keeps games portable: per-call modes written for
2865
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
2866
+ * work anyway (no hyve JWT exists there).
2867
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
2851
2868
  * 3. Otherwise the configured storageMode is used.
2852
2869
  *
2853
2870
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
2854
2871
  * @param mode Storage mode override (cloud or local)
2855
2872
  */
2856
2873
  async getStorageAdapter(mode) {
2857
- if (mode) {
2858
- return mode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2859
- }
2860
2874
  if (this.crazyGamesService && this.crazyGamesStorageAdapter) {
2861
2875
  if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
2862
- if (this.crazyGamesService.getEnvironment() === "crazygames") {
2876
+ const env = this.crazyGamesService.getEnvironment();
2877
+ if (env === "crazygames" || env === "local") {
2878
+ if (mode) {
2879
+ logger.debug(`Storage mode '${mode}' ignored on CrazyGames \u2014 using the CrazyGames data store`);
2880
+ }
2863
2881
  return this.crazyGamesStorageAdapter;
2864
2882
  }
2865
2883
  }
2866
- return this.storageMode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2884
+ const selected = mode ?? this.storageMode;
2885
+ return selected === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2867
2886
  }
2868
2887
  /**
2869
2888
  * Returns the current game ID or throws if not available.
package/dist/react.d.mts CHANGED
@@ -465,12 +465,16 @@ declare class HyveClient {
465
465
  */
466
466
  reset(): void;
467
467
  /**
468
- * Get the storage adapter based on mode.
468
+ * Get the storage adapter based on platform and mode.
469
469
  *
470
470
  * Selection order:
471
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
472
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
473
- * (D3 public storageMode type is preserved; this is chosen internally).
471
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
472
+ * 'cloud'/'local' are durability intents and the CrazyGames store
473
+ * satisfies both (account-synced when logged in, device-local for
474
+ * guests). This keeps games portable: per-call modes written for
475
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
476
+ * work anyway (no hyve JWT exists there).
477
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
474
478
  * 3. Otherwise the configured storageMode is used.
475
479
  *
476
480
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
package/dist/react.d.ts CHANGED
@@ -465,12 +465,16 @@ declare class HyveClient {
465
465
  */
466
466
  reset(): void;
467
467
  /**
468
- * Get the storage adapter based on mode.
468
+ * Get the storage adapter based on platform and mode.
469
469
  *
470
470
  * Selection order:
471
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
472
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
473
- * (D3 public storageMode type is preserved; this is chosen internally).
471
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
472
+ * 'cloud'/'local' are durability intents and the CrazyGames store
473
+ * satisfies both (account-synced when logged in, device-local for
474
+ * guests). This keeps games portable: per-call modes written for
475
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
476
+ * work anyway (no hyve JWT exists there).
477
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
474
478
  * 3. Otherwise the configured storageMode is used.
475
479
  *
476
480
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
package/dist/react.js CHANGED
@@ -1196,7 +1196,7 @@ var PlaygamaService = class {
1196
1196
  };
1197
1197
 
1198
1198
  // src/services/crazygames.ts
1199
- var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v2.js";
1199
+ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1200
1200
  var CrazyGamesService = class {
1201
1201
  initialized = false;
1202
1202
  environment = null;
@@ -1224,7 +1224,8 @@ var CrazyGamesService = class {
1224
1224
  }
1225
1225
  }
1226
1226
  /**
1227
- * Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
1227
+ * Loads the CrazyGames SDK v3 from CDN, runs the mandatory `SDK.init()`, and
1228
+ * confirms the environment is 'crazygames' (or 'local' for dev).
1228
1229
  * Safe to call multiple times — resolves immediately if already initialized.
1229
1230
  */
1230
1231
  async initialize() {
@@ -1236,13 +1237,19 @@ var CrazyGamesService = class {
1236
1237
  logger.warn("[CrazyGamesService] SDK not found after script load");
1237
1238
  return false;
1238
1239
  }
1239
- const env = await sdk.getEnvironment();
1240
+ await sdk.init();
1241
+ const env = sdk.environment;
1240
1242
  if (env !== "crazygames" && env !== "local") {
1241
1243
  logger.warn("[CrazyGamesService] Unexpected environment:", env);
1242
1244
  return false;
1243
1245
  }
1244
1246
  this.environment = env;
1245
1247
  this.initialized = true;
1248
+ if (!sdk.data) {
1249
+ logger.warn(
1250
+ '[CrazyGamesService] data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings to use persistent storage'
1251
+ );
1252
+ }
1246
1253
  return true;
1247
1254
  } catch (error) {
1248
1255
  logger.warn("[CrazyGamesService] Failed to initialize:", error);
@@ -1409,30 +1416,36 @@ var CrazyGamesService = class {
1409
1416
  }
1410
1417
  }
1411
1418
  /**
1412
- * Reads a value from the CrazyGames data store. Returns null when the SDK
1413
- * is unavailable or the key is absent.
1419
+ * Returns the SDK v3 data module, throwing an actionable error when the SDK
1420
+ * is not initialized or the module is disabled for this game submission.
1414
1421
  */
1415
- async dataGetItem(key) {
1422
+ getDataModule() {
1416
1423
  const sdk = window.CrazyGames?.SDK;
1417
- if (!this.initialized || !sdk) return null;
1418
- const value = await sdk.data.getItem(key);
1424
+ if (!this.initialized || !sdk) {
1425
+ throw new Error("CrazyGames SDK not initialized");
1426
+ }
1427
+ if (!sdk.data) {
1428
+ throw new Error(
1429
+ 'CrazyGames data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings'
1430
+ );
1431
+ }
1432
+ return sdk.data;
1433
+ }
1434
+ /**
1435
+ * Reads a value from the CrazyGames data store. Returns null when the key
1436
+ * is absent.
1437
+ */
1438
+ async dataGetItem(key) {
1439
+ const value = await this.getDataModule().getItem(key);
1419
1440
  return value ?? null;
1420
1441
  }
1421
1442
  /** Writes a value to the CrazyGames data store. */
1422
1443
  async dataSetItem(key, value) {
1423
- const sdk = window.CrazyGames?.SDK;
1424
- if (!this.initialized || !sdk) {
1425
- throw new Error("CrazyGames SDK not initialized");
1426
- }
1427
- await sdk.data.setItem(key, value);
1444
+ await this.getDataModule().setItem(key, value);
1428
1445
  }
1429
1446
  /** Removes a value from the CrazyGames data store. */
1430
1447
  async dataRemoveItem(key) {
1431
- const sdk = window.CrazyGames?.SDK;
1432
- if (!this.initialized || !sdk) {
1433
- throw new Error("CrazyGames SDK not initialized");
1434
- }
1435
- await sdk.data.removeItem(key);
1448
+ await this.getDataModule().removeItem(key);
1436
1449
  }
1437
1450
  loadScript() {
1438
1451
  return new Promise((resolve, reject) => {
@@ -2834,28 +2847,34 @@ var HyveClient = class {
2834
2847
  logger.info("Client reset with new sessionId:", this.sessionId);
2835
2848
  }
2836
2849
  /**
2837
- * Get the storage adapter based on mode.
2850
+ * Get the storage adapter based on platform and mode.
2838
2851
  *
2839
2852
  * Selection order:
2840
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
2841
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
2842
- * (D3 public storageMode type is preserved; this is chosen internally).
2853
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
2854
+ * 'cloud'/'local' are durability intents and the CrazyGames store
2855
+ * satisfies both (account-synced when logged in, device-local for
2856
+ * guests). This keeps games portable: per-call modes written for
2857
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
2858
+ * work anyway (no hyve JWT exists there).
2859
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
2843
2860
  * 3. Otherwise the configured storageMode is used.
2844
2861
  *
2845
2862
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
2846
2863
  * @param mode Storage mode override (cloud or local)
2847
2864
  */
2848
2865
  async getStorageAdapter(mode) {
2849
- if (mode) {
2850
- return mode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2851
- }
2852
2866
  if (this.crazyGamesService && this.crazyGamesStorageAdapter) {
2853
2867
  if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
2854
- if (this.crazyGamesService.getEnvironment() === "crazygames") {
2868
+ const env = this.crazyGamesService.getEnvironment();
2869
+ if (env === "crazygames" || env === "local") {
2870
+ if (mode) {
2871
+ logger.debug(`Storage mode '${mode}' ignored on CrazyGames \u2014 using the CrazyGames data store`);
2872
+ }
2855
2873
  return this.crazyGamesStorageAdapter;
2856
2874
  }
2857
2875
  }
2858
- return this.storageMode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2876
+ const selected = mode ?? this.storageMode;
2877
+ return selected === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2859
2878
  }
2860
2879
  /**
2861
2880
  * Returns the current game ID or throws if not available.
package/dist/react.mjs CHANGED
@@ -1174,7 +1174,7 @@ var PlaygamaService = class {
1174
1174
  };
1175
1175
 
1176
1176
  // src/services/crazygames.ts
1177
- var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v2.js";
1177
+ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1178
1178
  var CrazyGamesService = class {
1179
1179
  initialized = false;
1180
1180
  environment = null;
@@ -1202,7 +1202,8 @@ var CrazyGamesService = class {
1202
1202
  }
1203
1203
  }
1204
1204
  /**
1205
- * Loads the CrazyGames SDK from CDN and confirms the environment is 'crazygames'.
1205
+ * Loads the CrazyGames SDK v3 from CDN, runs the mandatory `SDK.init()`, and
1206
+ * confirms the environment is 'crazygames' (or 'local' for dev).
1206
1207
  * Safe to call multiple times — resolves immediately if already initialized.
1207
1208
  */
1208
1209
  async initialize() {
@@ -1214,13 +1215,19 @@ var CrazyGamesService = class {
1214
1215
  logger.warn("[CrazyGamesService] SDK not found after script load");
1215
1216
  return false;
1216
1217
  }
1217
- const env = await sdk.getEnvironment();
1218
+ await sdk.init();
1219
+ const env = sdk.environment;
1218
1220
  if (env !== "crazygames" && env !== "local") {
1219
1221
  logger.warn("[CrazyGamesService] Unexpected environment:", env);
1220
1222
  return false;
1221
1223
  }
1222
1224
  this.environment = env;
1223
1225
  this.initialized = true;
1226
+ if (!sdk.data) {
1227
+ logger.warn(
1228
+ '[CrazyGamesService] data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings to use persistent storage'
1229
+ );
1230
+ }
1224
1231
  return true;
1225
1232
  } catch (error) {
1226
1233
  logger.warn("[CrazyGamesService] Failed to initialize:", error);
@@ -1387,30 +1394,36 @@ var CrazyGamesService = class {
1387
1394
  }
1388
1395
  }
1389
1396
  /**
1390
- * Reads a value from the CrazyGames data store. Returns null when the SDK
1391
- * is unavailable or the key is absent.
1397
+ * Returns the SDK v3 data module, throwing an actionable error when the SDK
1398
+ * is not initialized or the module is disabled for this game submission.
1392
1399
  */
1393
- async dataGetItem(key) {
1400
+ getDataModule() {
1394
1401
  const sdk = window.CrazyGames?.SDK;
1395
- if (!this.initialized || !sdk) return null;
1396
- const value = await sdk.data.getItem(key);
1402
+ if (!this.initialized || !sdk) {
1403
+ throw new Error("CrazyGames SDK not initialized");
1404
+ }
1405
+ if (!sdk.data) {
1406
+ throw new Error(
1407
+ 'CrazyGames data module unavailable \u2014 enable the "Progress Save" toggle in the CrazyGames game submission settings'
1408
+ );
1409
+ }
1410
+ return sdk.data;
1411
+ }
1412
+ /**
1413
+ * Reads a value from the CrazyGames data store. Returns null when the key
1414
+ * is absent.
1415
+ */
1416
+ async dataGetItem(key) {
1417
+ const value = await this.getDataModule().getItem(key);
1397
1418
  return value ?? null;
1398
1419
  }
1399
1420
  /** Writes a value to the CrazyGames data store. */
1400
1421
  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);
1422
+ await this.getDataModule().setItem(key, value);
1406
1423
  }
1407
1424
  /** Removes a value from the CrazyGames data store. */
1408
1425
  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);
1426
+ await this.getDataModule().removeItem(key);
1414
1427
  }
1415
1428
  loadScript() {
1416
1429
  return new Promise((resolve, reject) => {
@@ -2812,28 +2825,34 @@ var HyveClient = class {
2812
2825
  logger.info("Client reset with new sessionId:", this.sessionId);
2813
2826
  }
2814
2827
  /**
2815
- * Get the storage adapter based on mode.
2828
+ * Get the storage adapter based on platform and mode.
2816
2829
  *
2817
2830
  * Selection order:
2818
- * 1. An explicit `mode` override ('cloud' | 'local') always wins.
2819
- * 2. On the CrazyGames platform, the CrazyGames data store is auto-selected
2820
- * (D3 public storageMode type is preserved; this is chosen internally).
2831
+ * 1. On the CrazyGames platform, the CrazyGames data store is ALWAYS used —
2832
+ * 'cloud'/'local' are durability intents and the CrazyGames store
2833
+ * satisfies both (account-synced when logged in, device-local for
2834
+ * guests). This keeps games portable: per-call modes written for
2835
+ * hyve.gg need no changes on CrazyGames, where the cloud path cannot
2836
+ * work anyway (no hyve JWT exists there).
2837
+ * 2. Otherwise an explicit `mode` override ('cloud' | 'local') wins.
2821
2838
  * 3. Otherwise the configured storageMode is used.
2822
2839
  *
2823
2840
  * Awaits CrazyGames SDK initialization so the data store is ready before use.
2824
2841
  * @param mode Storage mode override (cloud or local)
2825
2842
  */
2826
2843
  async getStorageAdapter(mode) {
2827
- if (mode) {
2828
- return mode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2829
- }
2830
2844
  if (this.crazyGamesService && this.crazyGamesStorageAdapter) {
2831
2845
  if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
2832
- if (this.crazyGamesService.getEnvironment() === "crazygames") {
2846
+ const env = this.crazyGamesService.getEnvironment();
2847
+ if (env === "crazygames" || env === "local") {
2848
+ if (mode) {
2849
+ logger.debug(`Storage mode '${mode}' ignored on CrazyGames \u2014 using the CrazyGames data store`);
2850
+ }
2833
2851
  return this.crazyGamesStorageAdapter;
2834
2852
  }
2835
2853
  }
2836
- return this.storageMode === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2854
+ const selected = mode ?? this.storageMode;
2855
+ return selected === "local" ? this.localStorageAdapter : this.cloudStorageAdapter;
2837
2856
  }
2838
2857
  /**
2839
2858
  * Returns the current game ID or throws if not available.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyve-sdk/js",
3
- "version": "2.14.0-canary.1",
3
+ "version": "2.14.0-canary.3",
4
4
  "description": "Hyve SDK - TypeScript wrapper for Hyve game server integration",
5
5
  "private": false,
6
6
  "publishConfig": {