@hyve-sdk/js 2.11.2 → 2.13.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.mjs CHANGED
@@ -158,17 +158,26 @@ var NativeMessageType = /* @__PURE__ */ ((NativeMessageType2) => {
158
158
  NativeMessageType2["REQUEST_NOTIFICATION_PERMISSION"] = "REQUEST_NOTIFICATION_PERMISSION";
159
159
  NativeMessageType2["GET_PRODUCTS"] = "GET_PRODUCTS";
160
160
  NativeMessageType2["PURCHASE"] = "PURCHASE";
161
+ NativeMessageType2["SHOW_REWARDED_AD"] = "SHOW_REWARDED_AD";
162
+ NativeMessageType2["SHOW_INTERSTITIAL_AD"] = "SHOW_INTERSTITIAL_AD";
161
163
  NativeMessageType2["IAP_AVAILABILITY_RESULT"] = "IAP_AVAILABILITY_RESULT";
162
164
  NativeMessageType2["PUSH_PERMISSION_GRANTED"] = "PUSH_PERMISSION_GRANTED";
163
165
  NativeMessageType2["PUSH_PERMISSION_DENIED"] = "PUSH_PERMISSION_DENIED";
164
166
  NativeMessageType2["PRODUCTS_RESULT"] = "PRODUCTS_RESULT";
165
167
  NativeMessageType2["PURCHASE_COMPLETE"] = "PURCHASE_COMPLETE";
166
168
  NativeMessageType2["PURCHASE_ERROR"] = "PURCHASE_ERROR";
169
+ NativeMessageType2["AD_RESULT"] = "AD_RESULT";
167
170
  return NativeMessageType2;
168
171
  })(NativeMessageType || {});
