@bountyboard/arcade-sdk 1.0.0 → 1.0.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/AGENTS.md +10 -5
- package/README.md +36 -1
- package/dist/{chunk-WCGBR3MI.js → chunk-OWOESH4X.js} +94 -11
- package/dist/chunk-OWOESH4X.js.map +1 -0
- package/dist/global-sdk.d.ts +65 -10
- package/dist/global.js +93 -10
- package/dist/index.cjs +93 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/multiplayer.cjs +93 -10
- package/dist/multiplayer.cjs.map +1 -1
- package/dist/multiplayer.d.cts +2 -2
- package/dist/multiplayer.d.ts +2 -2
- package/dist/multiplayer.js +1 -1
- package/dist/{types-B6xH-lDi.d.cts → types-BKChtV3H.d.cts} +56 -11
- package/dist/{types-B6xH-lDi.d.ts → types-BKChtV3H.d.ts} +56 -11
- package/docs/game-design-playbook.md +5 -3
- package/package.json +1 -1
- package/dist/chunk-WCGBR3MI.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -11,9 +11,12 @@ https://www.bountyboard.gg/arcade/sdk/llms.txt — this file is about using it C
|
|
|
11
11
|
fire-and-forget calls no-op, `save`/`load` reject with `code: 'unsupported'`, `getPlayer`
|
|
12
12
|
resolves `null`, `getVariant` resolves its alphabetical control, `joinRoom` rejects
|
|
13
13
|
`'unsupported'`. The game must run perfectly with all of those outcomes at once.
|
|
14
|
-
2. **Rewarded ads**:
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
2. **Rewarded ads**: prepare at the natural break with `prepareRewardedAd(...)`. Only enable
|
|
15
|
+
YOUR "Watch ad to revive" control when preparation returns `status: 'ready'`, and call the
|
|
16
|
+
returned `show()` as the first SDK action in that click/tap handler (no `await`, microtask, or
|
|
17
|
+
timer first). Grant only when its final result has `status === 'viewed'`. Pause gameplay and
|
|
18
|
+
audio in `onStart`; never loop or auto-retry. `rewardedBreak()` is legacy compatibility, not
|
|
19
|
+
the pattern for new integrations.
|
|
17
20
|
3. **Scores are integers and `gameOver` fires exactly once per run.** Don't submit
|
|
18
21
|
post-game-over corrections; the server enforces per-game plausibility caps and rejects
|
|
19
22
|
implausible values. Call `submitScore` freely during play — the host throttles.
|
|
@@ -64,8 +67,10 @@ the safe/default experience the alphabetically-first name. Don't re-randomize cl
|
|
|
64
67
|
'bb-arcade'`) with a scripted parent — reply to `init`/`ready` with a `bb-arcade-host` config
|
|
65
68
|
message, answer `save`/`load` requests by `requestId`. This is how Bounty Board's own
|
|
66
69
|
Playwright harnesses verify games; it exercises the real protocol without the real site.
|
|
67
|
-
- Ads: on localhost the SDK auto-enables Google's test-ads mode;
|
|
68
|
-
`
|
|
70
|
+
- Ads: on localhost the SDK auto-enables Google's test-ads mode; preparation returning
|
|
71
|
+
`'unavailable'` locally is normal. Mock both a ready placement and an unavailable placement;
|
|
72
|
+
assert that `show()` is called directly by your UI handler and only final status `'viewed'`
|
|
73
|
+
grants the reward.
|
|
69
74
|
- Multiplayer: run the room server locally (`wrangler dev` with `DEV_ALLOW_UNSIGNED=1`) and pass
|
|
70
75
|
`{ roomUrl, ticket }` overrides to `joinRoom` for development.
|
|
71
76
|
|
package/README.md
CHANGED
|
@@ -48,13 +48,48 @@ with standalone typings at [`/arcade-sdk.d.ts`](https://www.bountyboard.gg/arcad
|
|
|
48
48
|
| `save(blob)` / `load()` | 1MB cloud save per player (hosted builds; always catch rejections) |
|
|
49
49
|
| `getPlayer()` | `{ name, avatarUrl }` or `null` — display identity only, always handle null |
|
|
50
50
|
| `getVariant(key, variants)` | Deterministic A/B split; alphabetical first variant = control |
|
|
51
|
-
| `
|
|
51
|
+
| `prepareRewardedAd(opts)` | Prepare an ad; call ready `show()` directly from the player's click |
|
|
52
|
+
| `rewardedBreak(opts)` | Legacy one-call ad flow (kept for compatibility; avoid in new integrations) |
|
|
52
53
|
| `xrSessionStart()` / `xrSessionEnd()` | Keep WebXR headset time counting as playtime |
|
|
53
54
|
| `multiplayer.joinRoom(...)` | Authoritative multiplayer rooms — see below |
|
|
54
55
|
|
|
55
56
|
Rejections from `save()`/`load()` are real `Error`s carrying a typed machine-readable
|
|
56
57
|
`code`: `'unsupported' | 'unauthenticated' | 'too_large' | 'rejected' | 'error'`.
|
|
57
58
|
|
|
59
|
+
## Rewarded ads without losing the player's click
|
|
60
|
+
|
|
61
|
+
Google can report that an ad is ready asynchronously. Prepare at the natural break (for
|
|
62
|
+
example, when the defeat panel opens), then call the retained `show()` as the first SDK action
|
|
63
|
+
inside the player's explicit Watch Ad click. Do not `await`, queue a microtask, or schedule a
|
|
64
|
+
timer before `show()`.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const prepared = await BBArcade.prepareRewardedAd({
|
|
68
|
+
placement: 'death_revive',
|
|
69
|
+
reward: 'extra_life',
|
|
70
|
+
onStart: () => pauseGameAndAudio(),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (prepared.status === 'ready') {
|
|
74
|
+
watchAdButton.disabled = false;
|
|
75
|
+
watchAdButton.onclick = () => {
|
|
76
|
+
const result = prepared.show(); // synchronous: keep this first in the click handler
|
|
77
|
+
void result.then(final => {
|
|
78
|
+
if (final.status === 'viewed') revivePlayer();
|
|
79
|
+
else showAdUnavailable(final.error, final.breakStatus);
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
} else {
|
|
83
|
+
showAdUnavailable(prepared.error, prepared.breakStatus);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`show()` is one-shot and always returns the same final-result promise. Grant the reward only
|
|
88
|
+
when `status === 'viewed'`; a raw `breakStatus` string is diagnostic context, not proof that
|
|
89
|
+
Google fired its completion callback. `rewardedBreak()` remains available so existing games do
|
|
90
|
+
not break, but new integrations should use the prepared flow because an asynchronous
|
|
91
|
+
`beforeReward` callback cannot reliably preserve browser user activation.
|
|
92
|
+
|
|
58
93
|
## Multiplayer
|
|
59
94
|
|
|
60
95
|
```ts
|
|
@@ -40,6 +40,11 @@ function toBoolean(value) {
|
|
|
40
40
|
function cleanRewardedSize(value) {
|
|
41
41
|
return value === "small" || value === "medium" || value === "large" ? value : "";
|
|
42
42
|
}
|
|
43
|
+
function hasTransientUserActivation() {
|
|
44
|
+
if (typeof navigator === "undefined") return false;
|
|
45
|
+
const activation = navigator.userActivation;
|
|
46
|
+
return (activation == null ? void 0 : activation.isActive) === true;
|
|
47
|
+
}
|
|
43
48
|
function safeCall(fn, arg) {
|
|
44
49
|
if (typeof fn !== "function") return;
|
|
45
50
|
try {
|
|
@@ -116,7 +121,7 @@ function sanitizePlayer(value) {
|
|
|
116
121
|
function createBBArcade() {
|
|
117
122
|
let adPlacementLoad = null;
|
|
118
123
|
let adPreloadConfigured = false;
|
|
119
|
-
let rewardedBreakActive =
|
|
124
|
+
let rewardedBreakActive = null;
|
|
120
125
|
let rewardedBreakSequence = 0;
|
|
121
126
|
function isLocalAdHost() {
|
|
122
127
|
try {
|
|
@@ -527,6 +532,16 @@ function createBBArcade() {
|
|
|
527
532
|
function show() {
|
|
528
533
|
if (shown) return;
|
|
529
534
|
shown = true;
|
|
535
|
+
if (!hasTransientUserActivation()) {
|
|
536
|
+
const activationError = Object.assign(
|
|
537
|
+
{ status: "error", error: "direct_user_action_required" },
|
|
538
|
+
base
|
|
539
|
+
);
|
|
540
|
+
post("rewarded_ad", void 0, Object.assign({ event: "error" }, activationError));
|
|
541
|
+
safeCall(options.onError, activationError);
|
|
542
|
+
done(activationError);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
530
545
|
post("rewarded_ad", void 0, Object.assign({ event: "started" }, base));
|
|
531
546
|
try {
|
|
532
547
|
showAdFn();
|
|
@@ -550,6 +565,7 @@ function createBBArcade() {
|
|
|
550
565
|
base
|
|
551
566
|
);
|
|
552
567
|
post("rewarded_ad", void 0, Object.assign({ event: "error" }, beforeError));
|
|
568
|
+
if (shown) return;
|
|
553
569
|
safeCall(options.onError, beforeError);
|
|
554
570
|
done(beforeError);
|
|
555
571
|
}
|
|
@@ -558,9 +574,9 @@ function createBBArcade() {
|
|
|
558
574
|
adBreak({
|
|
559
575
|
type: "reward",
|
|
560
576
|
name: options.placement,
|
|
561
|
-
size: options.size || void 0,
|
|
562
577
|
beforeReward: startAd,
|
|
563
578
|
adViewed: function() {
|
|
579
|
+
if (resolved) return;
|
|
564
580
|
status = "viewed";
|
|
565
581
|
const viewed = Object.assign({ status }, base);
|
|
566
582
|
post("rewarded_ad", void 0, Object.assign({ event: "viewed" }, viewed));
|
|
@@ -568,6 +584,7 @@ function createBBArcade() {
|
|
|
568
584
|
safeCall(options.onReward, viewed);
|
|
569
585
|
},
|
|
570
586
|
adDismissed: function() {
|
|
587
|
+
if (resolved) return;
|
|
571
588
|
if (status !== "viewed") status = "dismissed";
|
|
572
589
|
const dismissed = Object.assign({ status }, base);
|
|
573
590
|
post("rewarded_ad", void 0, Object.assign({ event: "dismissed" }, dismissed));
|
|
@@ -575,6 +592,7 @@ function createBBArcade() {
|
|
|
575
592
|
safeCall(options.onDismissed, dismissed);
|
|
576
593
|
},
|
|
577
594
|
adBreakDone: function(placementInfo) {
|
|
595
|
+
if (resolved) return;
|
|
578
596
|
const result = rewardOffered || status !== "unavailable" ? { status } : { status: "unavailable", error: "no_rewarded_ad" };
|
|
579
597
|
if (placementInfo && typeof placementInfo === "object") {
|
|
580
598
|
result.breakStatus = cleanText(
|
|
@@ -608,6 +626,52 @@ function createBBArcade() {
|
|
|
608
626
|
});
|
|
609
627
|
});
|
|
610
628
|
}
|
|
629
|
+
function prepareRewardedAd(rawOptions) {
|
|
630
|
+
const normalized = normalizeRewardedOptions(rawOptions);
|
|
631
|
+
const base = {
|
|
632
|
+
placement: normalized.placement,
|
|
633
|
+
reward: normalized.reward,
|
|
634
|
+
adBreakId: normalized.adBreakId
|
|
635
|
+
};
|
|
636
|
+
if (normalized.size) base.size = normalized.size;
|
|
637
|
+
let ready2 = false;
|
|
638
|
+
let finalPromise;
|
|
639
|
+
return new Promise(function(resolvePreparation) {
|
|
640
|
+
finalPromise = rewardedAd(
|
|
641
|
+
Object.assign({}, rawOptions, {
|
|
642
|
+
// Preserve the id used by the ready handle and every lifecycle event;
|
|
643
|
+
// rewardedAd normalizes again, but this explicit id keeps both stages
|
|
644
|
+
// tied to the same placement.
|
|
645
|
+
adBreakId: normalized.adBreakId,
|
|
646
|
+
beforeReward: function(showAd) {
|
|
647
|
+
if (ready2) return;
|
|
648
|
+
ready2 = true;
|
|
649
|
+
let shown = false;
|
|
650
|
+
const prepared = Object.assign(
|
|
651
|
+
{ status: "ready" },
|
|
652
|
+
base,
|
|
653
|
+
{
|
|
654
|
+
show: function() {
|
|
655
|
+
if (!shown) {
|
|
656
|
+
shown = true;
|
|
657
|
+
if (hasTransientUserActivation()) {
|
|
658
|
+
safeCall(rawOptions && rawOptions.onStart);
|
|
659
|
+
}
|
|
660
|
+
showAd();
|
|
661
|
+
}
|
|
662
|
+
return finalPromise;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
);
|
|
666
|
+
resolvePreparation(prepared);
|
|
667
|
+
}
|
|
668
|
+
})
|
|
669
|
+
);
|
|
670
|
+
finalPromise.then(function(result) {
|
|
671
|
+
if (!ready2) resolvePreparation(result);
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
}
|
|
611
675
|
function preloadRewardedAds(rawOptions) {
|
|
612
676
|
const options = normalizeRewardedOptions(rawOptions);
|
|
613
677
|
if (!areRewardedAdsEnabled()) return Promise.resolve(false);
|
|
@@ -654,7 +718,7 @@ function createBBArcade() {
|
|
|
654
718
|
reward: normalized.reward
|
|
655
719
|
};
|
|
656
720
|
if (normalized.size) base.size = normalized.size;
|
|
657
|
-
if (rewardedBreakActive) {
|
|
721
|
+
if (rewardedBreakActive && rewardedBreakActive.state !== "ready") {
|
|
658
722
|
post(
|
|
659
723
|
"rewarded_ad",
|
|
660
724
|
void 0,
|
|
@@ -665,24 +729,32 @@ function createBBArcade() {
|
|
|
665
729
|
);
|
|
666
730
|
return Promise.resolve(false);
|
|
667
731
|
}
|
|
668
|
-
|
|
732
|
+
const activeBreak = { state: "loading" };
|
|
733
|
+
rewardedBreakActive = activeBreak;
|
|
669
734
|
let startCalled = false;
|
|
735
|
+
const callerBeforeReward = rawOptions.beforeReward;
|
|
670
736
|
return rewardedAd(
|
|
671
737
|
Object.assign({}, rawOptions, {
|
|
672
738
|
beforeReward: function(showAd) {
|
|
673
|
-
if (
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
739
|
+
if (rewardedBreakActive === activeBreak) activeBreak.state = "ready";
|
|
740
|
+
const showWithStart = function() {
|
|
741
|
+
if (startCalled || rewardedBreakActive !== activeBreak) return;
|
|
742
|
+
startCalled = true;
|
|
743
|
+
activeBreak.state = "showing";
|
|
744
|
+
if (hasTransientUserActivation()) safeCall(rawOptions.onStart);
|
|
745
|
+
showAd();
|
|
746
|
+
};
|
|
747
|
+
if (typeof callerBeforeReward === "function") callerBeforeReward(showWithStart);
|
|
748
|
+
else showWithStart();
|
|
677
749
|
}
|
|
678
750
|
})
|
|
679
751
|
).then(
|
|
680
752
|
function(result) {
|
|
681
|
-
rewardedBreakActive =
|
|
753
|
+
if (rewardedBreakActive === activeBreak) rewardedBreakActive = null;
|
|
682
754
|
return Boolean(result && result.status === "viewed");
|
|
683
755
|
},
|
|
684
756
|
function() {
|
|
685
|
-
rewardedBreakActive =
|
|
757
|
+
if (rewardedBreakActive === activeBreak) rewardedBreakActive = null;
|
|
686
758
|
return false;
|
|
687
759
|
}
|
|
688
760
|
);
|
|
@@ -720,6 +792,8 @@ function createBBArcade() {
|
|
|
720
792
|
},
|
|
721
793
|
rewardedAd,
|
|
722
794
|
showRewardedAd: rewardedAd,
|
|
795
|
+
prepareRewardedAd,
|
|
796
|
+
prepareRewardedBreak: prepareRewardedAd,
|
|
723
797
|
rewardedBreak,
|
|
724
798
|
save: function(blob) {
|
|
725
799
|
return request("save", String(blob));
|
|
@@ -765,6 +839,13 @@ function createInertBBArcade() {
|
|
|
765
839
|
reward: "reward",
|
|
766
840
|
adBreakId: "bbad_inert"
|
|
767
841
|
});
|
|
842
|
+
const prepareUnavailable = () => Promise.resolve({
|
|
843
|
+
status: "unavailable",
|
|
844
|
+
error: "ad_break_unavailable",
|
|
845
|
+
placement: "rewarded",
|
|
846
|
+
reward: "reward",
|
|
847
|
+
adBreakId: "bbad_inert"
|
|
848
|
+
});
|
|
768
849
|
return {
|
|
769
850
|
lockToHost: noop,
|
|
770
851
|
init: () => Promise.resolve(),
|
|
@@ -781,6 +862,8 @@ function createInertBBArcade() {
|
|
|
781
862
|
xrSessionEnd: noop,
|
|
782
863
|
rewardedAd: unavailable,
|
|
783
864
|
showRewardedAd: unavailable,
|
|
865
|
+
prepareRewardedAd: prepareUnavailable,
|
|
866
|
+
prepareRewardedBreak: prepareUnavailable,
|
|
784
867
|
rewardedBreak: () => Promise.resolve(false),
|
|
785
868
|
save: () => Promise.reject(sdkError2("unsupported")),
|
|
786
869
|
load: () => Promise.reject(sdkError2("unsupported")),
|
|
@@ -802,4 +885,4 @@ export {
|
|
|
802
885
|
BBArcade,
|
|
803
886
|
src_default
|
|
804
887
|
};
|
|
805
|
-
//# sourceMappingURL=chunk-
|
|
888
|
+
//# sourceMappingURL=chunk-OWOESH4X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts","../src/core.ts","../src/inert.ts","../src/index.ts"],"sourcesContent":["/**\n * Wire-protocol constants for the bb-arcade postMessage protocol. The protocol\n * version is deliberately decoupled from the npm package's semver: every\n * message carries `v: VERSION`, and the host accepts all messages of the same\n * major protocol. Bump VERSION only for a breaking wire change (which also\n * means a new /arcade-sdk/v2.js URL).\n */\nexport const VERSION = 1;\n\n/** `source` field on every message the game/SDK posts to the embedding page. */\nexport const SDK_MESSAGE_SOURCE = 'bb-arcade';\n\n/** `source` field on every message the Bounty Board host posts to the game. */\nexport const HOST_MESSAGE_SOURCE = 'bb-arcade-host';\n","/**\n * Bounty Board Arcade SDK core — a faithful, typed port of the original\n * public/arcade-sdk.js IIFE. The whole SDK is one factory so every piece of\n * state (blocked flag, pending requests, ad config, host config) lives in one\n * closure per instance, exactly like the original script. Behavior parity with\n * the shipped v1 script is the hard requirement here; the v1 behavior test\n * suite (src/__tests__/arcade-sdk.test.ts at the repo root) runs against the\n * generated artifact and must pass unchanged.\n */\nimport { HOST_MESSAGE_SOURCE, SDK_MESSAGE_SOURCE, VERSION } from './protocol';\nimport type {\n BBArcadeConfig,\n BBArcadeError,\n BBArcadeErrorCode,\n BBArcadeLockToHostOptions,\n BBArcadePlayer,\n BBArcadePreparedRewardedAd,\n BBArcadePrepareRewardedAdOptions,\n BBArcadeRewardedAdOptions,\n BBArcadeRewardedAdPreparation,\n BBArcadeRewardedAdPrepareFailure,\n BBArcadeRewardedAdResult,\n BBArcadeRewardedBreakOptions,\n BBArcadeSDK,\n} from './types';\n\n// Canonical production home, used for the site-lock \"play the original\" link.\nconst BOUNTY_BOARD_URL = 'https://www.bountyboard.gg/arcade';\nconst ADSENSE_CLIENT_ID = 'ca-pub-7727110228190547';\nconst AD_PLACEMENT_SRC =\n 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=' + ADSENSE_CLIENT_ID;\nconst AD_PLACEMENT_SCRIPT_ID = 'bb-arcade-h5-ads';\nconst DEFAULT_AD_LOAD_TIMEOUT_MS = 6000;\nconst SAVE_TIMEOUT_MS = 15000;\n// How long init() waits for the host's config reply before resolving anyway.\n// Standalone play resolves immediately (there is no host to answer), so this\n// grace only delays games embedded somewhere that isn't a Bounty Board host.\nconst INIT_CONFIG_GRACE_MS = 1500;\n\n// Allowed by default: bountyboard.gg (and any *.bountyboard.gg subdomain),\n// Bounty Board's fixed staging alias, and localhost/127.0.0.1 for local\n// development — i.e. every environment Bounty Board itself serves the game\n// from. The staging alias is a single PINNED Bounty Board deployment, so\n// hardcoding that one host is safe; we still deliberately DON'T trust shared\n// or dynamic preview domains broadly (e.g. *.vercel.app) — a per-deploy\n// preview host or your own site must be added via the `allow` option.\n// This list gates BOTH the sitelock AND which parent origin may send the host\n// config (player identity / rewarded-ads), so a missing environment here\n// silently breaks the whole SDK handshake there, not just the sitelock.\nconst DEFAULT_ALLOW = [\n 'bountyboard.gg',\n 'bountyboard-staging.vercel.app',\n 'localhost',\n '127.0.0.1',\n];\n\n// Public half of the sitelock attestation keypair (ECDSA P-256, SPKI). The\n// matching private key lives ONLY in Bounty Board server env\n// (ARCADE_SITELOCK_PRIVATE_KEY); a re-hosting site can neither sign a fresh\n// attestation nor replay a captured one (the sender-origin check below).\nconst SITELOCK_PUBLIC_KEY_B64 =\n 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6WC8fJweDpfKRcZW9Q9eOLxPXmFCO8R8lY1kfZbuAQt65E0CTu2rM80XfW3nYrSAqO+Eev8gxrGS0bAnNaMDGg==';\n/** Attestations older/newer than this are stale (replay window). */\nconst SITELOCK_MAX_SKEW_MS = 5 * 60 * 1000;\n\n/**\n * Google H5 ads globals the SDK shims/loads on demand. A standalone shape\n * (not `extends Window`) so host apps that augment Window themselves (e.g.\n * test files declaring `adBreak?: jest.Mock`) can't create a conflicting\n * interface under a shared tsconfig.\n */\ntype AdsWindow = {\n adsbygoogle?: unknown[];\n adBreak?: (placement: Record<string, unknown>) => void;\n adConfig?: (settings: Record<string, unknown>) => void;\n};\n\ninterface HostAttestation {\n origin: string;\n ts: number;\n sig: string;\n}\n\n/** The identifying fields every rewarded-ad result/lifecycle event carries. */\ninterface AdResultBase {\n placement: string;\n reward: string;\n adBreakId: string;\n size?: 'small' | 'medium' | 'large';\n}\n\ninterface PendingEntry {\n resolve: (value: unknown) => void;\n reject: (err: BBArcadeError) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\ninterface NormalizedRewardedOptions {\n placement: string;\n reward: string;\n adBreakId: string;\n size: '' | 'small' | 'medium' | 'large';\n adChannel: string;\n testMode: boolean;\n loadTimeoutMs: number;\n beforeReward?: (showAd: () => void) => void;\n adViewed?: (result: BBArcadeRewardedAdResult) => void;\n adDismissed?: (result: BBArcadeRewardedAdResult) => void;\n adBreakDone?: (placementInfo: unknown) => void;\n onReward?: (result: BBArcadeRewardedAdResult) => void;\n onDismissed?: (result: BBArcadeRewardedAdResult) => void;\n onDone?: (result: BBArcadeRewardedAdResult) => void;\n onError?: (result: BBArcadeRewardedAdResult) => void;\n onUnavailable?: (result: BBArcadeRewardedAdResult) => void;\n}\n\nfunction sdkError(code: BBArcadeErrorCode): BBArcadeError {\n const err = new Error(code) as BBArcadeError;\n err.code = code;\n return err;\n}\n\nfunction b64ToBytes(b64: string): Uint8Array {\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return bytes;\n}\n\nfunction cleanText(value: unknown, fallback?: string, max?: number): string {\n if (typeof value !== 'string') return fallback || '';\n return value.replace(/[^\\w:.-]+/g, '_').slice(0, max || 80) || fallback || '';\n}\n\nfunction toBoolean(value: unknown): boolean {\n return value === true || value === 'true' || value === '1';\n}\n\nfunction cleanRewardedSize(value: unknown): '' | 'small' | 'medium' | 'large' {\n return value === 'small' || value === 'medium' || value === 'large' ? value : '';\n}\n\n/**\n * Google requires showAdFn to run while the browser still has transient user\n * activation from the player's direct click/tap. Fail closed when the browser\n * cannot positively prove that activation is active; showing without it is\n * both unreliable and contrary to the placement API contract.\n */\nfunction hasTransientUserActivation(): boolean {\n if (typeof navigator === 'undefined') return false;\n const activation = (\n navigator as Navigator & {\n userActivation?: { isActive?: boolean };\n }\n ).userActivation;\n return activation?.isActive === true;\n}\n\nfunction safeCall<T>(fn: ((arg: T) => void) | undefined, arg?: T): void {\n if (typeof fn !== 'function') return;\n try {\n fn(arg as T);\n } catch (err) {\n setTimeout(function () {\n throw err;\n }, 0);\n }\n}\n\n// Normalize an allow-list entry to a bare hostname, so a game author can pass a\n// plain host (\"mygame.com\"), a full URL (\"https://mygame.com/play\"), or a\n// host:port and still match against the embedder's hostname.\nfunction toAllowedHost(value: unknown): string {\n const raw = String(value).toLowerCase().trim();\n if (!raw) return '';\n try {\n return new URL(raw.indexOf('//') === -1 ? 'https://' + raw : raw).hostname;\n } catch (err) {\n return raw;\n }\n}\n\nfunction hostnameAllowed(origin: unknown, allowHosts: string[]): boolean {\n if (!origin || origin === 'null' || typeof origin !== 'string') return false;\n let host: string;\n try {\n host = new URL(origin).hostname.toLowerCase();\n } catch (err) {\n return false;\n }\n for (let i = 0; i < allowHosts.length; i++) {\n const allowed = allowHosts[i];\n // exact host, or a subdomain of an allowed host (\".allowed\")\n if (host === allowed || host.slice(-(allowed.length + 1)) === '.' + allowed) return true;\n }\n return false;\n}\n\n/**\n * Verify a host attestation: shape, freshness, the LOAD-BEARING sender\n * check (the postMessage carrying it must come from the origin it attests —\n * event.origin is browser-authenticated, so a stolen attestation replayed\n * from another site fails here), then the ECDSA signature over\n * \"<origin>:<ts>\". Resolves boolean; never throws.\n */\nfunction verifyHostAttestation(att: unknown, senderOrigin: string): Promise<boolean> {\n const a = att as HostAttestation | null | undefined;\n if (!a || typeof a.origin !== 'string' || typeof a.ts !== 'number' || typeof a.sig !== 'string')\n return Promise.resolve(false);\n if (a.origin !== senderOrigin) return Promise.resolve(false);\n if (Math.abs(Date.now() - a.ts) > SITELOCK_MAX_SKEW_MS) return Promise.resolve(false);\n try {\n return window.crypto.subtle\n .importKey(\n 'spki',\n b64ToBytes(SITELOCK_PUBLIC_KEY_B64),\n { name: 'ECDSA', namedCurve: 'P-256' },\n false,\n ['verify']\n )\n .then(function (key) {\n return window.crypto.subtle.verify(\n { name: 'ECDSA', hash: 'SHA-256' },\n key,\n b64ToBytes(a.sig),\n new TextEncoder().encode(a.origin + ':' + a.ts)\n );\n })\n .then(\n function (ok) {\n return ok === true;\n },\n function () {\n return false;\n }\n );\n } catch (err) {\n return Promise.resolve(false);\n }\n}\n\n// Normalize the host-sent player identity. Only a non-empty string name\n// counts; the avatar is a plain URL string or null. Anything malformed\n// collapses to null so games can rely on the { name, avatarUrl } shape.\nfunction sanitizePlayer(value: unknown): BBArcadePlayer | null {\n if (!value || typeof value !== 'object') return null;\n const raw = value as { name?: unknown; avatarUrl?: unknown };\n const name = typeof raw.name === 'string' ? raw.name.trim().slice(0, 120) : '';\n if (!name) return null;\n const avatarUrl =\n typeof raw.avatarUrl === 'string' && raw.avatarUrl.trim() ? raw.avatarUrl : null;\n return { name: name, avatarUrl: avatarUrl };\n}\n\n/**\n * Build one SDK instance. Attaches this instance's `message` listeners to\n * `window` immediately (the host pushes its config at load time, so listener\n * timing is part of the protocol). Browser-only — the module entry substitutes\n * an inert stub when `window` is undefined (SSR-safety for bundled games).\n */\nexport function createBBArcade(): BBArcadeSDK {\n let adPlacementLoad: Promise<AdsWindow['adBreak'] | null> | null = null;\n let adPreloadConfigured = false;\n let rewardedBreakActive: { state: 'loading' | 'ready' | 'showing' } | null = null;\n let rewardedBreakSequence = 0;\n\n function isLocalAdHost(): boolean {\n try {\n const protocol = window.location && window.location.protocol;\n const host = window.location && window.location.hostname;\n return (\n protocol === 'file:' ||\n host === 'localhost' ||\n host === '127.0.0.1' ||\n host === '::1' ||\n host === '[::1]'\n );\n } catch (err) {\n return false;\n }\n }\n\n const localAdHost = isLocalAdHost();\n const config = {\n rewardedAds: {\n enabled: true,\n adChannel: '',\n testMode: localAdHost,\n loadTimeoutMs: DEFAULT_AD_LOAD_TIMEOUT_MS,\n preload: false,\n },\n };\n const hostConfig = {\n rewardedAds: {\n received: false,\n enabled: localAdHost,\n adChannel: '',\n testMode: localAdHost,\n preload: false,\n },\n };\n // True once ANY valid host config message has arrived — this (not the\n // rewarded-ads sub-flag) is what resolves the init()/getPlayer() handshake.\n let hostConfigReceived = false;\n // The logged-in player's display identity from the host config:\n // { name, avatarUrl } or null for guests. Display name + avatar ONLY — the\n // host never sends ids, emails, or roles.\n let hostPlayer: BBArcadePlayer | null = null;\n\n // Set once lockToHost() decides the game is running somewhere it shouldn't.\n // After that the SDK goes silent so a scraped copy can't keep posting.\n let blocked = false;\n\n const pending: Record<string, PendingEntry> = {};\n let seq = 0;\n\n // The game can be embedded by any Bounty Board environment (prod, staging,\n // previews), so target '*' — the payload carries nothing sensitive and the\n // parent page validates the message's source window + origin itself.\n function post(type: string, score?: number, extra?: object): void {\n if (blocked) return;\n const message: Record<string, unknown> = { source: SDK_MESSAGE_SOURCE, v: VERSION, type: type };\n if (typeof score === 'number' && isFinite(score)) message.score = Math.floor(score);\n if (extra && typeof extra === 'object') {\n const fields = extra as Record<string, unknown>;\n for (const key in fields) {\n if (Object.prototype.hasOwnProperty.call(fields, key) && fields[key] !== undefined) {\n message[key] = fields[key];\n }\n }\n }\n try {\n window.parent.postMessage(message, '*');\n } catch (err) {\n /* not embedded — standalone play is fine */\n }\n }\n\n function isEmbedded(): boolean {\n try {\n return window.parent != null && window.parent !== window;\n } catch (err) {\n return true; // touching a cross-origin parent threw → we are embedded\n }\n }\n\n // ── Request/response over postMessage (cloud save, experiments, host) ──────\n // Cloud saves only work for Bounty-Board-hosted build uploads: those run in a\n // sandbox with no localStorage/IndexedDB, so saves go through the embedding\n // page (which holds the player's login). save/load return a Promise; the\n // parent posts back a result for the matching requestId. Rejections are Error\n // objects that also carry a machine-readable `code` property.\n function request(type: string, payload?: unknown): Promise<unknown> {\n return new Promise(function (resolve, reject) {\n if (blocked) return reject(sdkError('error'));\n // Standalone play (no embedding page): nothing will ever answer, so fail\n // fast instead of hanging until the timeout.\n if (!isEmbedded()) return reject(sdkError('unsupported'));\n const requestId = type + '_' + ++seq + '_' + Date.now();\n const timer = setTimeout(function () {\n if (pending[requestId]) {\n delete pending[requestId];\n reject(sdkError('error'));\n }\n }, SAVE_TIMEOUT_MS);\n pending[requestId] = { resolve: resolve, reject: reject, timer: timer };\n try {\n window.parent.postMessage(\n {\n source: SDK_MESSAGE_SOURCE,\n v: VERSION,\n type: type,\n requestId: requestId,\n payload: payload,\n },\n '*'\n );\n } catch (err) {\n clearTimeout(timer);\n delete pending[requestId];\n reject(sdkError('error'));\n }\n });\n }\n\n window.addEventListener('message', function (event: MessageEvent) {\n // Replies come from the embedding page only; ignore anything else (e.g. a\n // co-resident ad/nested frame, or a null-source synthetic event) so it\n // can't resolve a pending request with a forged payload. Mirrors the\n // host-config listener's source check below.\n if (event.source !== window.parent) return;\n const data = event.data as\n | {\n source?: string;\n type?: string;\n requestId?: string;\n ok?: boolean;\n code?: BBArcadeErrorCode;\n data?: unknown;\n attestation?: unknown;\n variant?: unknown;\n }\n | null\n | undefined;\n if (!data || data.source !== SDK_MESSAGE_SOURCE) return;\n // Any '<type>_result' with a pending requestId is a legitimate reply: the\n // sender is already proven to be the embedding host (source check above)\n // and requestIds are per-request — so enumerating reply types here would\n // only create a synchronized-edit hazard with the host (a forgotten entry\n // = that request silently timing out after 15s).\n if (typeof data.type !== 'string' || !data.type.endsWith('_result')) return;\n const entry = pending[data.requestId as string];\n if (!entry) return;\n clearTimeout(entry.timer);\n delete pending[data.requestId as string];\n // The browser-authenticated sender origin is authoritative. Never trust a\n // parent-supplied origin string: an arbitrary embedder could claim to be\n // bountyboard.gg and enable host-only SDK features.\n if (data.type === 'host_result') return entry.resolve({ origin: event.origin });\n if (data.type === 'host_attest_result')\n // Keep the browser-authenticated sender origin alongside the claimed\n // attestation — the verifier requires them to MATCH.\n return entry.resolve({ origin: event.origin, attestation: data.attestation || null });\n if (data.type === 'experiment_result')\n return entry.resolve(typeof data.variant === 'string' ? data.variant : null);\n // Generic ok/code convention (save_result, load_result, mp_ticket_result,\n // and any future reply type): resolve the carried data — load()'s \"no\n // save yet\" stays null, save()'s bare ack stays undefined.\n if (data.ok)\n entry.resolve(\n data.type === 'load_result' ? (data.data != null ? data.data : null) : data.data\n );\n else entry.reject(sdkError(data.code || 'error'));\n });\n\n // ── Site lock (anti scrape-and-reupload) ───────────────────────────────────\n\n // The origin of the page that embeds this game, read from the browser (which\n // the embedding page can't forge). Returns null when it can't be determined\n // synchronously (opaque-origin builds with no referrer) — callers fall back to\n // the host handshake then.\n function embeddingOriginSync(): string | null {\n try {\n if (window.top === window.self) return window.location.origin; // not embedded\n } catch (err) {\n /* cross-origin top → we are embedded; keep probing */\n }\n try {\n const ancestors = window.location.ancestorOrigins;\n if (ancestors && ancestors.length && ancestors[0] && ancestors[0] !== 'null')\n return ancestors[0]; // the immediate embedder\n } catch (err) {\n /* not supported (Firefox) → fall through to referrer */\n }\n if (document.referrer) {\n try {\n return new URL(document.referrer).origin;\n } catch (err) {\n /* unparseable referrer */\n }\n }\n return null;\n }\n\n function blockHost(options: BBArcadeLockToHostOptions): void {\n if (blocked) return;\n blocked = true;\n if (typeof options.onBlocked === 'function') {\n try {\n options.onBlocked();\n } catch (err) {\n /* the game's own handler threw — nothing more we can do */\n }\n return;\n }\n if (options.redirect) {\n try {\n (window.top as Window).location.href = options.redirect; // escape the framing if allowed\n return;\n } catch (err) {\n try {\n window.location.href = options.redirect;\n return;\n } catch (err2) {\n /* navigation blocked (sandbox) → fall through to the splash */\n }\n }\n }\n try {\n // Link the splash out to Bounty Board so a player who lands on a scraped\n // copy can reach the real game. target=\"_blank\" works even from a\n // sandboxed re-host iframe that can't navigate its top; the canonical\n // production URL is intentional (a copy blocked anywhere points home).\n document.documentElement.innerHTML =\n '<div style=\"position:fixed;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;' +\n 'font-family:system-ui,-apple-system,sans-serif;background:#1a1206;color:#fff;text-align:center;padding:24px;\">' +\n '<div style=\"font-size:18px;font-weight:700;\">This game isn\\'t available here</div>' +\n '<a href=\"' +\n BOUNTY_BOARD_URL +\n '\" target=\"_blank\" rel=\"noopener\" ' +\n 'style=\"display:inline-block;background:#f5b942;color:#1a1206;font-weight:700;font-size:14px;' +\n 'text-decoration:none;padding:10px 20px;border-radius:999px;\">Play the original on Bounty Board</a>' +\n '</div>';\n } catch (err) {\n /* document not writable — the silent `blocked` flag still stops SDK posts */\n }\n }\n\n /** The original best-effort check (browser signals + host handshake). */\n function lockToHostBestEffort(options: BBArcadeLockToHostOptions, allow: string[]): void {\n const origin = embeddingOriginSync();\n if (origin != null) {\n if (!hostnameAllowed(origin, allow)) blockHost(options);\n return; // conclusive without the parent\n }\n // Opaque-origin build with no referrer: ask the parent where we're hosted.\n request('host').then(\n function (res) {\n const r = res as { origin?: string } | null;\n if (!hostnameAllowed(r && r.origin, allow)) blockHost(options);\n },\n function () {\n blockHost(options); // no Bounty Board parent answered\n }\n );\n }\n\n function lockToHost(options?: BBArcadeLockToHostOptions): void {\n const opts = options || {};\n const allow = DEFAULT_ALLOW.slice();\n if (opts.allow && opts.allow.length) {\n for (let i = 0; i < opts.allow.length; i++) {\n const host = toAllowedHost(opts.allow[i]);\n if (host) allow.push(host);\n }\n }\n // Signed mode ({ signed: true }): demand a server-signed origin\n // attestation from the parent and verify it cryptographically. Graceful\n // degradation is deliberate and narrow: no crypto.subtle (insecure dev\n // context) or an honest \"not configured\" reply falls back to the\n // best-effort check; a PRESENT attestation that fails verification, or no\n // parent answering at all, blocks.\n if (opts.signed) {\n if (!(window.crypto && window.crypto.subtle)) {\n lockToHostBestEffort(opts, allow);\n return;\n }\n request('host_attest').then(\n function (res) {\n const r = res as { origin: string; attestation: HostAttestation | null } | null;\n if (!r || !r.attestation) {\n lockToHostBestEffort(opts, allow); // host has no signing key yet\n return;\n }\n verifyHostAttestation(r.attestation, r.origin).then(function (ok) {\n if (!ok || !hostnameAllowed(r.attestation && r.attestation.origin, allow))\n blockHost(opts);\n });\n },\n function () {\n blockHost(opts); // no Bounty Board parent answered\n }\n );\n return;\n }\n lockToHostBestEffort(opts, allow);\n }\n\n // ── Rewarded ads (Google H5 placement API, host-gated) ─────────────────────\n\n function nextAdBreakId(): string {\n rewardedBreakSequence += 1;\n return [\n 'bbad',\n Date.now().toString(36),\n rewardedBreakSequence.toString(36),\n Math.random().toString(36).slice(2, 10),\n ].join('_');\n }\n\n function configure(nextConfig?: BBArcadeConfig): void {\n if (!nextConfig || typeof nextConfig !== 'object') return;\n const rewardedAds = nextConfig.rewardedAds;\n if (!rewardedAds || typeof rewardedAds !== 'object') return;\n\n if (rewardedAds.enabled !== undefined) {\n config.rewardedAds.enabled = rewardedAds.enabled !== false;\n }\n if (rewardedAds.adChannel !== undefined) {\n config.rewardedAds.adChannel = cleanText(rewardedAds.adChannel);\n }\n if (rewardedAds.testMode !== undefined) {\n config.rewardedAds.testMode = toBoolean(rewardedAds.testMode);\n }\n if (rewardedAds.preload !== undefined) {\n config.rewardedAds.preload = toBoolean(rewardedAds.preload);\n }\n if (\n typeof rewardedAds.loadTimeoutMs === 'number' &&\n isFinite(rewardedAds.loadTimeoutMs) &&\n rewardedAds.loadTimeoutMs > 0\n ) {\n config.rewardedAds.loadTimeoutMs = Math.min(15000, Math.floor(rewardedAds.loadTimeoutMs));\n }\n if (config.rewardedAds.preload) preloadRewardedAds();\n }\n\n // init() promises waiting on the host's config message (the real handshake:\n // the host re-sends its config whenever the game posts init/ready, so a\n // late-loading SDK can't miss the load-time push).\n let configWaiters: Array<() => void> = [];\n\n function notifyConfigReceived(): void {\n const waiters = configWaiters;\n configWaiters = [];\n for (let i = 0; i < waiters.length; i++) safeCall(waiters[i]);\n }\n\n function applyHostConfig(message: unknown): void {\n if (!message || typeof message !== 'object') return;\n const msg = message as {\n source?: string;\n type?: string;\n player?: unknown;\n rewardedAds?: {\n enabled?: unknown;\n adChannel?: unknown;\n testMode?: unknown;\n preload?: unknown;\n };\n };\n if (msg.source !== HOST_MESSAGE_SOURCE || msg.type !== 'config') return;\n\n hostConfigReceived = true;\n\n // Player display identity (name + avatar only; null for guests). Only\n // applied when the key is present, so an older host that doesn't send it\n // can't clear a value a newer message already delivered.\n if (msg.player !== undefined) {\n hostPlayer = sanitizePlayer(msg.player);\n }\n\n const rewardedAds = msg.rewardedAds;\n if (rewardedAds && typeof rewardedAds === 'object') {\n hostConfig.rewardedAds.received = true;\n if (rewardedAds.enabled !== undefined) {\n hostConfig.rewardedAds.enabled = toBoolean(rewardedAds.enabled);\n }\n if (rewardedAds.adChannel !== undefined) {\n hostConfig.rewardedAds.adChannel = cleanText(rewardedAds.adChannel);\n }\n if (rewardedAds.testMode !== undefined) {\n hostConfig.rewardedAds.testMode = toBoolean(rewardedAds.testMode);\n }\n if (rewardedAds.preload !== undefined) {\n hostConfig.rewardedAds.preload = toBoolean(rewardedAds.preload);\n }\n\n if (\n hostConfig.rewardedAds.enabled &&\n (hostConfig.rewardedAds.preload || config.rewardedAds.preload)\n ) {\n preloadRewardedAds();\n }\n }\n\n notifyConfigReceived();\n }\n\n function areRewardedAdsEnabled(): boolean {\n return config.rewardedAds.enabled !== false && hostConfig.rewardedAds.enabled === true;\n }\n\n function installAdPlacementShim(): void {\n const w = window as unknown as AdsWindow;\n w.adsbygoogle = w.adsbygoogle || [];\n if (typeof w.adBreak !== 'function') {\n w.adBreak = function (placement) {\n (w.adsbygoogle as unknown[]).push(placement);\n };\n }\n if (typeof w.adConfig !== 'function') {\n w.adConfig = function (settings) {\n (w.adsbygoogle as unknown[]).push(settings);\n };\n }\n }\n\n function ensureAdPlacement(\n options: NormalizedRewardedOptions\n ): Promise<AdsWindow['adBreak'] | null> {\n if (typeof document === 'undefined') return Promise.resolve(null);\n installAdPlacementShim();\n const w = window as unknown as AdsWindow;\n if (typeof w.adBreak !== 'function') return Promise.resolve(null);\n if (adPlacementLoad) return adPlacementLoad;\n\n adPlacementLoad = new Promise(function (resolve) {\n let script = document.getElementById(AD_PLACEMENT_SCRIPT_ID);\n if (!script) {\n const el = document.createElement('script');\n el.id = AD_PLACEMENT_SCRIPT_ID;\n el.async = true;\n el.src = AD_PLACEMENT_SRC;\n el.crossOrigin = 'anonymous';\n el.setAttribute('data-ad-client', ADSENSE_CLIENT_ID);\n if (options.adChannel) el.setAttribute('data-ad-channel', options.adChannel);\n if (options.testMode) el.setAttribute('data-adbreak-test', 'on');\n (document.head || document.documentElement).appendChild(el);\n script = el;\n }\n resolve(w.adBreak);\n });\n\n return adPlacementLoad;\n }\n\n function requestAdPreload(): void {\n const w = window as unknown as AdsWindow;\n if (adPreloadConfigured || typeof w.adConfig !== 'function') return;\n adPreloadConfigured = true;\n try {\n w.adConfig({ preloadAdBreaks: 'on' });\n } catch (err) {\n // Preloading is best-effort; the rewarded placement can still report\n // availability through adBreakDone.\n }\n }\n\n function normalizeRewardedOptions(\n options?: BBArcadeRewardedAdOptions\n ): NormalizedRewardedOptions {\n const opts = options && typeof options === 'object' ? options : {};\n const hostHasConfig = hostConfig.rewardedAds.received;\n return {\n placement: cleanText(opts.placement || opts.name, 'rewarded'),\n reward: cleanText(opts.reward, 'reward'),\n adBreakId: cleanText(opts.adBreakId, nextAdBreakId(), 128),\n size: cleanRewardedSize(opts.size),\n adChannel: cleanText(\n hostHasConfig\n ? hostConfig.rewardedAds.adChannel\n : opts.adChannel || config.rewardedAds.adChannel\n ),\n testMode: hostHasConfig\n ? hostConfig.rewardedAds.testMode\n : opts.testMode !== undefined\n ? toBoolean(opts.testMode)\n : config.rewardedAds.testMode || hostConfig.rewardedAds.testMode,\n loadTimeoutMs:\n typeof opts.loadTimeoutMs === 'number' && isFinite(opts.loadTimeoutMs)\n ? Math.min(15000, Math.max(1, Math.floor(opts.loadTimeoutMs)))\n : config.rewardedAds.loadTimeoutMs,\n beforeReward: opts.beforeReward,\n adViewed: opts.adViewed,\n adDismissed: opts.adDismissed,\n adBreakDone: opts.adBreakDone,\n onReward: opts.onReward,\n onDismissed: opts.onDismissed,\n onDone: opts.onDone,\n onError: opts.onError,\n onUnavailable: opts.onUnavailable,\n };\n }\n\n function rewardedAd(rawOptions?: BBArcadeRewardedAdOptions): Promise<BBArcadeRewardedAdResult> {\n const options = normalizeRewardedOptions(rawOptions);\n const base: AdResultBase = {\n placement: options.placement,\n reward: options.reward,\n adBreakId: options.adBreakId,\n };\n if (options.size) base.size = options.size;\n\n post('rewarded_ad', undefined, Object.assign({ event: 'requested' }, base));\n if (!areRewardedAdsEnabled()) {\n const disabled: BBArcadeRewardedAdResult = Object.assign(\n { status: 'unavailable' as const, error: 'host_disabled' },\n base\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'unavailable' }, disabled));\n safeCall(options.onUnavailable, disabled);\n safeCall(options.onDone, disabled);\n return Promise.resolve(disabled);\n }\n\n return ensureAdPlacement(options).then(function (adBreak) {\n if (typeof adBreak !== 'function') {\n const unavailable: BBArcadeRewardedAdResult = Object.assign(\n { status: 'unavailable' as const, error: 'ad_break_unavailable' },\n base\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'unavailable' }, unavailable));\n safeCall(options.onUnavailable, unavailable);\n safeCall(options.onDone, unavailable);\n return unavailable;\n }\n requestAdPreload();\n\n return new Promise<BBArcadeRewardedAdResult>(function (resolve) {\n let resolved = false;\n let status: BBArcadeRewardedAdResult['status'] = 'unavailable';\n let rewardOffered = false;\n const waitTimer = setTimeout(function () {\n if (rewardOffered || resolved) return;\n const timeout: BBArcadeRewardedAdResult = Object.assign(\n { status: 'unavailable' as const, error: 'ad_break_timeout' },\n base\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'unavailable' }, timeout));\n safeCall(options.onUnavailable, timeout);\n done(timeout);\n }, options.loadTimeoutMs);\n\n function done(result?: Partial<BBArcadeRewardedAdResult>): void {\n if (resolved) return;\n resolved = true;\n clearTimeout(waitTimer);\n const finalResult = Object.assign(\n { status: 'unavailable' as BBArcadeRewardedAdResult['status'] },\n base,\n result || {}\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'done' }, finalResult));\n safeCall(options.onDone, finalResult);\n resolve(finalResult);\n }\n\n function startAd(showAdFn: () => void): void {\n // A late beforeReward (ad becomes ready after the load timeout already\n // resolved the placement 'unavailable') must not still show an ad — the\n // game has moved on, so it would be an unrewarded, involuntary impression.\n if (resolved) return;\n rewardOffered = true;\n clearTimeout(waitTimer);\n status = 'ready';\n let shown = false;\n function show(): void {\n if (shown) return;\n shown = true;\n if (!hasTransientUserActivation()) {\n const activationError: BBArcadeRewardedAdResult = Object.assign(\n { status: 'error' as const, error: 'direct_user_action_required' },\n base\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'error' }, activationError));\n safeCall(options.onError, activationError);\n done(activationError);\n return;\n }\n post('rewarded_ad', undefined, Object.assign({ event: 'started' }, base));\n try {\n showAdFn();\n } catch (err) {\n const showError: BBArcadeRewardedAdResult = Object.assign(\n { status: 'error' as const, error: 'show_ad_error' },\n base\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'error' }, showError));\n safeCall(options.onError, showError);\n done(showError);\n }\n }\n\n post('rewarded_ad', undefined, Object.assign({ event: 'ready' }, base));\n try {\n if (typeof options.beforeReward === 'function') options.beforeReward(show);\n else show();\n } catch (err) {\n const beforeError: BBArcadeRewardedAdResult = Object.assign(\n { status: 'error' as const, error: 'before_reward_error' },\n base\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'error' }, beforeError));\n // If the callback called show() before throwing, Google's ad is\n // already in flight. Keep the placement alive so a later adViewed\n // can still grant the reward; this error is diagnostic only.\n if (shown) return;\n safeCall(options.onError, beforeError);\n done(beforeError);\n }\n }\n\n try {\n adBreak({\n type: 'reward',\n name: options.placement,\n beforeReward: startAd,\n adViewed: function () {\n if (resolved) return;\n status = 'viewed';\n const viewed: BBArcadeRewardedAdResult = Object.assign({ status: status }, base);\n post('rewarded_ad', undefined, Object.assign({ event: 'viewed' }, viewed));\n safeCall(options.adViewed, viewed);\n safeCall(options.onReward, viewed);\n },\n adDismissed: function () {\n if (resolved) return;\n if (status !== 'viewed') status = 'dismissed';\n const dismissed: BBArcadeRewardedAdResult = Object.assign({ status: status }, base);\n post('rewarded_ad', undefined, Object.assign({ event: 'dismissed' }, dismissed));\n safeCall(options.adDismissed, dismissed);\n safeCall(options.onDismissed, dismissed);\n },\n adBreakDone: function (placementInfo: unknown) {\n if (resolved) return;\n const result: Partial<BBArcadeRewardedAdResult> =\n rewardOffered || status !== 'unavailable'\n ? { status: status }\n : { status: 'unavailable', error: 'no_rewarded_ad' };\n if (placementInfo && typeof placementInfo === 'object') {\n result.breakStatus = cleanText(\n (placementInfo as { breakStatus?: unknown }).breakStatus\n );\n }\n if (result.status === 'unavailable') {\n post(\n 'rewarded_ad',\n undefined,\n Object.assign({ event: 'unavailable' }, base, result)\n );\n safeCall(\n options.onUnavailable,\n Object.assign({ status: 'unavailable' as const }, base, result)\n );\n }\n safeCall(options.adBreakDone, placementInfo);\n done(result);\n },\n });\n } catch (err) {\n const error: BBArcadeRewardedAdResult = Object.assign(\n { status: 'error' as const, error: 'ad_break_error' },\n base\n );\n post('rewarded_ad', undefined, Object.assign({ event: 'error' }, error));\n safeCall(options.onError, error);\n done(error);\n }\n });\n });\n }\n\n /**\n * Two-stage rewarded flow. Google's beforeReward callback can arrive after\n * the original request stack, when the browser's transient user activation\n * is already gone. Retain the SDK's one-shot show function here and hand it\n * back to the game so show() itself can be invoked synchronously by the\n * player's later click/tap.\n *\n * The existing rewardedAd implementation remains the single lifecycle\n * engine: this wrapper only owns beforeReward, so callbacks, structured\n * errors, Google's breakStatus, and the viewed-only reward signal cannot\n * drift between the legacy and prepared APIs.\n */\n function prepareRewardedAd(\n rawOptions?: BBArcadePrepareRewardedAdOptions\n ): Promise<BBArcadeRewardedAdPreparation> {\n const normalized = normalizeRewardedOptions(rawOptions);\n const base: AdResultBase = {\n placement: normalized.placement,\n reward: normalized.reward,\n adBreakId: normalized.adBreakId,\n };\n if (normalized.size) base.size = normalized.size;\n\n let ready = false;\n let finalPromise: Promise<BBArcadeRewardedAdResult>;\n\n return new Promise<BBArcadeRewardedAdPreparation>(function (resolvePreparation) {\n finalPromise = rewardedAd(\n Object.assign({}, rawOptions, {\n // Preserve the id used by the ready handle and every lifecycle event;\n // rewardedAd normalizes again, but this explicit id keeps both stages\n // tied to the same placement.\n adBreakId: normalized.adBreakId,\n beforeReward: function (showAd: () => void) {\n if (ready) return;\n ready = true;\n let shown = false;\n const prepared: BBArcadePreparedRewardedAd = Object.assign(\n { status: 'ready' as const },\n base,\n {\n show: function (): Promise<BBArcadeRewardedAdResult> {\n // The first meaningful operation is synchronous. A click\n // handler can call prepared.show() without an await/microtask\n // stealing the browser's transient user activation.\n if (!shown) {\n shown = true;\n // Do not pause the game/audio when the display attempt is\n // about to fail closed for missing activation. showAd()\n // still runs so the caller receives the structured error.\n if (hasTransientUserActivation()) {\n safeCall(rawOptions && rawOptions.onStart);\n }\n showAd();\n }\n return finalPromise;\n },\n }\n );\n resolvePreparation(prepared);\n },\n })\n );\n\n finalPromise.then(function (result) {\n // No beforeReward means Google never offered an ad. Return the exact\n // structured terminal result, including error + breakStatus, rather\n // than collapsing the preparation to a boolean.\n if (!ready) resolvePreparation(result as BBArcadeRewardedAdPrepareFailure);\n });\n });\n }\n\n function preloadRewardedAds(rawOptions?: BBArcadeRewardedAdOptions): Promise<boolean> {\n const options = normalizeRewardedOptions(rawOptions);\n if (!areRewardedAdsEnabled()) return Promise.resolve(false);\n return ensureAdPlacement(options).then(function (adBreak) {\n if (typeof adBreak !== 'function') return false;\n requestAdPreload();\n return true;\n });\n }\n\n function whenHostConfig(graceMs: number): Promise<void> {\n if (hostConfigReceived || !isEmbedded()) return Promise.resolve();\n return new Promise(function (resolve) {\n const timer = setTimeout(resolve, graceMs);\n configWaiters.push(function () {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n // The player's display identity, delivered with the host's config message.\n // Waits on the same handshake (and grace) as init(), so it resolves null —\n // never hangs — for guests, standalone play, or when no host ever answers.\n function getPlayer(): Promise<BBArcadePlayer | null> {\n if (blocked) return Promise.resolve(null);\n return whenHostConfig(INIT_CONFIG_GRACE_MS).then(function () {\n // Hand out a copy so a game can't mutate the SDK's own record.\n return hostPlayer ? { name: hostPlayer.name, avatarUrl: hostPlayer.avatarUrl } : null;\n });\n }\n\n function init(options?: BBArcadeConfig): Promise<void> {\n configure(options);\n // Announce to the host. A Bounty Board host replies to init/ready with its\n // config message, which resolves this promise — so config delivery doesn't\n // depend on the host's load-time push racing the SDK attaching a listener.\n post('init');\n return whenHostConfig(INIT_CONFIG_GRACE_MS);\n }\n\n function ready(): void {\n post('ready');\n }\n\n function normalizeRewardedBreakInput(\n optionsOrOnStart?: BBArcadeRewardedBreakOptions | (() => void)\n ): BBArcadeRewardedBreakOptions {\n if (typeof optionsOrOnStart === 'function') return { onStart: optionsOrOnStart };\n return optionsOrOnStart && typeof optionsOrOnStart === 'object' ? optionsOrOnStart : {};\n }\n\n function rewardedBreak(\n optionsOrOnStart?: BBArcadeRewardedBreakOptions | (() => void)\n ): Promise<boolean> {\n const rawOptions = normalizeRewardedBreakInput(optionsOrOnStart);\n const normalized = normalizeRewardedOptions(rawOptions);\n const base: AdResultBase = {\n adBreakId: normalized.adBreakId,\n placement: normalized.placement,\n reward: normalized.reward,\n };\n if (normalized.size) base.size = normalized.size;\n\n if (rewardedBreakActive && rewardedBreakActive.state !== 'ready') {\n post(\n 'rewarded_ad',\n undefined,\n Object.assign(\n { event: 'unavailable', status: 'unavailable', error: 'ad_in_progress' },\n base\n )\n );\n return Promise.resolve(false);\n }\n\n const activeBreak: NonNullable<typeof rewardedBreakActive> = { state: 'loading' };\n rewardedBreakActive = activeBreak;\n let startCalled = false;\n const callerBeforeReward = rawOptions.beforeReward;\n return rewardedAd(\n Object.assign({}, rawOptions, {\n beforeReward: function (showAd: () => void) {\n if (rewardedBreakActive === activeBreak) activeBreak.state = 'ready';\n const showWithStart = function (): void {\n // A newer unshown rewardedBreak supersedes this retained offer.\n // Ignore a stale click so it cannot pause the game or emit a\n // misleading started event after Google has invalidated it.\n if (startCalled || rewardedBreakActive !== activeBreak) return;\n startCalled = true;\n activeBreak.state = 'showing';\n if (hasTransientUserActivation()) safeCall(rawOptions.onStart);\n showAd();\n };\n // Preserve the caller's two-stage readiness UI. Legacy callers that\n // omitted beforeReward keep their original one-call behavior, but the\n // guarded show wrapper above now fails closed if activation is gone.\n if (typeof callerBeforeReward === 'function') callerBeforeReward(showWithStart);\n else showWithStart();\n },\n })\n ).then(\n function (result) {\n if (rewardedBreakActive === activeBreak) rewardedBreakActive = null;\n return Boolean(result && result.status === 'viewed');\n },\n function () {\n if (rewardedBreakActive === activeBreak) rewardedBreakActive = null;\n return false;\n }\n );\n }\n\n window.addEventListener('message', function (event: MessageEvent) {\n // Host config must come from the embedding page itself — a null/other\n // source (nested frame, synthetic event) must not be able to flip flags.\n if (event.source !== window.parent) return;\n if (!hostnameAllowed(event.origin, DEFAULT_ALLOW)) return;\n applyHostConfig(event.data);\n });\n\n const sdk: BBArcadeSDK = {\n lockToHost: lockToHost,\n init: init,\n configure: configure,\n preloadRewardedAds: preloadRewardedAds,\n preloadRewardedAd: preloadRewardedAds,\n ready: ready,\n gameLoadingFinished: ready,\n gameplayStart: function () {\n post('gameplay_start');\n },\n gameplayStop: function () {\n post('gameplay_stop');\n },\n submitScore: function (score, opts) {\n post('score', score, opts && opts.mode === 'daily' ? { mode: 'daily' } : undefined);\n },\n gameOver: function (score, opts) {\n post('gameover', score, opts && opts.mode === 'daily' ? { mode: 'daily' } : undefined);\n },\n xrSessionStart: function () {\n post('xr_session_start');\n },\n xrSessionEnd: function () {\n post('xr_session_end');\n },\n rewardedAd: rewardedAd,\n showRewardedAd: rewardedAd,\n prepareRewardedAd: prepareRewardedAd,\n prepareRewardedBreak: prepareRewardedAd,\n rewardedBreak: rewardedBreak,\n save: function (blob) {\n return request('save', String(blob)) as Promise<void>;\n },\n load: function () {\n return request('load') as Promise<string | null>;\n },\n getPlayer: getPlayer,\n getVariant: function (key, variants) {\n const sorted = Array.isArray(variants) ? variants.slice().sort() : [];\n const fallback = sorted.length > 0 ? sorted[0] : null;\n if (typeof key !== 'string' || !key || sorted.length < 2) {\n return Promise.resolve(fallback);\n }\n return request('experiment', { key: key, variants: sorted }).then(\n function (variant) {\n return typeof variant === 'string' ? variant : fallback;\n },\n function () {\n return fallback;\n }\n );\n },\n version: VERSION,\n };\n\n // Internal, non-public hook used by the multiplayer module to ride the same\n // request/response transport (mp_ticket). Hidden behind a symbol-free\n // underscore name and excluded from BBArcadeSDK so it never becomes API.\n (sdk as BBArcadeSDK & { _request?: typeof request })._request = request;\n\n return sdk;\n}\n","/**\n * Inert BBArcade used when the module is evaluated without a `window` (SSR,\n * tests, bundler prerender). Mirrors the real SDK's standalone semantics:\n * fire-and-forget calls no-op, save/load reject fast with 'unsupported',\n * getPlayer resolves null, getVariant resolves its alphabetical control.\n */\nimport { VERSION } from './protocol';\nimport type {\n BBArcadeError,\n BBArcadeErrorCode,\n BBArcadeRewardedAdPreparation,\n BBArcadeRewardedAdResult,\n BBArcadeSDK,\n} from './types';\n\nfunction sdkError(code: BBArcadeErrorCode): BBArcadeError {\n const err = new Error(code) as BBArcadeError;\n err.code = code;\n return err;\n}\n\nexport function createInertBBArcade(): BBArcadeSDK {\n const noop = () => {};\n const unavailable = (): Promise<BBArcadeRewardedAdResult> =>\n Promise.resolve({\n status: 'unavailable',\n error: 'ad_break_unavailable',\n placement: 'rewarded',\n reward: 'reward',\n adBreakId: 'bbad_inert',\n });\n const prepareUnavailable = (): Promise<BBArcadeRewardedAdPreparation> =>\n Promise.resolve({\n status: 'unavailable',\n error: 'ad_break_unavailable',\n placement: 'rewarded',\n reward: 'reward',\n adBreakId: 'bbad_inert',\n });\n return {\n lockToHost: noop,\n init: () => Promise.resolve(),\n configure: noop,\n preloadRewardedAds: () => Promise.resolve(false),\n preloadRewardedAd: () => Promise.resolve(false),\n ready: noop,\n gameLoadingFinished: noop,\n gameplayStart: noop,\n gameplayStop: noop,\n submitScore: noop,\n gameOver: noop,\n xrSessionStart: noop,\n xrSessionEnd: noop,\n rewardedAd: unavailable,\n showRewardedAd: unavailable,\n prepareRewardedAd: prepareUnavailable,\n prepareRewardedBreak: prepareUnavailable,\n rewardedBreak: () => Promise.resolve(false),\n save: () => Promise.reject(sdkError('unsupported')),\n load: () => Promise.reject(sdkError('unsupported')),\n getPlayer: () => Promise.resolve(null),\n getVariant: (key, variants) => {\n const sorted = Array.isArray(variants) ? variants.slice().sort() : [];\n return Promise.resolve(sorted.length > 0 ? sorted[0] : null);\n },\n version: VERSION,\n };\n}\n","/**\n * @bountyboard/arcade-sdk — module entry.\n *\n * import { BBArcade } from '@bountyboard/arcade-sdk';\n *\n * Importing attaches the SDK's postMessage listeners immediately (the Bounty\n * Board host pushes its config at load time, so listener timing matters) but\n * does NOT install a window.BBArcade global — that's the script-tag build\n * (https://www.bountyboard.gg/arcade-sdk/v1.js, or the './global' subpath).\n * Evaluated without a window (SSR/prerender) it exports an inert instance with\n * standalone semantics, so importing is always safe.\n */\nimport { createBBArcade } from './core';\nimport { createInertBBArcade } from './inert';\nimport type { BBArcadeSDK } from './types';\n\nexport const BBArcade: BBArcadeSDK =\n typeof window === 'undefined' ? createInertBBArcade() : createBBArcade();\n\nexport default BBArcade;\n\nexport { VERSION as PROTOCOL_VERSION } from './protocol';\nexport type {\n BBArcadeConfig,\n BBArcadeError,\n BBArcadeErrorCode,\n BBArcadeLockToHostOptions,\n BBArcadePlayer,\n BBArcadePreparedRewardedAd,\n BBArcadePrepareRewardedAdOptions,\n BBArcadeRewardedAdOptions,\n BBArcadeRewardedAdPreparation,\n BBArcadeRewardedAdPrepareFailure,\n BBArcadeRewardedAdResult,\n BBArcadeRewardedAdStatus,\n BBArcadeRewardedAdsConfig,\n BBArcadeRewardedBreakOptions,\n BBArcadeSDK,\n} from './types';\n"],"mappings":"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","names":["ready","sdkError"]}
|
package/dist/global-sdk.d.ts
CHANGED
|
@@ -103,13 +103,19 @@ interface BBArcadeRewardedAdResult {
|
|
|
103
103
|
* Failure detail when status is 'unavailable' or 'error', e.g.
|
|
104
104
|
* 'host_disabled' | 'ad_break_unavailable' | 'ad_break_timeout' |
|
|
105
105
|
* 'no_rewarded_ad' | 'ad_in_progress' | 'show_ad_error' |
|
|
106
|
-
* '
|
|
106
|
+
* 'direct_user_action_required' | 'before_reward_error' |
|
|
107
|
+
* 'ad_break_error'.
|
|
107
108
|
*/
|
|
108
109
|
error?: string;
|
|
109
110
|
/** Google's H5 breakStatus string, when the placement reported one. */
|
|
110
111
|
breakStatus?: string;
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
/** A terminal failure returned while preparing a rewarded placement. */
|
|
115
|
+
type BBArcadeRewardedAdPrepareFailure = BBArcadeRewardedAdResult & {
|
|
116
|
+
status: 'unavailable' | 'error';
|
|
117
|
+
};
|
|
118
|
+
|
|
113
119
|
/** Options for rewardedAd()/showRewardedAd() (all optional). */
|
|
114
120
|
interface BBArcadeRewardedAdOptions {
|
|
115
121
|
/** Placement name for analytics, e.g. 'revive' (alias: name). */
|
|
@@ -120,7 +126,7 @@ interface BBArcadeRewardedAdOptions {
|
|
|
120
126
|
reward?: string;
|
|
121
127
|
/** Supply your own ad-break id instead of the generated one. */
|
|
122
128
|
adBreakId?: string;
|
|
123
|
-
/**
|
|
129
|
+
/** Legacy SDK analytics metadata; never forwarded as a Google adBreak field. */
|
|
124
130
|
size?: 'small' | 'medium' | 'large';
|
|
125
131
|
/** Legacy AdSense channel label. Ignored on Bounty Board. */
|
|
126
132
|
adChannel?: string;
|
|
@@ -129,8 +135,10 @@ interface BBArcadeRewardedAdOptions {
|
|
|
129
135
|
/** Per-call override of how long to wait for an ad, in ms (max 15000). */
|
|
130
136
|
loadTimeoutMs?: number;
|
|
131
137
|
/**
|
|
132
|
-
* Called when an ad is ready.
|
|
133
|
-
*
|
|
138
|
+
* Called when an ad is ready. Retain showAd and invoke it directly from the
|
|
139
|
+
* player's click/tap handler. An asynchronous call without active browser
|
|
140
|
+
* user activation fails with 'direct_user_action_required'. Prefer
|
|
141
|
+
* prepareRewardedAd() for the typed two-stage flow.
|
|
134
142
|
*/
|
|
135
143
|
beforeReward?: (showAd: () => void) => void;
|
|
136
144
|
/** The player watched the ad to completion — grant the reward. */
|
|
@@ -151,6 +159,38 @@ interface BBArcadeRewardedAdOptions {
|
|
|
151
159
|
onUnavailable?: (result: BBArcadeRewardedAdResult) => void;
|
|
152
160
|
}
|
|
153
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Options for prepareRewardedAd(). The preparation owns Google's beforeReward
|
|
164
|
+
* callback so it can retain showAdFn safely; onStart runs synchronously inside
|
|
165
|
+
* the returned handle's show() method.
|
|
166
|
+
*/
|
|
167
|
+
type BBArcadePrepareRewardedAdOptions = Omit<BBArcadeRewardedAdOptions, 'beforeReward'> & {
|
|
168
|
+
/** Called synchronously immediately before Google's retained showAdFn. */
|
|
169
|
+
onStart?: () => void;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* A rewarded placement Google has made available. Call show() directly from
|
|
174
|
+
* the player's click/tap handler; do not await or schedule other work first.
|
|
175
|
+
*/
|
|
176
|
+
interface BBArcadePreparedRewardedAd {
|
|
177
|
+
status: 'ready';
|
|
178
|
+
placement: string;
|
|
179
|
+
reward: string;
|
|
180
|
+
adBreakId: string;
|
|
181
|
+
size?: 'small' | 'medium' | 'large';
|
|
182
|
+
/**
|
|
183
|
+
* One-shot display call. Invokes Google's retained showAdFn synchronously,
|
|
184
|
+
* then resolves with the final structured outcome. Only 'viewed' grants.
|
|
185
|
+
*/
|
|
186
|
+
show(): Promise<BBArcadeRewardedAdResult>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Result of prepareRewardedAd(): a ready handle or a terminal failure. */
|
|
190
|
+
type BBArcadeRewardedAdPreparation =
|
|
191
|
+
| BBArcadePreparedRewardedAd
|
|
192
|
+
| BBArcadeRewardedAdPrepareFailure;
|
|
193
|
+
|
|
154
194
|
/** Options for rewardedBreak(): everything rewardedAd() takes, plus onStart. */
|
|
155
195
|
interface BBArcadeRewardedBreakOptions extends BBArcadeRewardedAdOptions {
|
|
156
196
|
/** Called once right before the ad shows — pause gameplay/audio here. */
|
|
@@ -283,17 +323,32 @@ interface BBArcadeSDK {
|
|
|
283
323
|
/** Signal that the immersive WebXR session ended. */
|
|
284
324
|
xrSessionEnd(): void;
|
|
285
325
|
/**
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
326
|
+
* Low-level rewarded placement API. Resolves with the full result object;
|
|
327
|
+
* only status 'viewed' should grant. Prefer prepareRewardedAd() for new
|
|
328
|
+
* integrations so Google's show function stays in the player's click stack.
|
|
289
329
|
*/
|
|
290
330
|
rewardedAd(options?: BBArcadeRewardedAdOptions): Promise<BBArcadeRewardedAdResult>;
|
|
291
331
|
/** Alias of rewardedAd. */
|
|
292
332
|
showRewardedAd(options?: BBArcadeRewardedAdOptions): Promise<BBArcadeRewardedAdResult>;
|
|
293
333
|
/**
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
334
|
+
* Safely prepare a rewarded placement without showing it. When status is
|
|
335
|
+
* 'ready', call result.show() directly from the player's click/tap handler;
|
|
336
|
+
* it resolves with the full final result and only 'viewed' should grant.
|
|
337
|
+
*/
|
|
338
|
+
prepareRewardedAd(
|
|
339
|
+
options?: BBArcadePrepareRewardedAdOptions
|
|
340
|
+
): Promise<BBArcadeRewardedAdPreparation>;
|
|
341
|
+
/** Alias of prepareRewardedAd. */
|
|
342
|
+
prepareRewardedBreak(
|
|
343
|
+
options?: BBArcadePrepareRewardedAdOptions
|
|
344
|
+
): Promise<BBArcadeRewardedAdPreparation>;
|
|
345
|
+
/**
|
|
346
|
+
* Legacy one-call rewarded break. Resolves true only when the ad was viewed.
|
|
347
|
+
*
|
|
348
|
+
* @deprecated Use prepareRewardedAd(), then call the ready handle's show()
|
|
349
|
+
* directly from the player's click/tap handler. Google's beforeReward may be
|
|
350
|
+
* asynchronous, so this convenience call cannot preserve user activation in
|
|
351
|
+
* every browser.
|
|
297
352
|
*/
|
|
298
353
|
rewardedBreak(optionsOrOnStart?: BBArcadeRewardedBreakOptions | (() => void)): Promise<boolean>;
|
|
299
354
|
/**
|