@bountyboard/arcade-sdk 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +22 -10
- package/CHANGELOG.md +73 -0
- package/README.md +378 -85
- package/dist/{chunk-OWOESH4X.js → chunk-PYW5F45R.js} +384 -10
- package/dist/chunk-PYW5F45R.js.map +1 -0
- package/dist/global-sdk.d.ts +100 -3
- package/dist/global.js +482 -48
- package/dist/index.cjs +383 -9
- 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 +481 -48
- 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 +88 -29
- package/dist/multiplayer.js.map +1 -1
- package/dist/{types-Cq8p7ZkC.d.cts → types-B5bqWnHH.d.cts} +99 -4
- package/dist/{types-Cq8p7ZkC.d.ts → types-B5bqWnHH.d.ts} +99 -4
- package/docs/external-authoritative-servers.md +265 -0
- package/docs/llms.txt +800 -0
- package/docs/relay-rooms.md +134 -0
- package/package.json +2 -1
- package/dist/chunk-OWOESH4X.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,316 @@ var VERSION = 1;
|
|
|
31
31
|
var SDK_MESSAGE_SOURCE = "bb-arcade";
|
|
32
32
|
var HOST_MESSAGE_SOURCE = "bb-arcade-host";
|
|
33
33
|
|
|
34
|
+
// src/storage.ts
|
|
35
|
+
var FLUSH_DELAY_MS = 400;
|
|
36
|
+
var ENVELOPE_TAG = "__bbArcadeStorage";
|
|
37
|
+
var ENVELOPE_VERSION = 1;
|
|
38
|
+
function hasOwn(target, key) {
|
|
39
|
+
return Object.prototype.hasOwnProperty.call(target, key);
|
|
40
|
+
}
|
|
41
|
+
function emptyMap() {
|
|
42
|
+
return /* @__PURE__ */ Object.create(null);
|
|
43
|
+
}
|
|
44
|
+
function storageError(code) {
|
|
45
|
+
const err = new Error(code);
|
|
46
|
+
err.code = code;
|
|
47
|
+
return err;
|
|
48
|
+
}
|
|
49
|
+
function parseEnvelope(raw) {
|
|
50
|
+
if (raw == null) return { ls: {}, app: null };
|
|
51
|
+
if (raw.charAt(0) !== "{") return { ls: {}, app: raw };
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(raw);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return { ls: {}, app: raw };
|
|
57
|
+
}
|
|
58
|
+
if (!parsed || typeof parsed !== "object") return { ls: {}, app: raw };
|
|
59
|
+
const record = parsed;
|
|
60
|
+
if (record[ENVELOPE_TAG] !== ENVELOPE_VERSION) return { ls: {}, app: raw };
|
|
61
|
+
const ls = emptyMap();
|
|
62
|
+
const source = record.ls;
|
|
63
|
+
if (source && typeof source === "object") {
|
|
64
|
+
const entries = source;
|
|
65
|
+
for (const key in entries) {
|
|
66
|
+
if (hasOwn(entries, key) && typeof entries[key] === "string") {
|
|
67
|
+
ls[key] = entries[key];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { ls, app: typeof record.app === "string" ? record.app : null };
|
|
72
|
+
}
|
|
73
|
+
function buildEnvelope(ls, app) {
|
|
74
|
+
const envelope = {};
|
|
75
|
+
envelope[ENVELOPE_TAG] = ENVELOPE_VERSION;
|
|
76
|
+
envelope.ls = ls;
|
|
77
|
+
envelope.app = app;
|
|
78
|
+
return JSON.stringify(envelope);
|
|
79
|
+
}
|
|
80
|
+
function quotaError() {
|
|
81
|
+
try {
|
|
82
|
+
return new DOMException("Cloud save quota exceeded", "QuotaExceededError");
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const fallback = new Error("Cloud save quota exceeded");
|
|
85
|
+
fallback.name = "QuotaExceededError";
|
|
86
|
+
return fallback;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
var RESERVED = {
|
|
90
|
+
getItem: true,
|
|
91
|
+
setItem: true,
|
|
92
|
+
removeItem: true,
|
|
93
|
+
clear: true,
|
|
94
|
+
key: true,
|
|
95
|
+
length: true
|
|
96
|
+
};
|
|
97
|
+
function createStorageShim(backend = {}) {
|
|
98
|
+
const map = emptyMap();
|
|
99
|
+
const localWrites = emptyMap();
|
|
100
|
+
function keys() {
|
|
101
|
+
return Object.keys(map);
|
|
102
|
+
}
|
|
103
|
+
function changed() {
|
|
104
|
+
if (backend.onChange) backend.onChange();
|
|
105
|
+
}
|
|
106
|
+
const api = {
|
|
107
|
+
getItem: function(key) {
|
|
108
|
+
const name = String(key);
|
|
109
|
+
return hasOwn(map, name) ? map[name] : null;
|
|
110
|
+
},
|
|
111
|
+
setItem: function(key, value) {
|
|
112
|
+
const name = String(key);
|
|
113
|
+
const next = String(value);
|
|
114
|
+
if (backend.wouldOverflow && backend.wouldOverflow(name, next)) throw quotaError();
|
|
115
|
+
map[name] = next;
|
|
116
|
+
localWrites[name] = true;
|
|
117
|
+
changed();
|
|
118
|
+
},
|
|
119
|
+
removeItem: function(key) {
|
|
120
|
+
const name = String(key);
|
|
121
|
+
localWrites[name] = true;
|
|
122
|
+
if (!hasOwn(map, name)) return;
|
|
123
|
+
delete map[name];
|
|
124
|
+
changed();
|
|
125
|
+
},
|
|
126
|
+
clear: function() {
|
|
127
|
+
const existing = keys();
|
|
128
|
+
for (let i = 0; i < existing.length; i++) {
|
|
129
|
+
delete map[existing[i]];
|
|
130
|
+
localWrites[existing[i]] = true;
|
|
131
|
+
}
|
|
132
|
+
changed();
|
|
133
|
+
},
|
|
134
|
+
key: function(index) {
|
|
135
|
+
const existing = keys();
|
|
136
|
+
const at = Number(index);
|
|
137
|
+
return at >= 0 && at < existing.length ? existing[at] : null;
|
|
138
|
+
},
|
|
139
|
+
get length() {
|
|
140
|
+
return keys().length;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const storage = new Proxy(api, {
|
|
144
|
+
get(target, prop, receiver) {
|
|
145
|
+
if (typeof prop !== "string") return Reflect.get(target, prop, receiver);
|
|
146
|
+
if (hasOwn(RESERVED, prop)) return Reflect.get(target, prop, receiver);
|
|
147
|
+
return hasOwn(map, prop) ? map[prop] : void 0;
|
|
148
|
+
},
|
|
149
|
+
set(target, prop, value) {
|
|
150
|
+
if (typeof prop !== "string" || hasOwn(RESERVED, prop)) return true;
|
|
151
|
+
api.setItem(prop, value);
|
|
152
|
+
return true;
|
|
153
|
+
},
|
|
154
|
+
has(target, prop) {
|
|
155
|
+
if (typeof prop !== "string") return Reflect.has(target, prop);
|
|
156
|
+
return hasOwn(RESERVED, prop) || hasOwn(map, prop);
|
|
157
|
+
},
|
|
158
|
+
deleteProperty(target, prop) {
|
|
159
|
+
if (typeof prop === "string" && !hasOwn(RESERVED, prop)) api.removeItem(prop);
|
|
160
|
+
return true;
|
|
161
|
+
},
|
|
162
|
+
ownKeys() {
|
|
163
|
+
return keys();
|
|
164
|
+
},
|
|
165
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
166
|
+
if (typeof prop !== "string" || !hasOwn(map, prop)) return void 0;
|
|
167
|
+
return { value: map[prop], writable: true, enumerable: true, configurable: true };
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
storage,
|
|
172
|
+
entries: function() {
|
|
173
|
+
const copy = emptyMap();
|
|
174
|
+
const existing = keys();
|
|
175
|
+
for (let i = 0; i < existing.length; i++) copy[existing[i]] = map[existing[i]];
|
|
176
|
+
return copy;
|
|
177
|
+
},
|
|
178
|
+
hydrate: function(values) {
|
|
179
|
+
for (const key in values) {
|
|
180
|
+
if (hasOwn(values, key) && !hasOwn(localWrites, key)) map[key] = values[key];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function settled(promise) {
|
|
186
|
+
return promise.then(
|
|
187
|
+
function() {
|
|
188
|
+
},
|
|
189
|
+
function() {
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function installSessionStorageShim() {
|
|
194
|
+
if (nativeStorageWorks("sessionStorage")) return;
|
|
195
|
+
try {
|
|
196
|
+
Object.defineProperty(window, "sessionStorage", {
|
|
197
|
+
value: createStorageShim().storage,
|
|
198
|
+
configurable: true
|
|
199
|
+
});
|
|
200
|
+
} catch (err) {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function nativeStorageWorks(area) {
|
|
204
|
+
try {
|
|
205
|
+
const store = window[area];
|
|
206
|
+
if (!store) return false;
|
|
207
|
+
const probe = "__bb_arcade_probe__";
|
|
208
|
+
store.setItem(probe, "1");
|
|
209
|
+
store.removeItem(probe);
|
|
210
|
+
return true;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function createStorageController(options) {
|
|
216
|
+
let shim = null;
|
|
217
|
+
let mode = "native";
|
|
218
|
+
let appBlob = null;
|
|
219
|
+
let hydration = null;
|
|
220
|
+
let flushTimer = null;
|
|
221
|
+
let inFlight = Promise.resolve();
|
|
222
|
+
let dirty = false;
|
|
223
|
+
let hydrated = false;
|
|
224
|
+
function serialize() {
|
|
225
|
+
return buildEnvelope(shim ? shim.entries() : {}, appBlob);
|
|
226
|
+
}
|
|
227
|
+
function hydrateFromCloud() {
|
|
228
|
+
hydration = options.load().then(
|
|
229
|
+
function(raw) {
|
|
230
|
+
const envelope = parseEnvelope(raw);
|
|
231
|
+
if (shim) shim.hydrate(envelope.ls);
|
|
232
|
+
if (appBlob === null) appBlob = envelope.app;
|
|
233
|
+
hydrated = true;
|
|
234
|
+
return mode;
|
|
235
|
+
},
|
|
236
|
+
function(err) {
|
|
237
|
+
const code = err && err.code;
|
|
238
|
+
if (code === "unsupported" || code === "unauthenticated") mode = "memory";
|
|
239
|
+
return mode;
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
return hydration;
|
|
243
|
+
}
|
|
244
|
+
function wouldOverflow(key, value) {
|
|
245
|
+
if (!shim) return false;
|
|
246
|
+
const next = shim.entries();
|
|
247
|
+
next[key] = value;
|
|
248
|
+
return options.byteLength(buildEnvelope(next, appBlob)) > options.maxBytes;
|
|
249
|
+
}
|
|
250
|
+
function persist() {
|
|
251
|
+
return whenHydrated().then(function() {
|
|
252
|
+
if (mode === "memory") return;
|
|
253
|
+
if (hydrated) return options.save(serialize());
|
|
254
|
+
return hydrateFromCloud().then(function() {
|
|
255
|
+
if (mode === "memory") return;
|
|
256
|
+
if (!hydrated) throw storageError("error");
|
|
257
|
+
return options.save(serialize());
|
|
258
|
+
});
|
|
259
|
+
}).then(void 0, function(err) {
|
|
260
|
+
dirty = true;
|
|
261
|
+
throw err;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function flushNow() {
|
|
265
|
+
if (flushTimer !== null) {
|
|
266
|
+
clearTimeout(flushTimer);
|
|
267
|
+
flushTimer = null;
|
|
268
|
+
}
|
|
269
|
+
dirty = false;
|
|
270
|
+
const next = inFlight.then(persist, persist);
|
|
271
|
+
inFlight = settled(next);
|
|
272
|
+
return next;
|
|
273
|
+
}
|
|
274
|
+
function scheduleFlush() {
|
|
275
|
+
dirty = true;
|
|
276
|
+
if (flushTimer !== null) return;
|
|
277
|
+
flushTimer = setTimeout(function() {
|
|
278
|
+
flushTimer = null;
|
|
279
|
+
void settled(flushNow());
|
|
280
|
+
}, FLUSH_DELAY_MS);
|
|
281
|
+
}
|
|
282
|
+
function whenHydrated() {
|
|
283
|
+
return hydration ? hydration : Promise.resolve(mode);
|
|
284
|
+
}
|
|
285
|
+
function attachUnloadFlush() {
|
|
286
|
+
const flushIfDirty = function() {
|
|
287
|
+
if (dirty) void settled(flushNow());
|
|
288
|
+
};
|
|
289
|
+
try {
|
|
290
|
+
window.addEventListener("pagehide", flushIfDirty);
|
|
291
|
+
window.addEventListener("visibilitychange", function() {
|
|
292
|
+
if (document.visibilityState === "hidden") flushIfDirty();
|
|
293
|
+
});
|
|
294
|
+
} catch (err) {
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function install() {
|
|
298
|
+
if (shim) return mode;
|
|
299
|
+
installSessionStorageShim();
|
|
300
|
+
if (nativeStorageWorks("localStorage")) {
|
|
301
|
+
mode = "native";
|
|
302
|
+
return mode;
|
|
303
|
+
}
|
|
304
|
+
const handle = createStorageShim({ onChange: scheduleFlush, wouldOverflow });
|
|
305
|
+
try {
|
|
306
|
+
Object.defineProperty(window, "localStorage", {
|
|
307
|
+
value: handle.storage,
|
|
308
|
+
configurable: true
|
|
309
|
+
});
|
|
310
|
+
} catch (err) {
|
|
311
|
+
return mode;
|
|
312
|
+
}
|
|
313
|
+
shim = handle;
|
|
314
|
+
mode = "cloud";
|
|
315
|
+
attachUnloadFlush();
|
|
316
|
+
void hydrateFromCloud();
|
|
317
|
+
return mode;
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
install,
|
|
321
|
+
active: function() {
|
|
322
|
+
return shim !== null;
|
|
323
|
+
},
|
|
324
|
+
ready: function() {
|
|
325
|
+
return whenHydrated();
|
|
326
|
+
},
|
|
327
|
+
flush: function() {
|
|
328
|
+
if (!shim && appBlob === null) return Promise.resolve();
|
|
329
|
+
return flushNow();
|
|
330
|
+
},
|
|
331
|
+
writeApp: function(blob) {
|
|
332
|
+
if (options.byteLength(buildEnvelope(shim ? shim.entries() : {}, blob)) > options.maxBytes) {
|
|
333
|
+
return Promise.reject(storageError("too_large"));
|
|
334
|
+
}
|
|
335
|
+
appBlob = blob;
|
|
336
|
+
return flushNow();
|
|
337
|
+
},
|
|
338
|
+
get mode() {
|
|
339
|
+
return mode;
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
34
344
|
// src/core.ts
|
|
35
345
|
var BOUNTY_BOARD_URL = "https://www.bountyboard.gg/arcade";
|
|
36
346
|
var ADSENSE_CLIENT_ID = "ca-pub-7727110228190547";
|
|
@@ -38,6 +348,8 @@ var AD_PLACEMENT_SRC = "https://pagead2.googlesyndication.com/pagead/js/adsbygoo
|
|
|
38
348
|
var AD_PLACEMENT_SCRIPT_ID = "bb-arcade-h5-ads";
|
|
39
349
|
var DEFAULT_AD_LOAD_TIMEOUT_MS = 6e3;
|
|
40
350
|
var SAVE_TIMEOUT_MS = 15e3;
|
|
351
|
+
var LOCK_TIMEOUT_MS = 5e3;
|
|
352
|
+
var MAX_SAVE_BYTES = 1024 * 1024;
|
|
41
353
|
var INIT_CONFIG_GRACE_MS = 1500;
|
|
42
354
|
var DEFAULT_ALLOW = [
|
|
43
355
|
"bountyboard.gg",
|
|
@@ -62,6 +374,14 @@ function cleanText(value, fallback, max) {
|
|
|
62
374
|
if (typeof value !== "string") return fallback || "";
|
|
63
375
|
return value.replace(/[^\w:.-]+/g, "_").slice(0, max || 80) || fallback || "";
|
|
64
376
|
}
|
|
377
|
+
var cachedTextEncoder = null;
|
|
378
|
+
function utf8ByteLength(value) {
|
|
379
|
+
if (typeof TextEncoder !== "undefined") {
|
|
380
|
+
cachedTextEncoder = cachedTextEncoder || new TextEncoder();
|
|
381
|
+
return cachedTextEncoder.encode(value).length;
|
|
382
|
+
}
|
|
383
|
+
return new Blob([value]).size;
|
|
384
|
+
}
|
|
65
385
|
function toBoolean(value) {
|
|
66
386
|
return value === true || value === "true" || value === "1";
|
|
67
387
|
}
|
|
@@ -181,13 +501,14 @@ function createBBArcade() {
|
|
|
181
501
|
};
|
|
182
502
|
let hostConfigReceived = false;
|
|
183
503
|
let hostPlayer = null;
|
|
504
|
+
const playerChangeHandlers = [];
|
|
184
505
|
let blocked = false;
|
|
185
506
|
const pending = {};
|
|
186
507
|
let seq = 0;
|
|
187
508
|
function post(type, score, extra) {
|
|
188
509
|
if (blocked) return;
|
|
189
510
|
const message = { source: SDK_MESSAGE_SOURCE, v: VERSION, type };
|
|
190
|
-
if (typeof score === "number" && isFinite(score)) message.score = Math.
|
|
511
|
+
if (typeof score === "number" && isFinite(score)) message.score = Math.trunc(score);
|
|
191
512
|
if (extra && typeof extra === "object") {
|
|
192
513
|
const fields = extra;
|
|
193
514
|
for (const key in fields) {
|
|
@@ -208,7 +529,7 @@ function createBBArcade() {
|
|
|
208
529
|
return true;
|
|
209
530
|
}
|
|
210
531
|
}
|
|
211
|
-
function request(type, payload) {
|
|
532
|
+
function request(type, payload, timeoutMs = SAVE_TIMEOUT_MS) {
|
|
212
533
|
return new Promise(function(resolve, reject) {
|
|
213
534
|
if (blocked) return reject(sdkError("error"));
|
|
214
535
|
if (!isEmbedded()) return reject(sdkError("unsupported"));
|
|
@@ -218,7 +539,7 @@ function createBBArcade() {
|
|
|
218
539
|
delete pending[requestId];
|
|
219
540
|
reject(sdkError("error"));
|
|
220
541
|
}
|
|
221
|
-
},
|
|
542
|
+
}, timeoutMs);
|
|
222
543
|
pending[requestId] = { resolve, reject, timer };
|
|
223
544
|
try {
|
|
224
545
|
window.parent.postMessage(
|
|
@@ -310,7 +631,7 @@ function createBBArcade() {
|
|
|
310
631
|
if (!hostnameAllowed(origin, allow)) blockHost(options);
|
|
311
632
|
return;
|
|
312
633
|
}
|
|
313
|
-
request("host").then(
|
|
634
|
+
request("host", void 0, LOCK_TIMEOUT_MS).then(
|
|
314
635
|
function(res) {
|
|
315
636
|
const r = res;
|
|
316
637
|
if (!hostnameAllowed(r && r.origin, allow)) blockHost(options);
|
|
@@ -334,7 +655,7 @@ function createBBArcade() {
|
|
|
334
655
|
lockToHostBestEffort(opts, allow);
|
|
335
656
|
return;
|
|
336
657
|
}
|
|
337
|
-
request("host_attest").then(
|
|
658
|
+
request("host_attest", void 0, LOCK_TIMEOUT_MS).then(
|
|
338
659
|
function(res) {
|
|
339
660
|
const r = res;
|
|
340
661
|
if (!r || !r.attestation) {
|
|
@@ -396,7 +717,15 @@ function createBBArcade() {
|
|
|
396
717
|
if (msg.source !== HOST_MESSAGE_SOURCE || msg.type !== "config") return;
|
|
397
718
|
hostConfigReceived = true;
|
|
398
719
|
if (msg.player !== void 0) {
|
|
399
|
-
|
|
720
|
+
const nextPlayer = sanitizePlayer(msg.player);
|
|
721
|
+
const changed = nextPlayer === null ? hostPlayer !== null : hostPlayer === null || nextPlayer.name !== hostPlayer.name || nextPlayer.avatarUrl !== hostPlayer.avatarUrl;
|
|
722
|
+
hostPlayer = nextPlayer;
|
|
723
|
+
if (changed) {
|
|
724
|
+
const snapshot = nextPlayer ? { name: nextPlayer.name, avatarUrl: nextPlayer.avatarUrl } : null;
|
|
725
|
+
for (let i = 0; i < playerChangeHandlers.length; i++) {
|
|
726
|
+
safeCall(playerChangeHandlers[i], snapshot);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
400
729
|
}
|
|
401
730
|
const rewardedAds = msg.rewardedAds;
|
|
402
731
|
if (rewardedAds && typeof rewardedAds === "object") {
|
|
@@ -528,6 +857,7 @@ function createBBArcade() {
|
|
|
528
857
|
let resolved = false;
|
|
529
858
|
let status = "unavailable";
|
|
530
859
|
let rewardOffered = false;
|
|
860
|
+
let adShown = false;
|
|
531
861
|
const waitTimer = setTimeout(function() {
|
|
532
862
|
if (rewardOffered || resolved) return;
|
|
533
863
|
const timeout = Object.assign(
|
|
@@ -572,6 +902,7 @@ function createBBArcade() {
|
|
|
572
902
|
}
|
|
573
903
|
post("rewarded_ad", void 0, Object.assign({ event: "started" }, base));
|
|
574
904
|
try {
|
|
905
|
+
adShown = true;
|
|
575
906
|
showAdFn();
|
|
576
907
|
} catch (err) {
|
|
577
908
|
const showError = Object.assign(
|
|
@@ -621,7 +952,13 @@ function createBBArcade() {
|
|
|
621
952
|
},
|
|
622
953
|
adBreakDone: function(placementInfo) {
|
|
623
954
|
if (resolved) return;
|
|
624
|
-
const result = rewardOffered || status !== "unavailable" ? {
|
|
955
|
+
const result = rewardOffered || status !== "unavailable" ? {
|
|
956
|
+
// A reward was offered but the ad never reached the screen
|
|
957
|
+
// (the game retained show() and let the break lapse): the
|
|
958
|
+
// break is over without a view, so resolve 'dismissed',
|
|
959
|
+
// not the non-terminal-looking 'ready'.
|
|
960
|
+
status: status === "ready" && !adShown ? "dismissed" : status
|
|
961
|
+
} : { status: "unavailable", error: "no_rewarded_ad" };
|
|
625
962
|
if (placementInfo && typeof placementInfo === "object") {
|
|
626
963
|
result.breakStatus = cleanText(
|
|
627
964
|
placementInfo.breakStatus
|
|
@@ -792,6 +1129,16 @@ function createBBArcade() {
|
|
|
792
1129
|
if (!hostnameAllowed(event.origin, DEFAULT_ALLOW)) return;
|
|
793
1130
|
applyHostConfig(event.data);
|
|
794
1131
|
});
|
|
1132
|
+
const storage = createStorageController({
|
|
1133
|
+
save: function(blob) {
|
|
1134
|
+
return request("save", blob);
|
|
1135
|
+
},
|
|
1136
|
+
load: function() {
|
|
1137
|
+
return request("load");
|
|
1138
|
+
},
|
|
1139
|
+
byteLength: utf8ByteLength,
|
|
1140
|
+
maxBytes: MAX_SAVE_BYTES
|
|
1141
|
+
});
|
|
795
1142
|
const sdk = {
|
|
796
1143
|
lockToHost,
|
|
797
1144
|
init,
|
|
@@ -824,12 +1171,29 @@ function createBBArcade() {
|
|
|
824
1171
|
prepareRewardedBreak: prepareRewardedAd,
|
|
825
1172
|
rewardedBreak,
|
|
826
1173
|
save: function(blob) {
|
|
827
|
-
|
|
1174
|
+
const data = String(blob);
|
|
1175
|
+
if (utf8ByteLength(data) > MAX_SAVE_BYTES) {
|
|
1176
|
+
return Promise.reject(sdkError("too_large"));
|
|
1177
|
+
}
|
|
1178
|
+
if (storage.active()) return storage.writeApp(data);
|
|
1179
|
+
return request("save", data);
|
|
828
1180
|
},
|
|
829
1181
|
load: function() {
|
|
830
|
-
return request("load")
|
|
1182
|
+
return request("load").then(function(raw) {
|
|
1183
|
+
return parseEnvelope(raw).app;
|
|
1184
|
+
});
|
|
831
1185
|
},
|
|
1186
|
+
storage,
|
|
832
1187
|
getPlayer,
|
|
1188
|
+
onPlayerChange: function(handler) {
|
|
1189
|
+
if (typeof handler !== "function") return function() {
|
|
1190
|
+
};
|
|
1191
|
+
playerChangeHandlers.push(handler);
|
|
1192
|
+
return function() {
|
|
1193
|
+
const idx = playerChangeHandlers.indexOf(handler);
|
|
1194
|
+
if (idx !== -1) playerChangeHandlers.splice(idx, 1);
|
|
1195
|
+
};
|
|
1196
|
+
},
|
|
833
1197
|
getVariant: function(key, variants) {
|
|
834
1198
|
const sorted = Array.isArray(variants) ? variants.slice().sort() : [];
|
|
835
1199
|
const fallback = sorted.length > 0 ? sorted[0] : null;
|
|
@@ -895,7 +1259,17 @@ function createInertBBArcade() {
|
|
|
895
1259
|
rewardedBreak: () => Promise.resolve(false),
|
|
896
1260
|
save: () => Promise.reject(sdkError2("unsupported")),
|
|
897
1261
|
load: () => Promise.reject(sdkError2("unsupported")),
|
|
1262
|
+
// No window to shim and nothing to persist to: report the honest mode
|
|
1263
|
+
// rather than pretending an install succeeded.
|
|
1264
|
+
storage: {
|
|
1265
|
+
install: () => "memory",
|
|
1266
|
+
ready: () => Promise.resolve("memory"),
|
|
1267
|
+
flush: () => Promise.resolve(),
|
|
1268
|
+
mode: "memory"
|
|
1269
|
+
},
|
|
898
1270
|
getPlayer: () => Promise.resolve(null),
|
|
1271
|
+
onPlayerChange: () => () => {
|
|
1272
|
+
},
|
|
899
1273
|
getVariant: (key, variants) => {
|
|
900
1274
|
const sorted = Array.isArray(variants) ? variants.slice().sort() : [];
|
|
901
1275
|
return Promise.resolve(sorted.length > 0 ? sorted[0] : null);
|