169
172
  var NativeBridge = class {
170
173
  static handlers = /* @__PURE__ */ new Map();
171
174
  static isInitialized = false;
175
+ /**
176
+ * Pending `sendAdRequest()` promises, keyed by the ad type native echoes
177
+ * back in `AD_RESULT.type` ("rewarded" | "interstitial"). One in-flight
178
+ * request per type — ads are shown sequentially.
179
+ */
180
+ static adResultResolvers = /* @__PURE__ */ new Map();
172
181
  /**
173
182
  * Checks if the app is running inside a React Native WebView
174
183
  */
@@ -221,16 +230,20 @@ var NativeBridge = class {
221
230
  "PUSH_PERMISSION_DENIED" /* PUSH_PERMISSION_DENIED */,
222
231
  "PRODUCTS_RESULT" /* PRODUCTS_RESULT */,
223
232
  "PURCHASE_COMPLETE" /* PURCHASE_COMPLETE */,
224
- "PURCHASE_ERROR" /* PURCHASE_ERROR */
233
+ "PURCHASE_ERROR" /* PURCHASE_ERROR */,
234
+ "AD_RESULT" /* AD_RESULT */
225
235
  ];
226
236
  if (!nativeResponseTypes.includes(data.type)) {
227
237
  return;
228
238
  }
239
+ if (data.type === "AD_RESULT" /* AD_RESULT */) {
240
+ this.resolveAdResult(data.payload);
241
+ }
229
242
  const handler = this.handlers.get(data.type);
230
243
  if (handler) {
231
244
  logger.debug(`[NativeBridge] Handling message: ${data.type}`);
232
245
  handler(data.payload);
233
- } else {
246
+ } else if (data.type !== "AD_RESULT" /* AD_RESULT */) {
234
247
  logger.warn(`[NativeBridge] No handler registered for: ${data.type}`);
235
248
  }
236
249
  } catch (error) {
@@ -356,6 +369,81 @@ var NativeBridge = class {
356
369
  static purchase(productId, userId) {
357
370
  this.send("PURCHASE" /* PURCHASE */, { productId, userId });
358
371
  }
372
+ /**
373
+ * Sends a native ad request and resolves with the `AD_RESULT` native sends
374
+ * back. Never rejects — a missing native context, timeout, or superseded
375
+ * request resolves to `{ success: false }` with an error code so callers can
376
+ * decide whether to fall back to the web ad path.
377
+ *
378
+ * @param messageType - `SHOW_REWARDED_AD` or `SHOW_INTERSTITIAL_AD`
379
+ * @param payload - `{ gameId, format, placement? }`
380
+ * @param timeoutMs - How long to wait for `AD_RESULT` before giving up
381
+ *
382
+ * @example
383
+ * const result = await NativeBridge.sendAdRequest(
384
+ * NativeMessageType.SHOW_REWARDED_AD,
385
+ * { gameId: "123", format: "rewarded", placement: "level_end" },
386
+ * );
387
+ * if (result.success) grantReward();
388
+ */
389
+ static sendAdRequest(messageType, payload, timeoutMs = 12e4) {
390
+ const expectedType = messageType === "SHOW_REWARDED_AD" /* SHOW_REWARDED_AD */ ? "rewarded" : "interstitial";
391
+ return new Promise((resolve) => {
392
+ if (!this.isNativeContext()) {
393
+ resolve({ type: expectedType, success: false, error: "no_native_context" });
394
+ return;
395
+ }
396
+ this.initialize();
397
+ const existing = this.adResultResolvers.get(expectedType);
398
+ if (existing) {
399
+ logger.warn(`[NativeBridge] Superseding in-flight ${expectedType} ad request`);
400
+ existing({ type: expectedType, success: false, error: "superseded" });
401
+ }
402
+ let settled = false;
403
+ const timer = setTimeout(() => {
404
+ if (settled) return;
405
+ settled = true;
406
+ this.adResultResolvers.delete(expectedType);
407
+ logger.warn(`[NativeBridge] Ad request timed out: ${expectedType}`);
408
+ resolve({ type: expectedType, success: false, error: "timeout" });
409
+ }, timeoutMs);
410
+ this.adResultResolvers.set(expectedType, (result) => {
411
+ if (settled) return;
412
+ settled = true;
413
+ clearTimeout(timer);
414
+ this.adResultResolvers.delete(expectedType);
415
+ resolve(result);
416
+ });
417
+ this.send(messageType, payload);
418
+ });
419
+ }
420
+ /**
421
+ * Registers a handler invoked for every `AD_RESULT` message from native,
422
+ * in addition to resolving any pending {@link sendAdRequest} promise.
423
+ * Useful for diagnostics or availability tracking.
424
+ *
425
+ * @param handler - Called with the raw `AD_RESULT` payload
426
+ */
427
+ static onAdResult(handler) {
428
+ this.on("AD_RESULT" /* AD_RESULT */, handler);
429
+ }
430
+ /**
431
+ * Resolves the pending {@link sendAdRequest} promise that matches an
432
+ * incoming `AD_RESULT` payload. No-op when nothing is waiting (e.g. a late
433
+ * result after a timeout).
434
+ */
435
+ static resolveAdResult(payload) {
436
+ if (!payload?.type) {
437
+ logger.warn("[NativeBridge] AD_RESULT received without a type");
438
+ return;
439
+ }
440
+ const resolver = this.adResultResolvers.get(payload.type);
441
+ if (resolver) {
442
+ resolver(payload);
443
+ } else {
444
+ logger.debug(`[NativeBridge] No pending ad request for: ${payload.type}`);
445
+ }
446
+ }
359
447
  };
360
448
 
361
449
  // src/utils/jwt.ts
@@ -712,10 +800,12 @@ function getAttributionData() {
712
800
  }
713
801
 
714
802
  // src/services/ads.ts
803
+ var FALLBACK_ERROR_CODES = ["not_configured", "config_fetch_failed"];
715
804
  var AdsService = class {
716
805
  config = {
717
806
  sound: "on",
718
807
  debug: false,
808
+ useNativeAds: false,
719
809
  onBeforeAd: () => {
720
810
  },
721
811
  onAfterAd: () => {
@@ -726,6 +816,16 @@ var AdsService = class {
726
816
  // Cached init promise — ensures adConfig() is only called once
727
817
  initPromise = null;
728
818
  ready = false;
819
+ // Game this SDK instance serves — sent to native so it can resolve unit IDs.
820
+ // The SDK never stores or resolves unit IDs itself (Decision 7).
821
+ gameId = null;
822
+ /**
823
+ * Set the game ID used in native ad requests. Called by the client once the
824
+ * game ID is known (from URL params or JWT). No-op for the web ad path.
825
+ */
826
+ setGameId(gameId) {
827
+ this.gameId = gameId;
828
+ }
729
829
  /**
730
830
  * Optionally configure the ads service.
731
831
  * Not required — ads work without calling this.
@@ -739,7 +839,10 @@ var AdsService = class {
739
839
  onRewardEarned: config.onRewardEarned ?? this.config.onRewardEarned
740
840
  };
741
841
  if (this.config.debug) {
742
- logger.debug("[AdsService] Configuration updated:", { sound: this.config.sound });
842
+ logger.debug("[AdsService] Configuration updated:", {
843
+ sound: this.config.sound,
844
+ useNativeAds: this.config.useNativeAds
845
+ });
743
846
  }
744
847
  }
745
848
  /**
@@ -776,10 +879,93 @@ var AdsService = class {
776
879
  }
777
880
  /**
778
881
  * Show an ad. Auto-initializes on the first call.
779
- * Returns immediately with success: false if ads are disabled or unavailable.
882
+ *
883
+ * Inside the Hyve mobile shell (`window.ReactNativeWebView` present) with
884
+ * `useNativeAds` enabled, the request is routed to native AdMob via the
885
+ * bridge. Otherwise — or when native reports the slot is `not_configured` /
886
+ * `config_fetch_failed` — it falls back to the Google H5 web path.
887
+ *
888
+ * Returns `success: false` if ads are disabled or unavailable.
889
+ *
890
+ * @param type - Ad type to show
891
+ * @param placement - Optional placement key, forwarded to native; ignored on
892
+ * the web path (H5 has no placement concept).
780
893
  */
781
- async show(type) {
894
+ async show(type, placement) {
782
895
  const requestedAt = Date.now();
896
+ if (this.isNativeAdsContext()) {
897
+ const nativeResult = await this.showNative(type, placement, requestedAt);
898
+ if (nativeResult) return nativeResult;
899
+ }
900
+ return this.showWeb(type, requestedAt, true);
901
+ }
902
+ /**
903
+ * True when running inside the Hyve mobile shell with native ads enabled.
904
+ */
905
+ isNativeAdsContext() {
906
+ return NativeBridge.isNativeContext() && this.config.useNativeAds === true;
907
+ }
908
+ /**
909
+ * Route an ad request to native AdMob via the bridge (Decision 7 — native
910
+ * owns unit-ID resolution; the SDK only sends `{ gameId, format, placement }`).
911
+ *
912
+ * Returns the resolved `AdResult`, or `null` to indicate the caller should
913
+ * fall back to the web ad path for this single call.
914
+ */
915
+ async showNative(type, placement, requestedAt) {
916
+ if (!this.gameId) {
917
+ if (this.config.debug) {
918
+ logger.debug("[AdsService] Native ads enabled but no gameId set \u2014 using web path");
919
+ }
920
+ return null;
921
+ }
922
+ const format = type === "rewarded" ? "rewarded" : "interstitial";
923
+ const messageType = type === "rewarded" ? "SHOW_REWARDED_AD" /* SHOW_REWARDED_AD */ : "SHOW_INTERSTITIAL_AD" /* SHOW_INTERSTITIAL_AD */;
924
+ if (this.config.debug) {
925
+ logger.debug(`[AdsService] Requesting native ${type} ad (format: ${format})`, {
926
+ gameId: this.gameId,
927
+ placement
928
+ });
929
+ }
930
+ this.config.onBeforeAd(type);
931
+ const result = await NativeBridge.sendAdRequest(messageType, {
932
+ gameId: this.gameId,
933
+ format,
934
+ placement
935
+ });
936
+ const completedAt = Date.now();
937
+ if (result.error && FALLBACK_ERROR_CODES.includes(result.error)) {
938
+ if (this.config.debug) {
939
+ logger.debug(`[AdsService] Native ad ${result.error} \u2014 falling back to web path`);
940
+ }
941
+ return this.showWeb(type, requestedAt, false);
942
+ }
943
+ this.config.onAfterAd(type);
944
+ if (type === "rewarded" && result.success) {
945
+ this.config.onRewardEarned();
946
+ }
947
+ if (this.config.debug) {
948
+ logger.debug("[AdsService] Native ad result:", {
949
+ type,
950
+ success: result.success,
951
+ error: result.error
952
+ });
953
+ }
954
+ return {
955
+ success: result.success,
956
+ type,
957
+ error: result.error ? new Error(result.error) : void 0,
958
+ requestedAt,
959
+ completedAt
960
+ };
961
+ }
962
+ /**
963
+ * Show an ad via the Google H5 web path. Auto-initializes on first call.
964
+ *
965
+ * @param fireBeforeAd - When false, `onBeforeAd` is not fired (the native
966
+ * fallback path already fired it before delegating here).
967
+ */
968
+ async showWeb(type, requestedAt, fireBeforeAd) {
783
969
  const ready = await this.initialize();
784
970
  if (!ready || !window.adBreak) {
785
971
  return {
@@ -790,12 +976,15 @@ var AdsService = class {
790
976
  completedAt: Date.now()
791
977
  };
792
978
  }
793
- return this.showAdBreak(type);
979
+ return this.showAdBreak(type, fireBeforeAd);
794
980
  }
795
981
  /**
796
982
  * Show an ad break via the Google H5 API.
983
+ *
984
+ * @param fireBeforeAd - When false, skips `onBeforeAd` (already fired by the
985
+ * native fallback path that delegated here).
797
986
  */
798
- async showAdBreak(type) {
987
+ async showAdBreak(type, fireBeforeAd = true) {
799
988
  const requestedAt = Date.now();
800
989
  return new Promise((resolve) => {
801
990
  const googleType = type === "rewarded" ? "reward" : type === "preroll" ? "start" : "next";
@@ -803,7 +992,9 @@ var AdsService = class {
803
992
  if (this.config.debug) {
804
993
  logger.debug(`[AdsService] Showing ${type} ad`);
805
994
  }
806
- this.config.onBeforeAd(type);
995
+ if (fireBeforeAd) {
996
+ this.config.onBeforeAd(type);
997
+ }
807
998
  const adBreakConfig = {
808
999
  type: googleType,
809
1000
  name: adName,
@@ -1977,6 +2168,7 @@ var HyveClient = class {
1977
2168
  this.gameId = params.gameId;
1978
2169
  logger.info("Game ID extracted from game-id parameter:", this.gameId);
1979
2170
  }
2171
+ this.adsService.setGameId(this.gameId);
1980
2172
  if (this.jwtToken) {
1981
2173
  logger.info("Authentication successful via JWT");
1982
2174
  } else {
@@ -2082,6 +2274,93 @@ var HyveClient = class {
2082
2274
  return false;
2083
2275
  }
2084
2276
  }
2277
+ /**
2278
+ * Required lifecycle telemetry — Session start.
2279
+ * See https://docs.hyve.gg/docs/telemetry#required-lifecycle-events
2280
+ */
2281
+ async sessionStart() {
2282
+ return this.sendTelemetry("game", "session", "start");
2283
+ }
2284
+ /**
2285
+ * Required lifecycle telemetry — Session end.
2286
+ */
2287
+ async sessionEnd() {
2288
+ return this.sendTelemetry("game", "session", "end");
2289
+ }
2290
+ /**
2291
+ * Required lifecycle telemetry — Lobby loading start.
2292
+ */
2293
+ async lobbyLoadingStart() {
2294
+ return this.sendTelemetry("game", "loading", "start");
2295
+ }
2296
+ /**
2297
+ * Required lifecycle telemetry — Lobby loading end.
2298
+ */
2299
+ async lobbyLoadingEnd() {
2300
+ return this.sendTelemetry("game", "loading", "end");
2301
+ }
2302
+ /**
2303
+ * Required lifecycle telemetry — Game loading start.
2304
+ */
2305
+ async gameLoadingStart() {
2306
+ return this.sendTelemetry("game", "game_loading", "start");
2307
+ }
2308
+ /**
2309
+ * Required lifecycle telemetry — Game loading end.
2310
+ */
2311
+ async gameLoadingEnd() {
2312
+ return this.sendTelemetry("game", "game_loading", "end");
2313
+ }
2314
+ /**
2315
+ * Required lifecycle telemetry — Lobby initialization.
2316
+ */
2317
+ async lobbyInit() {
2318
+ return this.sendTelemetry("game", "lobby", "init");
2319
+ }
2320
+ /**
2321
+ * Required lifecycle telemetry — Gameplay end.
2322
+ * Also notifies CrazyGames that gameplay has stopped.
2323
+ */
2324
+ async gameplayEnd() {
2325
+ if (this.crazyGamesService) {
2326
+ if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
2327
+ this.crazyGamesService.gameplayStop();
2328
+ }
2329
+ return this.sendTelemetry("game", "gameplay", "end");
2330
+ }
2331
+ /**
2332
+ * Required lifecycle telemetry — Store opened.
2333
+ */
2334
+ async storeOpen() {
2335
+ return this.sendTelemetry("game", "store", "start");
2336
+ }
2337
+ /**
2338
+ * Required lifecycle telemetry — Purchase complete.
2339
+ * @param itemName Name of the purchased item (required by the platform)
2340
+ */
2341
+ async purchaseComplete(itemName) {
2342
+ return this.sendTelemetry("game", "purchase", "complete", null, null, {
2343
+ item_name: itemName
2344
+ });
2345
+ }
2346
+ /**
2347
+ * Required lifecycle telemetry — Purchase failed.
2348
+ * @param itemName Name of the item the purchase was for
2349
+ */
2350
+ async purchaseFail(itemName) {
2351
+ return this.sendTelemetry("game", "purchase", "fail", null, null, {
2352
+ item_name: itemName
2353
+ });
2354
+ }
2355
+ /**
2356
+ * Required lifecycle telemetry — Purchase cancelled.
2357
+ * @param itemName Name of the item the purchase was for
2358
+ */
2359
+ async purchaseCancel(itemName) {
2360
+ return this.sendTelemetry("game", "purchase", "cancel", null, null, {
2361
+ item_name: itemName
2362
+ });
2363
+ }
2085
2364
  /**
2086
2365
  * Makes an authenticated API call using the JWT token
2087
2366
  * @param endpoint API endpoint path (will be appended to base URL)
@@ -2203,6 +2482,7 @@ var HyveClient = class {
2203
2482
  this.userId = null;
2204
2483
  this.jwtToken = null;
2205
2484
  this.gameId = null;
2485
+ this.adsService.setGameId(null);
2206
2486
  logger.info("User logged out");
2207
2487
  }
2208
2488
  /**
@@ -2377,9 +2657,11 @@ var HyveClient = class {
2377
2657
  /**
2378
2658
  * Show an ad
2379
2659
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
2660
+ * @param placement Optional placement key, forwarded to the native AdMob path
2661
+ * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
2380
2662
  * @returns Promise resolving to ad result
2381
2663
  */
2382
- async showAd(type) {
2664
+ async showAd(type, placement) {
2383
2665
  if (this.crazyGamesService) {
2384
2666
  if (this.crazyGamesInitPromise) {
2385
2667
  await this.crazyGamesInitPromise;
@@ -2418,17 +2700,18 @@ var HyveClient = class {
2418
2700
  });
2419
2701
  }
2420
2702
  }
2421
- return this.adsService.show(type);
2703
+ return this.adsService.show(type, placement);
2422
2704
  }
2423
2705
  /**
2424
- * Notifies CrazyGames that gameplay has started.
2425
- * No-op on other platforms.
2706
+ * Required lifecycle telemetry Gameplay start.
2707
+ * Also notifies CrazyGames that gameplay has started.
2426
2708
  */
2427
2709
  async gameplayStart() {
2428
2710
  if (this.crazyGamesService) {
2429
2711
  if (this.crazyGamesInitPromise) await this.crazyGamesInitPromise;
2430
2712
  this.crazyGamesService.gameplayStart();
2431
2713
  }
2714
+ return this.sendTelemetry("game", "gameplay", "start");
2432
2715
  }
2433
2716
  /**
2434
2717
  * Notifies CrazyGames that gameplay has stopped.
package/dist/react.d.mts CHANGED
@@ -138,6 +138,10 @@ interface GetGameDataLeaderboardParams {
138
138
  *
139
139
  * @packageDocumentation
140
140
  */
141
+ /**
142
+ * Ad types the SDK's `show()` accepts. Limited to formats with a working
143
+ * native path; `'preroll'` maps to a native interstitial (Decision 2).
144
+ */
141
145
  type AdType = 'rewarded' | 'interstitial' | 'preroll';
142
146
  interface AdResult {
143
147
  success: boolean;
@@ -149,6 +153,19 @@ interface AdResult {
149
153
  interface AdConfig {
150
154
  sound?: 'on' | 'off';
151
155
  debug?: boolean;
156
+ /**
157
+ * Route ad requests through the native AdMob bridge when running inside the
158
+ * Hyve mobile shell (`window.ReactNativeWebView` present).
159
+ *
160
+ * Phase 0 rollout flag: defaults to `false`, where ads always use the
161
+ * existing H5 / Playgama web path. The native path is wired up behind this
162
+ * flag in a later phase — until then this is an inert toggle that exists so
163
+ * later releases can flip behavior without an API change. See
164
+ * `docs/admob-migration.md`.
165
+ *
166
+ * @default false
167
+ */
168
+ useNativeAds?: boolean;
152
169
  onBeforeAd?: (type: AdType) => void;
153
170
  onAfterAd?: (type: AdType) => void;
154
171
  onRewardEarned?: () => void;
@@ -282,6 +299,59 @@ declare class HyveClient {
282
299
  * @returns Promise resolving to boolean indicating success
283
300
  */
284
301
  sendTelemetry(eventLocation: string, eventCategory: string, eventAction: string, eventSubCategory?: string | null, eventSubAction?: string | null, eventDetails?: Record<string, any> | string | null, platformId?: string | null): Promise<boolean>;
302
+ /**
303
+ * Required lifecycle telemetry — Session start.
304
+ * See https://docs.hyve.gg/docs/telemetry#required-lifecycle-events
305
+ */
306
+ sessionStart(): Promise<boolean>;
307
+ /**
308
+ * Required lifecycle telemetry — Session end.
309
+ */
310
+ sessionEnd(): Promise<boolean>;
311
+ /**
312
+ * Required lifecycle telemetry — Lobby loading start.
313
+ */
314
+ lobbyLoadingStart(): Promise<boolean>;
315
+ /**
316
+ * Required lifecycle telemetry — Lobby loading end.
317
+ */
318
+ lobbyLoadingEnd(): Promise<boolean>;
319
+ /**
320
+ * Required lifecycle telemetry — Game loading start.
321
+ */
322
+ gameLoadingStart(): Promise<boolean>;
323
+ /**
324
+ * Required lifecycle telemetry — Game loading end.
325
+ */
326
+ gameLoadingEnd(): Promise<boolean>;
327
+ /**
328
+ * Required lifecycle telemetry — Lobby initialization.
329
+ */
330
+ lobbyInit(): Promise<boolean>;
331
+ /**
332
+ * Required lifecycle telemetry — Gameplay end.
333
+ * Also notifies CrazyGames that gameplay has stopped.
334
+ */
335
+ gameplayEnd(): Promise<boolean>;
336
+ /**
337
+ * Required lifecycle telemetry — Store opened.
338
+ */
339
+ storeOpen(): Promise<boolean>;
340
+ /**
341
+ * Required lifecycle telemetry — Purchase complete.
342
+ * @param itemName Name of the purchased item (required by the platform)
343
+ */
344
+ purchaseComplete(itemName: string): Promise<boolean>;
345
+ /**
346
+ * Required lifecycle telemetry — Purchase failed.
347
+ * @param itemName Name of the item the purchase was for
348
+ */
349
+ purchaseFail(itemName: string): Promise<boolean>;
350
+ /**
351
+ * Required lifecycle telemetry — Purchase cancelled.
352
+ * @param itemName Name of the item the purchase was for
353
+ */
354
+ purchaseCancel(itemName: string): Promise<boolean>;
285
355
  /**
286
356
  * Makes an authenticated API call using the JWT token
287
357
  * @param endpoint API endpoint path (will be appended to base URL)
@@ -427,14 +497,16 @@ declare class HyveClient {
427
497
  /**
428
498
  * Show an ad
429
499
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
500
+ * @param placement Optional placement key, forwarded to the native AdMob path
501
+ * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
430
502
  * @returns Promise resolving to ad result
431
503
  */
432
- showAd(type: AdType): Promise<AdResult>;
504
+ showAd(type: AdType, placement?: string): Promise<AdResult>;
433
505
  /**
434
- * Notifies CrazyGames that gameplay has started.
435
- * No-op on other platforms.
506
+ * Required lifecycle telemetry Gameplay start.
507
+ * Also notifies CrazyGames that gameplay has started.
436
508
  */
437
- gameplayStart(): Promise<void>;
509
+ gameplayStart(): Promise<boolean>;
438
510
  /**
439
511
  * Notifies CrazyGames that gameplay has stopped.
440
512
  * No-op on other platforms.
package/dist/react.d.ts CHANGED
@@ -138,6 +138,10 @@ interface GetGameDataLeaderboardParams {
138
138
  *
139
139
  * @packageDocumentation
140
140
  */
141
+ /**
142
+ * Ad types the SDK's `show()` accepts. Limited to formats with a working
143
+ * native path; `'preroll'` maps to a native interstitial (Decision 2).
144
+ */
141
145
  type AdType = 'rewarded' | 'interstitial' | 'preroll';
142
146
  interface AdResult {
143
147
  success: boolean;
@@ -149,6 +153,19 @@ interface AdResult {
149
153
  interface AdConfig {
150
154
  sound?: 'on' | 'off';
151
155
  debug?: boolean;
156
+ /**
157
+ * Route ad requests through the native AdMob bridge when running inside the
158
+ * Hyve mobile shell (`window.ReactNativeWebView` present).
159
+ *
160
+ * Phase 0 rollout flag: defaults to `false`, where ads always use the
161
+ * existing H5 / Playgama web path. The native path is wired up behind this
162
+ * flag in a later phase — until then this is an inert toggle that exists so
163
+ * later releases can flip behavior without an API change. See
164
+ * `docs/admob-migration.md`.
165
+ *
166
+ * @default false
167
+ */
168
+ useNativeAds?: boolean;
152
169
  onBeforeAd?: (type: AdType) => void;
153
170
  onAfterAd?: (type: AdType) => void;
154
171
  onRewardEarned?: () => void;
@@ -282,6 +299,59 @@ declare class HyveClient {
282
299
  * @returns Promise resolving to boolean indicating success
283
300
  */
284
301
  sendTelemetry(eventLocation: string, eventCategory: string, eventAction: string, eventSubCategory?: string | null, eventSubAction?: string | null, eventDetails?: Record<string, any> | string | null, platformId?: string | null): Promise<boolean>;
302
+ /**
303
+ * Required lifecycle telemetry — Session start.
304
+ * See https://docs.hyve.gg/docs/telemetry#required-lifecycle-events
305
+ */
306
+ sessionStart(): Promise<boolean>;
307
+ /**
308
+ * Required lifecycle telemetry — Session end.
309
+ */
310
+ sessionEnd(): Promise<boolean>;
311
+ /**
312
+ * Required lifecycle telemetry — Lobby loading start.
313
+ */
314
+ lobbyLoadingStart(): Promise<boolean>;
315
+ /**
316
+ * Required lifecycle telemetry — Lobby loading end.
317
+ */
318
+ lobbyLoadingEnd(): Promise<boolean>;
319
+ /**
320
+ * Required lifecycle telemetry — Game loading start.
321
+ */
322
+ gameLoadingStart(): Promise<boolean>;
323
+ /**
324
+ * Required lifecycle telemetry — Game loading end.
325
+ */
326
+ gameLoadingEnd(): Promise<boolean>;
327
+ /**
328
+ * Required lifecycle telemetry — Lobby initialization.
329
+ */
330
+ lobbyInit(): Promise<boolean>;
331
+ /**
332
+ * Required lifecycle telemetry — Gameplay end.
333
+ * Also notifies CrazyGames that gameplay has stopped.
334
+ */
335
+ gameplayEnd(): Promise<boolean>;
336
+ /**
337
+ * Required lifecycle telemetry — Store opened.
338
+ */
339
+ storeOpen(): Promise<boolean>;
340
+ /**
341
+ * Required lifecycle telemetry — Purchase complete.
342
+ * @param itemName Name of the purchased item (required by the platform)
343
+ */
344
+ purchaseComplete(itemName: string): Promise<boolean>;
345
+ /**
346
+ * Required lifecycle telemetry — Purchase failed.
347
+ * @param itemName Name of the item the purchase was for
348
+ */
349
+ purchaseFail(itemName: string): Promise<boolean>;
350
+ /**
351
+ * Required lifecycle telemetry — Purchase cancelled.
352
+ * @param itemName Name of the item the purchase was for
353
+ */
354
+ purchaseCancel(itemName: string): Promise<boolean>;
285
355
  /**
286
356
  * Makes an authenticated API call using the JWT token
287
357
  * @param endpoint API endpoint path (will be appended to base URL)
@@ -427,14 +497,16 @@ declare class HyveClient {
427
497
  /**
428
498
  * Show an ad
429
499
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
500
+ * @param placement Optional placement key, forwarded to the native AdMob path
501
+ * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
430
502
  * @returns Promise resolving to ad result
431
503
  */
432
- showAd(type: AdType): Promise<AdResult>;
504
+ showAd(type: AdType, placement?: string): Promise<AdResult>;
433
505
  /**
434
- * Notifies CrazyGames that gameplay has started.
435
- * No-op on other platforms.
506
+ * Required lifecycle telemetry Gameplay start.
507
+ * Also notifies CrazyGames that gameplay has started.
436
508
  */
437
- gameplayStart(): Promise<void>;
509
+ gameplayStart(): Promise<boolean>;
438
510
  /**
439
511
  * Notifies CrazyGames that gameplay has stopped.
440
512
  * No-op on other platforms.