@bountyboard/arcade-sdk 1.0.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.
@@ -0,0 +1,805 @@
1
+ // src/protocol.ts
2
+ var VERSION = 1;
3
+ var SDK_MESSAGE_SOURCE = "bb-arcade";
4
+ var HOST_MESSAGE_SOURCE = "bb-arcade-host";
5
+
6
+ // src/core.ts
7
+ var BOUNTY_BOARD_URL = "https://www.bountyboard.gg/arcade";
8
+ var ADSENSE_CLIENT_ID = "ca-pub-7727110228190547";
9
+ var AD_PLACEMENT_SRC = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=" + ADSENSE_CLIENT_ID;
10
+ var AD_PLACEMENT_SCRIPT_ID = "bb-arcade-h5-ads";
11
+ var DEFAULT_AD_LOAD_TIMEOUT_MS = 6e3;
12
+ var SAVE_TIMEOUT_MS = 15e3;
13
+ var INIT_CONFIG_GRACE_MS = 1500;
14
+ var DEFAULT_ALLOW = [
15
+ "bountyboard.gg",
16
+ "bountyboard-staging.vercel.app",
17
+ "localhost",
18
+ "127.0.0.1"
19
+ ];
20
+ var SITELOCK_PUBLIC_KEY_B64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6WC8fJweDpfKRcZW9Q9eOLxPXmFCO8R8lY1kfZbuAQt65E0CTu2rM80XfW3nYrSAqO+Eev8gxrGS0bAnNaMDGg==";
21
+ var SITELOCK_MAX_SKEW_MS = 5 * 60 * 1e3;
22
+ function sdkError(code) {
23
+ const err = new Error(code);
24
+ err.code = code;
25
+ return err;
26
+ }
27
+ function b64ToBytes(b64) {
28
+ const bin = atob(b64);
29
+ const bytes = new Uint8Array(bin.length);
30
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
31
+ return bytes;
32
+ }
33
+ function cleanText(value, fallback, max) {
34
+ if (typeof value !== "string") return fallback || "";
35
+ return value.replace(/[^\w:.-]+/g, "_").slice(0, max || 80) || fallback || "";
36
+ }
37
+ function toBoolean(value) {
38
+ return value === true || value === "true" || value === "1";
39
+ }
40
+ function cleanRewardedSize(value) {
41
+ return value === "small" || value === "medium" || value === "large" ? value : "";
42
+ }
43
+ function safeCall(fn, arg) {
44
+ if (typeof fn !== "function") return;
45
+ try {
46
+ fn(arg);
47
+ } catch (err) {
48
+ setTimeout(function() {
49
+ throw err;
50
+ }, 0);
51
+ }
52
+ }
53
+ function toAllowedHost(value) {
54
+ const raw = String(value).toLowerCase().trim();
55
+ if (!raw) return "";
56
+ try {
57
+ return new URL(raw.indexOf("//") === -1 ? "https://" + raw : raw).hostname;
58
+ } catch (err) {
59
+ return raw;
60
+ }
61
+ }
62
+ function hostnameAllowed(origin, allowHosts) {
63
+ if (!origin || origin === "null" || typeof origin !== "string") return false;
64
+ let host;
65
+ try {
66
+ host = new URL(origin).hostname.toLowerCase();
67
+ } catch (err) {
68
+ return false;
69
+ }
70
+ for (let i = 0; i < allowHosts.length; i++) {
71
+ const allowed = allowHosts[i];
72
+ if (host === allowed || host.slice(-(allowed.length + 1)) === "." + allowed) return true;
73
+ }
74
+ return false;
75
+ }
76
+ function verifyHostAttestation(att, senderOrigin) {
77
+ const a = att;
78
+ if (!a || typeof a.origin !== "string" || typeof a.ts !== "number" || typeof a.sig !== "string")
79
+ return Promise.resolve(false);
80
+ if (a.origin !== senderOrigin) return Promise.resolve(false);
81
+ if (Math.abs(Date.now() - a.ts) > SITELOCK_MAX_SKEW_MS) return Promise.resolve(false);
82
+ try {
83
+ return window.crypto.subtle.importKey(
84
+ "spki",
85
+ b64ToBytes(SITELOCK_PUBLIC_KEY_B64),
86
+ { name: "ECDSA", namedCurve: "P-256" },
87
+ false,
88
+ ["verify"]
89
+ ).then(function(key) {
90
+ return window.crypto.subtle.verify(
91
+ { name: "ECDSA", hash: "SHA-256" },
92
+ key,
93
+ b64ToBytes(a.sig),
94
+ new TextEncoder().encode(a.origin + ":" + a.ts)
95
+ );
96
+ }).then(
97
+ function(ok) {
98
+ return ok === true;
99
+ },
100
+ function() {
101
+ return false;
102
+ }
103
+ );
104
+ } catch (err) {
105
+ return Promise.resolve(false);
106
+ }
107
+ }
108
+ function sanitizePlayer(value) {
109
+ if (!value || typeof value !== "object") return null;
110
+ const raw = value;
111
+ const name = typeof raw.name === "string" ? raw.name.trim().slice(0, 120) : "";
112
+ if (!name) return null;
113
+ const avatarUrl = typeof raw.avatarUrl === "string" && raw.avatarUrl.trim() ? raw.avatarUrl : null;
114
+ return { name, avatarUrl };
115
+ }
116
+ function createBBArcade() {
117
+ let adPlacementLoad = null;
118
+ let adPreloadConfigured = false;
119
+ let rewardedBreakActive = false;
120
+ let rewardedBreakSequence = 0;
121
+ function isLocalAdHost() {
122
+ try {
123
+ const protocol = window.location && window.location.protocol;
124
+ const host = window.location && window.location.hostname;
125
+ return protocol === "file:" || host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
126
+ } catch (err) {
127
+ return false;
128
+ }
129
+ }
130
+ const localAdHost = isLocalAdHost();
131
+ const config = {
132
+ rewardedAds: {
133
+ enabled: true,
134
+ adChannel: "",
135
+ testMode: localAdHost,
136
+ loadTimeoutMs: DEFAULT_AD_LOAD_TIMEOUT_MS,
137
+ preload: false
138
+ }
139
+ };
140
+ const hostConfig = {
141
+ rewardedAds: {
142
+ received: false,
143
+ enabled: localAdHost,
144
+ adChannel: "",
145
+ testMode: localAdHost,
146
+ preload: false
147
+ }
148
+ };
149
+ let hostConfigReceived = false;
150
+ let hostPlayer = null;
151
+ let blocked = false;
152
+ const pending = {};
153
+ let seq = 0;
154
+ function post(type, score, extra) {
155
+ if (blocked) return;
156
+ const message = { source: SDK_MESSAGE_SOURCE, v: VERSION, type };
157
+ if (typeof score === "number" && isFinite(score)) message.score = Math.floor(score);
158
+ if (extra && typeof extra === "object") {
159
+ const fields = extra;
160
+ for (const key in fields) {
161
+ if (Object.prototype.hasOwnProperty.call(fields, key) && fields[key] !== void 0) {
162
+ message[key] = fields[key];
163
+ }
164
+ }
165
+ }
166
+ try {
167
+ window.parent.postMessage(message, "*");
168
+ } catch (err) {
169
+ }
170
+ }
171
+ function isEmbedded() {
172
+ try {
173
+ return window.parent != null && window.parent !== window;
174
+ } catch (err) {
175
+ return true;
176
+ }
177
+ }
178
+ function request(type, payload) {
179
+ return new Promise(function(resolve, reject) {
180
+ if (blocked) return reject(sdkError("error"));
181
+ if (!isEmbedded()) return reject(sdkError("unsupported"));
182
+ const requestId = type + "_" + ++seq + "_" + Date.now();
183
+ const timer = setTimeout(function() {
184
+ if (pending[requestId]) {
185
+ delete pending[requestId];
186
+ reject(sdkError("error"));
187
+ }
188
+ }, SAVE_TIMEOUT_MS);
189
+ pending[requestId] = { resolve, reject, timer };
190
+ try {
191
+ window.parent.postMessage(
192
+ {
193
+ source: SDK_MESSAGE_SOURCE,
194
+ v: VERSION,
195
+ type,
196
+ requestId,
197
+ payload
198
+ },
199
+ "*"
200
+ );
201
+ } catch (err) {
202
+ clearTimeout(timer);
203
+ delete pending[requestId];
204
+ reject(sdkError("error"));
205
+ }
206
+ });
207
+ }
208
+ window.addEventListener("message", function(event) {
209
+ if (event.source !== window.parent) return;
210
+ const data = event.data;
211
+ if (!data || data.source !== SDK_MESSAGE_SOURCE) return;
212
+ if (typeof data.type !== "string" || !data.type.endsWith("_result")) return;
213
+ const entry = pending[data.requestId];
214
+ if (!entry) return;
215
+ clearTimeout(entry.timer);
216
+ delete pending[data.requestId];
217
+ if (data.type === "host_result") return entry.resolve({ origin: event.origin });
218
+ if (data.type === "host_attest_result")
219
+ return entry.resolve({ origin: event.origin, attestation: data.attestation || null });
220
+ if (data.type === "experiment_result")
221
+ return entry.resolve(typeof data.variant === "string" ? data.variant : null);
222
+ if (data.ok)
223
+ entry.resolve(
224
+ data.type === "load_result" ? data.data != null ? data.data : null : data.data
225
+ );
226
+ else entry.reject(sdkError(data.code || "error"));
227
+ });
228
+ function embeddingOriginSync() {
229
+ try {
230
+ if (window.top === window.self) return window.location.origin;
231
+ } catch (err) {
232
+ }
233
+ try {
234
+ const ancestors = window.location.ancestorOrigins;
235
+ if (ancestors && ancestors.length && ancestors[0] && ancestors[0] !== "null")
236
+ return ancestors[0];
237
+ } catch (err) {
238
+ }
239
+ if (document.referrer) {
240
+ try {
241
+ return new URL(document.referrer).origin;
242
+ } catch (err) {
243
+ }
244
+ }
245
+ return null;
246
+ }
247
+ function blockHost(options) {
248
+ if (blocked) return;
249
+ blocked = true;
250
+ if (typeof options.onBlocked === "function") {
251
+ try {
252
+ options.onBlocked();
253
+ } catch (err) {
254
+ }
255
+ return;
256
+ }
257
+ if (options.redirect) {
258
+ try {
259
+ window.top.location.href = options.redirect;
260
+ return;
261
+ } catch (err) {
262
+ try {
263
+ window.location.href = options.redirect;
264
+ return;
265
+ } catch (err2) {
266
+ }
267
+ }
268
+ }
269
+ try {
270
+ document.documentElement.innerHTML = `<div style="position:fixed;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;font-family:system-ui,-apple-system,sans-serif;background:#1a1206;color:#fff;text-align:center;padding:24px;"><div style="font-size:18px;font-weight:700;">This game isn't available here</div><a href="` + BOUNTY_BOARD_URL + '" target="_blank" rel="noopener" style="display:inline-block;background:#f5b942;color:#1a1206;font-weight:700;font-size:14px;text-decoration:none;padding:10px 20px;border-radius:999px;">Play the original on Bounty Board</a></div>';
271
+ } catch (err) {
272
+ }
273
+ }
274
+ function lockToHostBestEffort(options, allow) {
275
+ const origin = embeddingOriginSync();
276
+ if (origin != null) {
277
+ if (!hostnameAllowed(origin, allow)) blockHost(options);
278
+ return;
279
+ }
280
+ request("host").then(
281
+ function(res) {
282
+ const r = res;
283
+ if (!hostnameAllowed(r && r.origin, allow)) blockHost(options);
284
+ },
285
+ function() {
286
+ blockHost(options);
287
+ }
288
+ );
289
+ }
290
+ function lockToHost(options) {
291
+ const opts = options || {};
292
+ const allow = DEFAULT_ALLOW.slice();
293
+ if (opts.allow && opts.allow.length) {
294
+ for (let i = 0; i < opts.allow.length; i++) {
295
+ const host = toAllowedHost(opts.allow[i]);
296
+ if (host) allow.push(host);
297
+ }
298
+ }
299
+ if (opts.signed) {
300
+ if (!(window.crypto && window.crypto.subtle)) {
301
+ lockToHostBestEffort(opts, allow);
302
+ return;
303
+ }
304
+ request("host_attest").then(
305
+ function(res) {
306
+ const r = res;
307
+ if (!r || !r.attestation) {
308
+ lockToHostBestEffort(opts, allow);
309
+ return;
310
+ }
311
+ verifyHostAttestation(r.attestation, r.origin).then(function(ok) {
312
+ if (!ok || !hostnameAllowed(r.attestation && r.attestation.origin, allow))
313
+ blockHost(opts);
314
+ });
315
+ },
316
+ function() {
317
+ blockHost(opts);
318
+ }
319
+ );
320
+ return;
321
+ }
322
+ lockToHostBestEffort(opts, allow);
323
+ }
324
+ function nextAdBreakId() {
325
+ rewardedBreakSequence += 1;
326
+ return [
327
+ "bbad",
328
+ Date.now().toString(36),
329
+ rewardedBreakSequence.toString(36),
330
+ Math.random().toString(36).slice(2, 10)
331
+ ].join("_");
332
+ }
333
+ function configure(nextConfig) {
334
+ if (!nextConfig || typeof nextConfig !== "object") return;
335
+ const rewardedAds = nextConfig.rewardedAds;
336
+ if (!rewardedAds || typeof rewardedAds !== "object") return;
337
+ if (rewardedAds.enabled !== void 0) {
338
+ config.rewardedAds.enabled = rewardedAds.enabled !== false;
339
+ }
340
+ if (rewardedAds.adChannel !== void 0) {
341
+ config.rewardedAds.adChannel = cleanText(rewardedAds.adChannel);
342
+ }
343
+ if (rewardedAds.testMode !== void 0) {
344
+ config.rewardedAds.testMode = toBoolean(rewardedAds.testMode);
345
+ }
346
+ if (rewardedAds.preload !== void 0) {
347
+ config.rewardedAds.preload = toBoolean(rewardedAds.preload);
348
+ }
349
+ if (typeof rewardedAds.loadTimeoutMs === "number" && isFinite(rewardedAds.loadTimeoutMs) && rewardedAds.loadTimeoutMs > 0) {
350
+ config.rewardedAds.loadTimeoutMs = Math.min(15e3, Math.floor(rewardedAds.loadTimeoutMs));
351
+ }
352
+ if (config.rewardedAds.preload) preloadRewardedAds();
353
+ }
354
+ let configWaiters = [];
355
+ function notifyConfigReceived() {
356
+ const waiters = configWaiters;
357
+ configWaiters = [];
358
+ for (let i = 0; i < waiters.length; i++) safeCall(waiters[i]);
359
+ }
360
+ function applyHostConfig(message) {
361
+ if (!message || typeof message !== "object") return;
362
+ const msg = message;
363
+ if (msg.source !== HOST_MESSAGE_SOURCE || msg.type !== "config") return;
364
+ hostConfigReceived = true;
365
+ if (msg.player !== void 0) {
366
+ hostPlayer = sanitizePlayer(msg.player);
367
+ }
368
+ const rewardedAds = msg.rewardedAds;
369
+ if (rewardedAds && typeof rewardedAds === "object") {
370
+ hostConfig.rewardedAds.received = true;
371
+ if (rewardedAds.enabled !== void 0) {
372
+ hostConfig.rewardedAds.enabled = toBoolean(rewardedAds.enabled);
373
+ }
374
+ if (rewardedAds.adChannel !== void 0) {
375
+ hostConfig.rewardedAds.adChannel = cleanText(rewardedAds.adChannel);
376
+ }
377
+ if (rewardedAds.testMode !== void 0) {
378
+ hostConfig.rewardedAds.testMode = toBoolean(rewardedAds.testMode);
379
+ }
380
+ if (rewardedAds.preload !== void 0) {
381
+ hostConfig.rewardedAds.preload = toBoolean(rewardedAds.preload);
382
+ }
383
+ if (hostConfig.rewardedAds.enabled && (hostConfig.rewardedAds.preload || config.rewardedAds.preload)) {
384
+ preloadRewardedAds();
385
+ }
386
+ }
387
+ notifyConfigReceived();
388
+ }
389
+ function areRewardedAdsEnabled() {
390
+ return config.rewardedAds.enabled !== false && hostConfig.rewardedAds.enabled === true;
391
+ }
392
+ function installAdPlacementShim() {
393
+ const w = window;
394
+ w.adsbygoogle = w.adsbygoogle || [];
395
+ if (typeof w.adBreak !== "function") {
396
+ w.adBreak = function(placement) {
397
+ w.adsbygoogle.push(placement);
398
+ };
399
+ }
400
+ if (typeof w.adConfig !== "function") {
401
+ w.adConfig = function(settings) {
402
+ w.adsbygoogle.push(settings);
403
+ };
404
+ }
405
+ }
406
+ function ensureAdPlacement(options) {
407
+ if (typeof document === "undefined") return Promise.resolve(null);
408
+ installAdPlacementShim();
409
+ const w = window;
410
+ if (typeof w.adBreak !== "function") return Promise.resolve(null);
411
+ if (adPlacementLoad) return adPlacementLoad;
412
+ adPlacementLoad = new Promise(function(resolve) {
413
+ let script = document.getElementById(AD_PLACEMENT_SCRIPT_ID);
414
+ if (!script) {
415
+ const el = document.createElement("script");
416
+ el.id = AD_PLACEMENT_SCRIPT_ID;
417
+ el.async = true;
418
+ el.src = AD_PLACEMENT_SRC;
419
+ el.crossOrigin = "anonymous";
420
+ el.setAttribute("data-ad-client", ADSENSE_CLIENT_ID);
421
+ if (options.adChannel) el.setAttribute("data-ad-channel", options.adChannel);
422
+ if (options.testMode) el.setAttribute("data-adbreak-test", "on");
423
+ (document.head || document.documentElement).appendChild(el);
424
+ script = el;
425
+ }
426
+ resolve(w.adBreak);
427
+ });
428
+ return adPlacementLoad;
429
+ }
430
+ function requestAdPreload() {
431
+ const w = window;
432
+ if (adPreloadConfigured || typeof w.adConfig !== "function") return;
433
+ adPreloadConfigured = true;
434
+ try {
435
+ w.adConfig({ preloadAdBreaks: "on" });
436
+ } catch (err) {
437
+ }
438
+ }
439
+ function normalizeRewardedOptions(options) {
440
+ const opts = options && typeof options === "object" ? options : {};
441
+ const hostHasConfig = hostConfig.rewardedAds.received;
442
+ return {
443
+ placement: cleanText(opts.placement || opts.name, "rewarded"),
444
+ reward: cleanText(opts.reward, "reward"),
445
+ adBreakId: cleanText(opts.adBreakId, nextAdBreakId(), 128),
446
+ size: cleanRewardedSize(opts.size),
447
+ adChannel: cleanText(
448
+ hostHasConfig ? hostConfig.rewardedAds.adChannel : opts.adChannel || config.rewardedAds.adChannel
449
+ ),
450
+ testMode: hostHasConfig ? hostConfig.rewardedAds.testMode : opts.testMode !== void 0 ? toBoolean(opts.testMode) : config.rewardedAds.testMode || hostConfig.rewardedAds.testMode,
451
+ loadTimeoutMs: typeof opts.loadTimeoutMs === "number" && isFinite(opts.loadTimeoutMs) ? Math.min(15e3, Math.max(1, Math.floor(opts.loadTimeoutMs))) : config.rewardedAds.loadTimeoutMs,
452
+ beforeReward: opts.beforeReward,
453
+ adViewed: opts.adViewed,
454
+ adDismissed: opts.adDismissed,
455
+ adBreakDone: opts.adBreakDone,
456
+ onReward: opts.onReward,
457
+ onDismissed: opts.onDismissed,
458
+ onDone: opts.onDone,
459
+ onError: opts.onError,
460
+ onUnavailable: opts.onUnavailable
461
+ };
462
+ }
463
+ function rewardedAd(rawOptions) {
464
+ const options = normalizeRewardedOptions(rawOptions);
465
+ const base = {
466
+ placement: options.placement,
467
+ reward: options.reward,
468
+ adBreakId: options.adBreakId
469
+ };
470
+ if (options.size) base.size = options.size;
471
+ post("rewarded_ad", void 0, Object.assign({ event: "requested" }, base));
472
+ if (!areRewardedAdsEnabled()) {
473
+ const disabled = Object.assign(
474
+ { status: "unavailable", error: "host_disabled" },
475
+ base
476
+ );
477
+ post("rewarded_ad", void 0, Object.assign({ event: "unavailable" }, disabled));
478
+ safeCall(options.onUnavailable, disabled);
479
+ safeCall(options.onDone, disabled);
480
+ return Promise.resolve(disabled);
481
+ }
482
+ return ensureAdPlacement(options).then(function(adBreak) {
483
+ if (typeof adBreak !== "function") {
484
+ const unavailable = Object.assign(
485
+ { status: "unavailable", error: "ad_break_unavailable" },
486
+ base
487
+ );
488
+ post("rewarded_ad", void 0, Object.assign({ event: "unavailable" }, unavailable));
489
+ safeCall(options.onUnavailable, unavailable);
490
+ safeCall(options.onDone, unavailable);
491
+ return unavailable;
492
+ }
493
+ requestAdPreload();
494
+ return new Promise(function(resolve) {
495
+ let resolved = false;
496
+ let status = "unavailable";
497
+ let rewardOffered = false;
498
+ const waitTimer = setTimeout(function() {
499
+ if (rewardOffered || resolved) return;
500
+ const timeout = Object.assign(
501
+ { status: "unavailable", error: "ad_break_timeout" },
502
+ base
503
+ );
504
+ post("rewarded_ad", void 0, Object.assign({ event: "unavailable" }, timeout));
505
+ safeCall(options.onUnavailable, timeout);
506
+ done(timeout);
507
+ }, options.loadTimeoutMs);
508
+ function done(result) {
509
+ if (resolved) return;
510
+ resolved = true;
511
+ clearTimeout(waitTimer);
512
+ const finalResult = Object.assign(
513
+ { status: "unavailable" },
514
+ base,
515
+ result || {}
516
+ );
517
+ post("rewarded_ad", void 0, Object.assign({ event: "done" }, finalResult));
518
+ safeCall(options.onDone, finalResult);
519
+ resolve(finalResult);
520
+ }
521
+ function startAd(showAdFn) {
522
+ if (resolved) return;
523
+ rewardOffered = true;
524
+ clearTimeout(waitTimer);
525
+ status = "ready";
526
+ let shown = false;
527
+ function show() {
528
+ if (shown) return;
529
+ shown = true;
530
+ post("rewarded_ad", void 0, Object.assign({ event: "started" }, base));
531
+ try {
532
+ showAdFn();
533
+ } catch (err) {
534
+ const showError = Object.assign(
535
+ { status: "error", error: "show_ad_error" },
536
+ base
537
+ );
538
+ post("rewarded_ad", void 0, Object.assign({ event: "error" }, showError));
539
+ safeCall(options.onError, showError);
540
+ done(showError);
541
+ }
542
+ }
543
+ post("rewarded_ad", void 0, Object.assign({ event: "ready" }, base));
544
+ try {
545
+ if (typeof options.beforeReward === "function") options.beforeReward(show);
546
+ else show();
547
+ } catch (err) {
548
+ const beforeError = Object.assign(
549
+ { status: "error", error: "before_reward_error" },
550
+ base
551
+ );
552
+ post("rewarded_ad", void 0, Object.assign({ event: "error" }, beforeError));
553
+ safeCall(options.onError, beforeError);
554
+ done(beforeError);
555
+ }
556
+ }
557
+ try {
558
+ adBreak({
559
+ type: "reward",
560
+ name: options.placement,
561
+ size: options.size || void 0,
562
+ beforeReward: startAd,
563
+ adViewed: function() {
564
+ status = "viewed";
565
+ const viewed = Object.assign({ status }, base);
566
+ post("rewarded_ad", void 0, Object.assign({ event: "viewed" }, viewed));
567
+ safeCall(options.adViewed, viewed);
568
+ safeCall(options.onReward, viewed);
569
+ },
570
+ adDismissed: function() {
571
+ if (status !== "viewed") status = "dismissed";
572
+ const dismissed = Object.assign({ status }, base);
573
+ post("rewarded_ad", void 0, Object.assign({ event: "dismissed" }, dismissed));
574
+ safeCall(options.adDismissed, dismissed);
575
+ safeCall(options.onDismissed, dismissed);
576
+ },
577
+ adBreakDone: function(placementInfo) {
578
+ const result = rewardOffered || status !== "unavailable" ? { status } : { status: "unavailable", error: "no_rewarded_ad" };
579
+ if (placementInfo && typeof placementInfo === "object") {
580
+ result.breakStatus = cleanText(
581
+ placementInfo.breakStatus
582
+ );
583
+ }
584
+ if (result.status === "unavailable") {
585
+ post(
586
+ "rewarded_ad",
587
+ void 0,
588
+ Object.assign({ event: "unavailable" }, base, result)
589
+ );
590
+ safeCall(
591
+ options.onUnavailable,
592
+ Object.assign({ status: "unavailable" }, base, result)
593
+ );
594
+ }
595
+ safeCall(options.adBreakDone, placementInfo);
596
+ done(result);
597
+ }
598
+ });
599
+ } catch (err) {
600
+ const error = Object.assign(
601
+ { status: "error", error: "ad_break_error" },
602
+ base
603
+ );
604
+ post("rewarded_ad", void 0, Object.assign({ event: "error" }, error));
605
+ safeCall(options.onError, error);
606
+ done(error);
607
+ }
608
+ });
609
+ });
610
+ }
611
+ function preloadRewardedAds(rawOptions) {
612
+ const options = normalizeRewardedOptions(rawOptions);
613
+ if (!areRewardedAdsEnabled()) return Promise.resolve(false);
614
+ return ensureAdPlacement(options).then(function(adBreak) {
615
+ if (typeof adBreak !== "function") return false;
616
+ requestAdPreload();
617
+ return true;
618
+ });
619
+ }
620
+ function whenHostConfig(graceMs) {
621
+ if (hostConfigReceived || !isEmbedded()) return Promise.resolve();
622
+ return new Promise(function(resolve) {
623
+ const timer = setTimeout(resolve, graceMs);
624
+ configWaiters.push(function() {
625
+ clearTimeout(timer);
626
+ resolve();
627
+ });
628
+ });
629
+ }
630
+ function getPlayer() {
631
+ if (blocked) return Promise.resolve(null);
632
+ return whenHostConfig(INIT_CONFIG_GRACE_MS).then(function() {
633
+ return hostPlayer ? { name: hostPlayer.name, avatarUrl: hostPlayer.avatarUrl } : null;
634
+ });
635
+ }
636
+ function init(options) {
637
+ configure(options);
638
+ post("init");
639
+ return whenHostConfig(INIT_CONFIG_GRACE_MS);
640
+ }
641
+ function ready() {
642
+ post("ready");
643
+ }
644
+ function normalizeRewardedBreakInput(optionsOrOnStart) {
645
+ if (typeof optionsOrOnStart === "function") return { onStart: optionsOrOnStart };
646
+ return optionsOrOnStart && typeof optionsOrOnStart === "object" ? optionsOrOnStart : {};
647
+ }
648
+ function rewardedBreak(optionsOrOnStart) {
649
+ const rawOptions = normalizeRewardedBreakInput(optionsOrOnStart);
650
+ const normalized = normalizeRewardedOptions(rawOptions);
651
+ const base = {
652
+ adBreakId: normalized.adBreakId,
653
+ placement: normalized.placement,
654
+ reward: normalized.reward
655
+ };
656
+ if (normalized.size) base.size = normalized.size;
657
+ if (rewardedBreakActive) {
658
+ post(
659
+ "rewarded_ad",
660
+ void 0,
661
+ Object.assign(
662
+ { event: "unavailable", status: "unavailable", error: "ad_in_progress" },
663
+ base
664
+ )
665
+ );
666
+ return Promise.resolve(false);
667
+ }
668
+ rewardedBreakActive = true;
669
+ let startCalled = false;
670
+ return rewardedAd(
671
+ Object.assign({}, rawOptions, {
672
+ beforeReward: function(showAd) {
673
+ if (startCalled) return;
674
+ startCalled = true;
675
+ safeCall(rawOptions.onStart);
676
+ showAd();
677
+ }
678
+ })
679
+ ).then(
680
+ function(result) {
681
+ rewardedBreakActive = false;
682
+ return Boolean(result && result.status === "viewed");
683
+ },
684
+ function() {
685
+ rewardedBreakActive = false;
686
+ return false;
687
+ }
688
+ );
689
+ }
690
+ window.addEventListener("message", function(event) {
691
+ if (event.source !== window.parent) return;
692
+ if (!hostnameAllowed(event.origin, DEFAULT_ALLOW)) return;
693
+ applyHostConfig(event.data);
694
+ });
695
+ const sdk = {
696
+ lockToHost,
697
+ init,
698
+ configure,
699
+ preloadRewardedAds,
700
+ preloadRewardedAd: preloadRewardedAds,
701
+ ready,
702
+ gameLoadingFinished: ready,
703
+ gameplayStart: function() {
704
+ post("gameplay_start");
705
+ },
706
+ gameplayStop: function() {
707
+ post("gameplay_stop");
708
+ },
709
+ submitScore: function(score, opts) {
710
+ post("score", score, opts && opts.mode === "daily" ? { mode: "daily" } : void 0);
711
+ },
712
+ gameOver: function(score, opts) {
713
+ post("gameover", score, opts && opts.mode === "daily" ? { mode: "daily" } : void 0);
714
+ },
715
+ xrSessionStart: function() {
716
+ post("xr_session_start");
717
+ },
718
+ xrSessionEnd: function() {
719
+ post("xr_session_end");
720
+ },
721
+ rewardedAd,
722
+ showRewardedAd: rewardedAd,
723
+ rewardedBreak,
724
+ save: function(blob) {
725
+ return request("save", String(blob));
726
+ },
727
+ load: function() {
728
+ return request("load");
729
+ },
730
+ getPlayer,
731
+ getVariant: function(key, variants) {
732
+ const sorted = Array.isArray(variants) ? variants.slice().sort() : [];
733
+ const fallback = sorted.length > 0 ? sorted[0] : null;
734
+ if (typeof key !== "string" || !key || sorted.length < 2) {
735
+ return Promise.resolve(fallback);
736
+ }
737
+ return request("experiment", { key, variants: sorted }).then(
738
+ function(variant) {
739
+ return typeof variant === "string" ? variant : fallback;
740
+ },
741
+ function() {
742
+ return fallback;
743
+ }
744
+ );
745
+ },
746
+ version: VERSION
747
+ };
748
+ sdk._request = request;
749
+ return sdk;
750
+ }
751
+
752
+ // src/inert.ts
753
+ function sdkError2(code) {
754
+ const err = new Error(code);
755
+ err.code = code;
756
+ return err;
757
+ }
758
+ function createInertBBArcade() {
759
+ const noop = () => {
760
+ };
761
+ const unavailable = () => Promise.resolve({
762
+ status: "unavailable",
763
+ error: "ad_break_unavailable",
764
+ placement: "rewarded",
765
+ reward: "reward",
766
+ adBreakId: "bbad_inert"
767
+ });
768
+ return {
769
+ lockToHost: noop,
770
+ init: () => Promise.resolve(),
771
+ configure: noop,
772
+ preloadRewardedAds: () => Promise.resolve(false),
773
+ preloadRewardedAd: () => Promise.resolve(false),
774
+ ready: noop,
775
+ gameLoadingFinished: noop,
776
+ gameplayStart: noop,
777
+ gameplayStop: noop,
778
+ submitScore: noop,
779
+ gameOver: noop,
780
+ xrSessionStart: noop,
781
+ xrSessionEnd: noop,
782
+ rewardedAd: unavailable,
783
+ showRewardedAd: unavailable,
784
+ rewardedBreak: () => Promise.resolve(false),
785
+ save: () => Promise.reject(sdkError2("unsupported")),
786
+ load: () => Promise.reject(sdkError2("unsupported")),
787
+ getPlayer: () => Promise.resolve(null),
788
+ getVariant: (key, variants) => {
789
+ const sorted = Array.isArray(variants) ? variants.slice().sort() : [];
790
+ return Promise.resolve(sorted.length > 0 ? sorted[0] : null);
791
+ },
792
+ version: VERSION
793
+ };
794
+ }
795
+
796
+ // src/index.ts
797
+ var BBArcade = typeof window === "undefined" ? createInertBBArcade() : createBBArcade();
798
+ var src_default = BBArcade;
799
+
800
+ export {
801
+ VERSION,
802
+ BBArcade,
803
+ src_default
804
+ };
805
+ //# sourceMappingURL=chunk-WCGBR3MI.js.map