@wvdsh/sdk-js 1.3.26 → 1.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +25 -52
- package/dist/index.js +505 -223
- package/dist/inject.global.js +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -84,6 +84,56 @@ var WavedashEvents = {
|
|
|
84
84
|
// src/services/audio.ts
|
|
85
85
|
import { IFRAME_MESSAGE_TYPE } from "@wvdsh/api";
|
|
86
86
|
|
|
87
|
+
// src/utils/logger.ts
|
|
88
|
+
var LOG_LEVEL = {
|
|
89
|
+
DEBUG: 0,
|
|
90
|
+
// Most verbose
|
|
91
|
+
INFO: 1,
|
|
92
|
+
WARN: 2,
|
|
93
|
+
ERROR: 3
|
|
94
|
+
};
|
|
95
|
+
var WavedashLogger = class {
|
|
96
|
+
constructor(logLevel = LOG_LEVEL.WARN) {
|
|
97
|
+
this.logLevel = logLevel;
|
|
98
|
+
}
|
|
99
|
+
setLogLevel(level) {
|
|
100
|
+
this.logLevel = level;
|
|
101
|
+
}
|
|
102
|
+
debug(message, ...args) {
|
|
103
|
+
if (this.logLevel <= LOG_LEVEL.DEBUG) {
|
|
104
|
+
console.log(`[WavedashJS] ${message}`, ...args);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
info(message, ...args) {
|
|
108
|
+
if (this.logLevel <= LOG_LEVEL.INFO) {
|
|
109
|
+
console.log(`[WavedashJS] ${message}`, ...args);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
warn(message, ...args) {
|
|
113
|
+
if (this.logLevel <= LOG_LEVEL.WARN) {
|
|
114
|
+
console.warn(`[WavedashJS] ${message}`, ...args);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
error(message, ...args) {
|
|
118
|
+
if (this.logLevel <= LOG_LEVEL.ERROR) {
|
|
119
|
+
console.error(`[WavedashJS] ${message}`, ...args);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var logger = new WavedashLogger();
|
|
124
|
+
|
|
125
|
+
// src/utils/parentOrigin.ts
|
|
126
|
+
var _parentOrigin = "";
|
|
127
|
+
function setParentOrigin(origin) {
|
|
128
|
+
_parentOrigin = origin;
|
|
129
|
+
}
|
|
130
|
+
function getParentOrigin() {
|
|
131
|
+
return _parentOrigin;
|
|
132
|
+
}
|
|
133
|
+
function hasParentFrame() {
|
|
134
|
+
return _parentOrigin !== "";
|
|
135
|
+
}
|
|
136
|
+
|
|
87
137
|
// src/services/manager.ts
|
|
88
138
|
var WavedashManager = class {
|
|
89
139
|
constructor(sdk) {
|
|
@@ -94,69 +144,30 @@ var WavedashManager = class {
|
|
|
94
144
|
};
|
|
95
145
|
|
|
96
146
|
// src/services/audio.ts
|
|
97
|
-
var WeakRefSet = class {
|
|
98
|
-
constructor() {
|
|
99
|
-
this.set = /* @__PURE__ */ new Set();
|
|
100
|
-
}
|
|
101
|
-
add(value) {
|
|
102
|
-
for (const ref of this.set) {
|
|
103
|
-
if (ref.deref() === value) return;
|
|
104
|
-
}
|
|
105
|
-
this.set.add(new WeakRef(value));
|
|
106
|
-
}
|
|
107
|
-
forEach(callback) {
|
|
108
|
-
for (const ref of this.set) {
|
|
109
|
-
const v = ref.deref();
|
|
110
|
-
if (v === void 0) this.set.delete(ref);
|
|
111
|
-
else callback(v);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
clear() {
|
|
115
|
-
this.set.clear();
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
147
|
var AudioManager = class extends WavedashManager {
|
|
119
148
|
constructor(sdk) {
|
|
120
149
|
super(sdk);
|
|
121
150
|
this._isMuted = false;
|
|
122
|
-
//
|
|
123
|
-
this.
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
this.
|
|
131
|
-
this.originalWebKitAudioContext = null;
|
|
132
|
-
this.originalAudio = null;
|
|
133
|
-
this.originalMutedDescriptor = null;
|
|
134
|
-
this.originalPlay = null;
|
|
135
|
-
this.originalSpeak = null;
|
|
136
|
-
this.originalUtteranceVolumeDescriptor = null;
|
|
137
|
-
this.mutationObserver = null;
|
|
151
|
+
// One shim per frame we've attached to.
|
|
152
|
+
this.frames = /* @__PURE__ */ new Set();
|
|
153
|
+
// Per-iframe state so we can re-shim across navigations/swaps and tear down
|
|
154
|
+
// the right frame when an iframe is removed or re-navigates. Keyed on the
|
|
155
|
+
// Document (replaced on every navigation), not contentWindow (a stable
|
|
156
|
+
// WindowProxy that survives navigations and so can't reveal a realm change).
|
|
157
|
+
this.iframeBindings = /* @__PURE__ */ new WeakMap();
|
|
158
|
+
this.iframeLoadHandlers = /* @__PURE__ */ new Map();
|
|
159
|
+
this.boundIframes = /* @__PURE__ */ new Set();
|
|
138
160
|
this.handleMute = (data) => {
|
|
139
161
|
if (this._isMuted === data.isMuted) return;
|
|
140
162
|
this._isMuted = data.isMuted;
|
|
141
|
-
|
|
142
|
-
this.contexts.forEach((gain, ctx) => {
|
|
143
|
-
const now = ctx.currentTime;
|
|
144
|
-
gain.gain.cancelScheduledValues(now);
|
|
145
|
-
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
146
|
-
gain.gain.linearRampToValueAtTime(target, now + 0.05);
|
|
147
|
-
});
|
|
148
|
-
const setMutedNative = this.originalMutedDescriptor?.set;
|
|
149
|
-
if (setMutedNative) {
|
|
150
|
-
this.elements.forEach((el) => {
|
|
151
|
-
const intended = this.intendedMuted.get(el) ?? false;
|
|
152
|
-
setMutedNative.call(el, this._isMuted ? true : intended);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
163
|
+
this.frames.forEach((shim) => shim.applyMute(this._isMuted));
|
|
155
164
|
this.sdk.gameEventManager.notifyGame(WavedashEvents.MUTE_CHANGED, {
|
|
156
165
|
isMuted: this._isMuted
|
|
157
166
|
});
|
|
158
167
|
};
|
|
159
|
-
|
|
168
|
+
if (typeof window !== "undefined") {
|
|
169
|
+
this.attachWindow(window);
|
|
170
|
+
}
|
|
160
171
|
this.sdk.iframeMessenger.addEventListener(
|
|
161
172
|
IFRAME_MESSAGE_TYPE.MUTE_CHANGED,
|
|
162
173
|
this.handleMute
|
|
@@ -173,6 +184,12 @@ var AudioManager = class extends WavedashManager {
|
|
|
173
184
|
* MUTE_CHANGED broadcast, so `isMuted()` updates independently of this result.
|
|
174
185
|
*/
|
|
175
186
|
async requestMute(muted) {
|
|
187
|
+
if (!hasParentFrame()) {
|
|
188
|
+
logger.debug(
|
|
189
|
+
"requestMute() is disabled outside a Wavedash parent frame (e.g. `wavedash dev`)"
|
|
190
|
+
);
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
176
193
|
const response = await this.sdk.iframeMessenger.requestFromParent(
|
|
177
194
|
IFRAME_MESSAGE_TYPE.SET_MUTE,
|
|
178
195
|
{ muted }
|
|
@@ -185,15 +202,159 @@ var AudioManager = class extends WavedashManager {
|
|
|
185
202
|
* host applied the change.
|
|
186
203
|
*/
|
|
187
204
|
async toggleMute() {
|
|
205
|
+
if (!hasParentFrame()) {
|
|
206
|
+
logger.debug(
|
|
207
|
+
"toggleMute() is disabled outside a Wavedash parent frame (e.g. `wavedash dev`)"
|
|
208
|
+
);
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
188
211
|
const response = await this.sdk.iframeMessenger.requestFromParent(
|
|
189
212
|
IFRAME_MESSAGE_TYPE.TOGGLE_MUTE
|
|
190
213
|
);
|
|
191
214
|
return response.success;
|
|
192
215
|
}
|
|
216
|
+
/** Shim a window we can reach. Same-origin only (cross-origin access throws). */
|
|
217
|
+
attachWindow(win) {
|
|
218
|
+
try {
|
|
219
|
+
void win.document;
|
|
220
|
+
} catch {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const shim = new AudioFrameShim(this, win);
|
|
224
|
+
this.frames.add(shim);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Start tracking an iframe: attach now (already-loaded frames) and on every
|
|
228
|
+
* `load` (about:blank → game, and later src swaps). Idempotent.
|
|
229
|
+
*/
|
|
230
|
+
bindIframe(iframe) {
|
|
231
|
+
if (!this.boundIframes.has(iframe)) {
|
|
232
|
+
this.boundIframes.add(iframe);
|
|
233
|
+
const onLoad = () => this.attachIframe(iframe);
|
|
234
|
+
this.iframeLoadHandlers.set(iframe, onLoad);
|
|
235
|
+
iframe.addEventListener("load", onLoad);
|
|
236
|
+
}
|
|
237
|
+
this.attachIframe(iframe);
|
|
238
|
+
}
|
|
239
|
+
/** Stop tracking an iframe and tear down its frame (iframe removed from DOM). */
|
|
240
|
+
unbindIframe(iframe) {
|
|
241
|
+
const handler = this.iframeLoadHandlers.get(iframe);
|
|
242
|
+
if (handler) {
|
|
243
|
+
iframe.removeEventListener("load", handler);
|
|
244
|
+
this.iframeLoadHandlers.delete(iframe);
|
|
245
|
+
}
|
|
246
|
+
this.boundIframes.delete(iframe);
|
|
247
|
+
this.teardownFrame(iframe);
|
|
248
|
+
}
|
|
193
249
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
250
|
+
* Install (or re-install) a shim for an iframe's current document. No-ops
|
|
251
|
+
* while not yet navigated or already shimmed; replaces the previous shim when
|
|
252
|
+
* the iframe navigates to a fresh document, and drops it when it goes
|
|
253
|
+
* cross-origin (we can no longer reach it).
|
|
196
254
|
*/
|
|
255
|
+
attachIframe(iframe) {
|
|
256
|
+
let win = null;
|
|
257
|
+
let doc = null;
|
|
258
|
+
try {
|
|
259
|
+
const cw = iframe.contentWindow;
|
|
260
|
+
if (cw) {
|
|
261
|
+
doc = cw.document;
|
|
262
|
+
win = cw;
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
}
|
|
266
|
+
if (!win || !doc) {
|
|
267
|
+
this.teardownFrame(iframe);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const existing = this.iframeBindings.get(iframe);
|
|
271
|
+
if (existing) {
|
|
272
|
+
if (existing.doc === doc) return;
|
|
273
|
+
this.teardownFrame(iframe);
|
|
274
|
+
}
|
|
275
|
+
const shim = new AudioFrameShim(this, win);
|
|
276
|
+
this.frames.add(shim);
|
|
277
|
+
this.iframeBindings.set(iframe, { doc, shim });
|
|
278
|
+
if (this._isMuted) shim.applyMute(true);
|
|
279
|
+
}
|
|
280
|
+
/** Remove and uninstall the shim bound to an iframe's (previous) document. */
|
|
281
|
+
teardownFrame(iframe) {
|
|
282
|
+
const binding = this.iframeBindings.get(iframe);
|
|
283
|
+
if (!binding) return;
|
|
284
|
+
this.frames.delete(binding.shim);
|
|
285
|
+
binding.shim.uninstall();
|
|
286
|
+
this.iframeBindings.delete(iframe);
|
|
287
|
+
}
|
|
288
|
+
destroy() {
|
|
289
|
+
this.sdk.iframeMessenger.removeEventListener(
|
|
290
|
+
IFRAME_MESSAGE_TYPE.MUTE_CHANGED,
|
|
291
|
+
this.handleMute
|
|
292
|
+
);
|
|
293
|
+
this.boundIframes.forEach((iframe) => {
|
|
294
|
+
const handler = this.iframeLoadHandlers.get(iframe);
|
|
295
|
+
if (handler) iframe.removeEventListener("load", handler);
|
|
296
|
+
});
|
|
297
|
+
this.boundIframes.clear();
|
|
298
|
+
this.iframeLoadHandlers.clear();
|
|
299
|
+
this.frames.forEach((shim) => shim.uninstall());
|
|
300
|
+
this.frames.clear();
|
|
301
|
+
super.destroy();
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
var AudioFrameShim = class {
|
|
305
|
+
constructor(manager, win) {
|
|
306
|
+
this.contexts = /* @__PURE__ */ new Map();
|
|
307
|
+
// Tracked media elements + the game's intended muted value (what it last set).
|
|
308
|
+
this.elements = new WeakRefSet();
|
|
309
|
+
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
310
|
+
// Utterances + the game's intended volume.
|
|
311
|
+
this.intendedUtteranceVolume = /* @__PURE__ */ new WeakMap();
|
|
312
|
+
// Child iframes discovered in this frame's document. Tracked so we can
|
|
313
|
+
// cascade-unbind them when this frame is torn down — their own removal events
|
|
314
|
+
// never fire when the containing document is discarded wholesale.
|
|
315
|
+
this.boundChildren = /* @__PURE__ */ new Set();
|
|
316
|
+
// Originals, restored on uninstall.
|
|
317
|
+
this.originalAudioContext = null;
|
|
318
|
+
this.originalWebKitAudioContext = null;
|
|
319
|
+
this.originalAudio = null;
|
|
320
|
+
this.originalMutedDescriptor = null;
|
|
321
|
+
this.originalPlay = null;
|
|
322
|
+
this.originalSpeak = null;
|
|
323
|
+
this.originalUtteranceVolumeDescriptor = null;
|
|
324
|
+
this.mutationObserver = null;
|
|
325
|
+
this.manager = manager;
|
|
326
|
+
this.win = win;
|
|
327
|
+
this.doc = win.document ?? null;
|
|
328
|
+
this.installShims();
|
|
329
|
+
}
|
|
330
|
+
/** Push the current mute state onto everything this frame is tracking. */
|
|
331
|
+
applyMute(isMuted) {
|
|
332
|
+
const target = isMuted ? 0 : 1;
|
|
333
|
+
this.contexts.forEach((gain, ctx) => {
|
|
334
|
+
const now = ctx.currentTime;
|
|
335
|
+
gain.gain.cancelScheduledValues(now);
|
|
336
|
+
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
337
|
+
gain.gain.linearRampToValueAtTime(target, now + 0.05);
|
|
338
|
+
});
|
|
339
|
+
const setMutedNative = this.originalMutedDescriptor?.set;
|
|
340
|
+
if (setMutedNative) {
|
|
341
|
+
this.elements.forEach((el) => {
|
|
342
|
+
const intended = this.intendedMuted.get(el) ?? false;
|
|
343
|
+
setMutedNative.call(el, isMuted ? true : intended);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** Hand a discovered child iframe to the manager, remembering it for teardown. */
|
|
348
|
+
bindChild(iframe) {
|
|
349
|
+
this.boundChildren.add(iframe);
|
|
350
|
+
this.manager.bindIframe(iframe);
|
|
351
|
+
}
|
|
352
|
+
/** Stop tracking a child iframe that was removed from this document. */
|
|
353
|
+
unbindChild(iframe) {
|
|
354
|
+
this.boundChildren.delete(iframe);
|
|
355
|
+
this.manager.unbindIframe(iframe);
|
|
356
|
+
}
|
|
357
|
+
/** Track a media element and silence it if currently muted. Idempotent. */
|
|
197
358
|
trackElement(el) {
|
|
198
359
|
if (this.intendedMuted.has(el)) return;
|
|
199
360
|
const getMuted = this.originalMutedDescriptor?.get;
|
|
@@ -201,139 +362,161 @@ var AudioManager = class extends WavedashManager {
|
|
|
201
362
|
const current = getMuted ? getMuted.call(el) : el.muted;
|
|
202
363
|
this.intendedMuted.set(el, current);
|
|
203
364
|
this.elements.add(el);
|
|
204
|
-
if (this.
|
|
365
|
+
if (this.manager.isMuted() && !current && setMuted) {
|
|
205
366
|
setMuted.call(el, true);
|
|
206
367
|
}
|
|
207
368
|
}
|
|
208
369
|
installShims() {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const OriginalAudio = window.Audio;
|
|
370
|
+
const win = this.win;
|
|
371
|
+
const doc = this.doc;
|
|
372
|
+
if (win.AudioContext) {
|
|
373
|
+
this.originalAudioContext = win.AudioContext;
|
|
374
|
+
win.AudioContext = this.shimAudioContextClass(win.AudioContext);
|
|
375
|
+
}
|
|
376
|
+
const w = win;
|
|
377
|
+
if (w.webkitAudioContext) {
|
|
378
|
+
this.originalWebKitAudioContext = w.webkitAudioContext;
|
|
379
|
+
w.webkitAudioContext = this.shimAudioContextClass(w.webkitAudioContext);
|
|
380
|
+
}
|
|
381
|
+
if (win.Audio) {
|
|
382
|
+
const OriginalAudio = win.Audio;
|
|
223
383
|
this.originalAudio = OriginalAudio;
|
|
224
|
-
((
|
|
384
|
+
((shim) => {
|
|
225
385
|
const Shimmed = function(src) {
|
|
226
386
|
const audio = new OriginalAudio(src);
|
|
227
|
-
|
|
387
|
+
shim.trackElement(audio);
|
|
228
388
|
return audio;
|
|
229
389
|
};
|
|
230
390
|
Shimmed.prototype = OriginalAudio.prototype;
|
|
231
|
-
|
|
391
|
+
win.Audio = Shimmed;
|
|
232
392
|
})(this);
|
|
233
393
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
394
|
+
if (doc) {
|
|
395
|
+
const HTMLMediaElementCtor = win.HTMLMediaElement;
|
|
396
|
+
const HTMLIFrameElementCtor = win.HTMLIFrameElement;
|
|
397
|
+
const HTMLElementCtor = win.HTMLElement;
|
|
398
|
+
doc.querySelectorAll("audio, video").forEach((el) => {
|
|
236
399
|
this.trackElement(el);
|
|
237
400
|
});
|
|
238
|
-
|
|
401
|
+
doc.querySelectorAll("iframe").forEach((el) => {
|
|
402
|
+
this.bindChild(el);
|
|
403
|
+
});
|
|
404
|
+
this.mutationObserver = new win.MutationObserver((mutations) => {
|
|
239
405
|
for (const m of mutations) {
|
|
240
406
|
m.addedNodes.forEach((node) => {
|
|
241
|
-
if (node instanceof
|
|
407
|
+
if (node instanceof HTMLMediaElementCtor) {
|
|
242
408
|
this.trackElement(node);
|
|
243
|
-
} else if (node instanceof
|
|
244
|
-
|
|
245
|
-
|
|
409
|
+
} else if (node instanceof HTMLIFrameElementCtor) {
|
|
410
|
+
this.bindChild(node);
|
|
411
|
+
} else if (node instanceof HTMLElementCtor) {
|
|
412
|
+
const el = node;
|
|
413
|
+
el.querySelectorAll("audio, video").forEach((m2) => {
|
|
414
|
+
this.trackElement(m2);
|
|
415
|
+
});
|
|
416
|
+
el.querySelectorAll("iframe").forEach((f) => {
|
|
417
|
+
this.bindChild(f);
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
m.removedNodes.forEach((node) => {
|
|
422
|
+
if (node instanceof HTMLIFrameElementCtor) {
|
|
423
|
+
this.unbindChild(node);
|
|
424
|
+
} else if (node instanceof HTMLElementCtor) {
|
|
425
|
+
node.querySelectorAll("iframe").forEach((f) => {
|
|
426
|
+
this.unbindChild(f);
|
|
246
427
|
});
|
|
247
428
|
}
|
|
248
429
|
});
|
|
249
430
|
}
|
|
250
431
|
});
|
|
251
|
-
this.mutationObserver.observe(
|
|
432
|
+
this.mutationObserver.observe(doc.documentElement, {
|
|
252
433
|
childList: true,
|
|
253
434
|
subtree: true
|
|
254
435
|
});
|
|
255
436
|
}
|
|
256
|
-
this.originalMutedDescriptor = Object.getOwnPropertyDescriptor(
|
|
437
|
+
this.originalMutedDescriptor = Object.getOwnPropertyDescriptor(
|
|
438
|
+
win.HTMLMediaElement.prototype,
|
|
439
|
+
"muted"
|
|
440
|
+
) ?? null;
|
|
257
441
|
const original = this.originalMutedDescriptor;
|
|
258
442
|
if (original?.get && original?.set) {
|
|
259
|
-
((
|
|
260
|
-
Object.defineProperty(HTMLMediaElement.prototype, "muted", {
|
|
443
|
+
((shim) => {
|
|
444
|
+
Object.defineProperty(win.HTMLMediaElement.prototype, "muted", {
|
|
261
445
|
configurable: true,
|
|
262
446
|
get() {
|
|
263
|
-
const intended =
|
|
447
|
+
const intended = shim.intendedMuted.get(this);
|
|
264
448
|
return intended !== void 0 ? intended : original.get.call(this);
|
|
265
449
|
},
|
|
266
450
|
set(value) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
original.set.call(this, manager.
|
|
451
|
+
shim.intendedMuted.set(this, value);
|
|
452
|
+
shim.elements.add(this);
|
|
453
|
+
original.set.call(this, shim.manager.isMuted() ? true : value);
|
|
270
454
|
}
|
|
271
455
|
});
|
|
272
456
|
})(this);
|
|
273
457
|
}
|
|
274
|
-
const originalPlay = HTMLMediaElement.prototype.play;
|
|
458
|
+
const originalPlay = win.HTMLMediaElement.prototype.play;
|
|
275
459
|
this.originalPlay = originalPlay;
|
|
276
|
-
((
|
|
277
|
-
HTMLMediaElement.prototype.play = function() {
|
|
278
|
-
|
|
460
|
+
((shim) => {
|
|
461
|
+
win.HTMLMediaElement.prototype.play = function() {
|
|
462
|
+
shim.trackElement(this);
|
|
279
463
|
return originalPlay.call(this);
|
|
280
464
|
};
|
|
281
465
|
})(this);
|
|
282
466
|
this.shimSpeechSynthesis();
|
|
283
467
|
}
|
|
284
468
|
/**
|
|
285
|
-
* Shim `
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* off (onstart/onend, synth.speaking/pending checks), so every call is
|
|
289
|
-
* delegated and silenced via volume instead. Volume is sampled at speak()
|
|
290
|
-
* time, so forcing the native value to 0 right before delegating silences
|
|
291
|
-
* anything spoken while muted; in-flight speech at the mute edge is
|
|
292
|
-
* deliberately left to finish (can't be softened mid-utterance, and
|
|
293
|
-
* cancel() would discard the pending queue).
|
|
469
|
+
* Shim `speechSynthesis`. We never swallow `speak()` (games sequence off its
|
|
470
|
+
* lifecycle) — instead we sample volume at call time and force it to 0 while
|
|
471
|
+
* muted. Speech already in flight at the mute edge is left to finish.
|
|
294
472
|
*/
|
|
295
473
|
shimSpeechSynthesis() {
|
|
296
|
-
|
|
474
|
+
const win = this.win;
|
|
475
|
+
if (!win.speechSynthesis || typeof win.SpeechSynthesisUtterance === "undefined") {
|
|
297
476
|
return;
|
|
298
477
|
}
|
|
299
478
|
this.originalUtteranceVolumeDescriptor = Object.getOwnPropertyDescriptor(
|
|
300
|
-
SpeechSynthesisUtterance.prototype,
|
|
479
|
+
win.SpeechSynthesisUtterance.prototype,
|
|
301
480
|
"volume"
|
|
302
481
|
) ?? null;
|
|
303
482
|
const volDesc = this.originalUtteranceVolumeDescriptor;
|
|
304
483
|
if (volDesc?.get && volDesc?.set) {
|
|
305
|
-
((
|
|
306
|
-
Object.defineProperty(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
484
|
+
((shim) => {
|
|
485
|
+
Object.defineProperty(
|
|
486
|
+
win.SpeechSynthesisUtterance.prototype,
|
|
487
|
+
"volume",
|
|
488
|
+
{
|
|
489
|
+
configurable: true,
|
|
490
|
+
get() {
|
|
491
|
+
const intended = shim.intendedUtteranceVolume.get(this);
|
|
492
|
+
return intended !== void 0 ? intended : volDesc.get.call(this);
|
|
493
|
+
},
|
|
494
|
+
set(value) {
|
|
495
|
+
shim.intendedUtteranceVolume.set(this, value);
|
|
496
|
+
volDesc.set.call(this, value);
|
|
497
|
+
}
|
|
315
498
|
}
|
|
316
|
-
|
|
499
|
+
);
|
|
317
500
|
})(this);
|
|
318
501
|
}
|
|
319
|
-
const speechSynthesis =
|
|
502
|
+
const speechSynthesis = win.speechSynthesis;
|
|
320
503
|
const originalSpeak = speechSynthesis.speak;
|
|
321
504
|
this.originalSpeak = originalSpeak;
|
|
322
|
-
((
|
|
505
|
+
((shim) => {
|
|
323
506
|
speechSynthesis.speak = function(utterance) {
|
|
324
|
-
if (manager.
|
|
325
|
-
if (!
|
|
507
|
+
if (shim.manager.isMuted()) {
|
|
508
|
+
if (!shim.intendedUtteranceVolume.has(utterance)) {
|
|
326
509
|
const current = volDesc?.get ? volDesc.get.call(utterance) : utterance.volume;
|
|
327
|
-
|
|
510
|
+
shim.intendedUtteranceVolume.set(utterance, current);
|
|
328
511
|
}
|
|
329
512
|
if (volDesc?.set) volDesc.set.call(utterance, 0);
|
|
330
513
|
else utterance.volume = 0;
|
|
331
514
|
} else {
|
|
332
|
-
const intended =
|
|
515
|
+
const intended = shim.intendedUtteranceVolume.get(utterance);
|
|
333
516
|
if (intended !== void 0) {
|
|
334
517
|
if (volDesc?.set) volDesc.set.call(utterance, intended);
|
|
335
518
|
else utterance.volume = intended;
|
|
336
|
-
|
|
519
|
+
shim.intendedUtteranceVolume.delete(utterance);
|
|
337
520
|
}
|
|
338
521
|
}
|
|
339
522
|
return originalSpeak.call(speechSynthesis, utterance);
|
|
@@ -341,13 +524,13 @@ var AudioManager = class extends WavedashManager {
|
|
|
341
524
|
})(this);
|
|
342
525
|
}
|
|
343
526
|
shimAudioContextClass(Original) {
|
|
344
|
-
return /* @__PURE__ */ ((
|
|
527
|
+
return /* @__PURE__ */ ((shim) => class extends Original {
|
|
345
528
|
constructor(opts) {
|
|
346
529
|
super(opts);
|
|
347
530
|
const masterGain = this.createGain();
|
|
348
531
|
masterGain.connect(this.destination);
|
|
349
532
|
masterGain.gain.setValueAtTime(
|
|
350
|
-
manager.
|
|
533
|
+
shim.manager.isMuted() ? 0 : 1,
|
|
351
534
|
this.currentTime
|
|
352
535
|
);
|
|
353
536
|
Object.defineProperty(this, "destination", {
|
|
@@ -356,60 +539,101 @@ var AudioManager = class extends WavedashManager {
|
|
|
356
539
|
return masterGain;
|
|
357
540
|
}
|
|
358
541
|
});
|
|
359
|
-
|
|
542
|
+
shim.contexts.set(this, masterGain);
|
|
360
543
|
}
|
|
361
544
|
close() {
|
|
362
|
-
|
|
545
|
+
shim.contexts.delete(this);
|
|
363
546
|
return super.close();
|
|
364
547
|
}
|
|
365
548
|
})(this);
|
|
366
549
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
this.mutationObserver
|
|
550
|
+
/**
|
|
551
|
+
* Restore the globals we patched. Best-effort per statement: a frame reached
|
|
552
|
+
* through an iframe may have navigated away (globals gone) before teardown.
|
|
553
|
+
*/
|
|
554
|
+
uninstall() {
|
|
555
|
+
const win = this.win;
|
|
556
|
+
try {
|
|
557
|
+
if (this.mutationObserver) {
|
|
558
|
+
this.mutationObserver.disconnect();
|
|
559
|
+
this.mutationObserver = null;
|
|
560
|
+
}
|
|
561
|
+
} catch {
|
|
375
562
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
563
|
+
this.boundChildren.forEach((child) => this.manager.unbindIframe(child));
|
|
564
|
+
this.boundChildren.clear();
|
|
565
|
+
const restore = (fn) => {
|
|
566
|
+
try {
|
|
567
|
+
fn();
|
|
568
|
+
} catch {
|
|
379
569
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
570
|
+
};
|
|
571
|
+
restore(() => {
|
|
572
|
+
if (this.originalAudioContext)
|
|
573
|
+
win.AudioContext = this.originalAudioContext;
|
|
574
|
+
});
|
|
575
|
+
restore(() => {
|
|
576
|
+
const w = win;
|
|
577
|
+
if (this.originalWebKitAudioContext && w.webkitAudioContext) {
|
|
578
|
+
w.webkitAudioContext = this.originalWebKitAudioContext;
|
|
383
579
|
}
|
|
384
|
-
|
|
385
|
-
|
|
580
|
+
});
|
|
581
|
+
restore(() => {
|
|
582
|
+
if (this.originalAudio) win.Audio = this.originalAudio;
|
|
583
|
+
});
|
|
584
|
+
restore(() => {
|
|
585
|
+
if (this.originalSpeak && win.speechSynthesis) {
|
|
586
|
+
win.speechSynthesis.speak = this.originalSpeak;
|
|
386
587
|
}
|
|
387
|
-
|
|
388
|
-
|
|
588
|
+
});
|
|
589
|
+
restore(() => {
|
|
590
|
+
if (this.originalUtteranceVolumeDescriptor && typeof win.SpeechSynthesisUtterance !== "undefined") {
|
|
591
|
+
Object.defineProperty(
|
|
592
|
+
win.SpeechSynthesisUtterance.prototype,
|
|
593
|
+
"volume",
|
|
594
|
+
this.originalUtteranceVolumeDescriptor
|
|
595
|
+
);
|
|
389
596
|
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
this.originalMutedDescriptor
|
|
406
|
-
);
|
|
407
|
-
}
|
|
597
|
+
});
|
|
598
|
+
restore(() => {
|
|
599
|
+
if (this.originalPlay) {
|
|
600
|
+
win.HTMLMediaElement.prototype.play = this.originalPlay;
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
restore(() => {
|
|
604
|
+
if (this.originalMutedDescriptor) {
|
|
605
|
+
Object.defineProperty(
|
|
606
|
+
win.HTMLMediaElement.prototype,
|
|
607
|
+
"muted",
|
|
608
|
+
this.originalMutedDescriptor
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
408
612
|
this.contexts.clear();
|
|
409
613
|
this.elements.clear();
|
|
410
614
|
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
411
615
|
this.intendedUtteranceVolume = /* @__PURE__ */ new WeakMap();
|
|
412
|
-
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
var WeakRefSet = class {
|
|
619
|
+
constructor() {
|
|
620
|
+
this.set = /* @__PURE__ */ new Set();
|
|
621
|
+
}
|
|
622
|
+
add(value) {
|
|
623
|
+
for (const ref of this.set) {
|
|
624
|
+
if (ref.deref() === value) return;
|
|
625
|
+
}
|
|
626
|
+
this.set.add(new WeakRef(value));
|
|
627
|
+
}
|
|
628
|
+
forEach(callback) {
|
|
629
|
+
for (const ref of this.set) {
|
|
630
|
+
const v = ref.deref();
|
|
631
|
+
if (v === void 0) this.set.delete(ref);
|
|
632
|
+
else callback(v);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
clear() {
|
|
636
|
+
this.set.clear();
|
|
413
637
|
}
|
|
414
638
|
};
|
|
415
639
|
|
|
@@ -483,44 +707,6 @@ function toBlobFromIndexedDBValue(value) {
|
|
|
483
707
|
throw new Error("Unrecognized value shape from IndexedDB");
|
|
484
708
|
}
|
|
485
709
|
|
|
486
|
-
// src/utils/logger.ts
|
|
487
|
-
var LOG_LEVEL = {
|
|
488
|
-
DEBUG: 0,
|
|
489
|
-
// Most verbose
|
|
490
|
-
INFO: 1,
|
|
491
|
-
WARN: 2,
|
|
492
|
-
ERROR: 3
|
|
493
|
-
};
|
|
494
|
-
var WavedashLogger = class {
|
|
495
|
-
constructor(logLevel = LOG_LEVEL.WARN) {
|
|
496
|
-
this.logLevel = logLevel;
|
|
497
|
-
}
|
|
498
|
-
setLogLevel(level) {
|
|
499
|
-
this.logLevel = level;
|
|
500
|
-
}
|
|
501
|
-
debug(message, ...args) {
|
|
502
|
-
if (this.logLevel <= LOG_LEVEL.DEBUG) {
|
|
503
|
-
console.log(`[WavedashJS] ${message}`, ...args);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
info(message, ...args) {
|
|
507
|
-
if (this.logLevel <= LOG_LEVEL.INFO) {
|
|
508
|
-
console.log(`[WavedashJS] ${message}`, ...args);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
warn(message, ...args) {
|
|
512
|
-
if (this.logLevel <= LOG_LEVEL.WARN) {
|
|
513
|
-
console.warn(`[WavedashJS] ${message}`, ...args);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
error(message, ...args) {
|
|
517
|
-
if (this.logLevel <= LOG_LEVEL.ERROR) {
|
|
518
|
-
console.error(`[WavedashJS] ${message}`, ...args);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
var logger = new WavedashLogger();
|
|
523
|
-
|
|
524
710
|
// src/services/fileSystem.ts
|
|
525
711
|
import { api } from "@wvdsh/api";
|
|
526
712
|
var REMOTE_STORAGE_FOLDER = "userfs";
|
|
@@ -967,6 +1153,7 @@ var FullscreenManager = class extends WavedashManager {
|
|
|
967
1153
|
super(sdk);
|
|
968
1154
|
this._isFullscreen = false;
|
|
969
1155
|
this.listeners = /* @__PURE__ */ new Set();
|
|
1156
|
+
if (!hasParentFrame()) return;
|
|
970
1157
|
this.sdk.iframeMessenger.addEventListener(
|
|
971
1158
|
IFRAME_MESSAGE_TYPE2.FULLSCREEN_CHANGED,
|
|
972
1159
|
(data) => {
|
|
@@ -988,6 +1175,12 @@ var FullscreenManager = class extends WavedashManager {
|
|
|
988
1175
|
* (e.g. browser rejected for lack of user activation).
|
|
989
1176
|
*/
|
|
990
1177
|
async requestFullscreen(fullscreen) {
|
|
1178
|
+
if (!hasParentFrame()) {
|
|
1179
|
+
logger.debug(
|
|
1180
|
+
"requestFullscreen() is disabled outside a Wavedash parent frame (e.g. `wavedash dev`)"
|
|
1181
|
+
);
|
|
1182
|
+
return false;
|
|
1183
|
+
}
|
|
991
1184
|
const response = await this.sdk.iframeMessenger.requestFromParent(
|
|
992
1185
|
IFRAME_MESSAGE_TYPE2.SET_FULLSCREEN,
|
|
993
1186
|
{ fullscreen }
|
|
@@ -995,6 +1188,12 @@ var FullscreenManager = class extends WavedashManager {
|
|
|
995
1188
|
return response.success;
|
|
996
1189
|
}
|
|
997
1190
|
async toggleFullscreen() {
|
|
1191
|
+
if (!hasParentFrame()) {
|
|
1192
|
+
logger.debug(
|
|
1193
|
+
"toggleFullscreen() is disabled outside a Wavedash parent frame (e.g. `wavedash dev`)"
|
|
1194
|
+
);
|
|
1195
|
+
return false;
|
|
1196
|
+
}
|
|
998
1197
|
const response = await this.sdk.iframeMessenger.requestFromParent(
|
|
999
1198
|
IFRAME_MESSAGE_TYPE2.TOGGLE_FULLSCREEN
|
|
1000
1199
|
);
|
|
@@ -1157,7 +1356,7 @@ var HeartbeatManager = class extends WavedashManager {
|
|
|
1157
1356
|
this.gamepadPollInterval = setInterval(() => {
|
|
1158
1357
|
this.pollGamepads();
|
|
1159
1358
|
}, this.GAMEPAD_POLL_INTERVAL_MS);
|
|
1160
|
-
this.deviceFingerprintReady = this.sdk.iframeMessenger.requestFromParent(IFRAME_MESSAGE_TYPE3.GET_DEVICE_FINGERPRINT).then((fingerprint) => {
|
|
1359
|
+
this.deviceFingerprintReady = !hasParentFrame() ? Promise.resolve() : this.sdk.iframeMessenger.requestFromParent(IFRAME_MESSAGE_TYPE3.GET_DEVICE_FINGERPRINT).then((fingerprint) => {
|
|
1161
1360
|
this.deviceFingerprint = fingerprint;
|
|
1162
1361
|
}).catch(() => {
|
|
1163
1362
|
});
|
|
@@ -1706,6 +1905,11 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
|
|
|
1706
1905
|
if (!this.lobbyId) {
|
|
1707
1906
|
throw new Error("User is not in a lobby");
|
|
1708
1907
|
}
|
|
1908
|
+
if (!hasParentFrame()) {
|
|
1909
|
+
throw new Error(
|
|
1910
|
+
"Lobby invite links are not available outside a Wavedash parent frame (e.g. `wavedash dev`)"
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1709
1913
|
const inviteLink = await this.sdk.iframeMessenger.requestFromParent(
|
|
1710
1914
|
IFRAME_MESSAGE_TYPE4.GET_LOBBY_INVITE_LINK,
|
|
1711
1915
|
{ lobbyId: this.lobbyId, copyToClipboard }
|
|
@@ -1971,6 +2175,7 @@ var OverlayManager = class extends WavedashManager {
|
|
|
1971
2175
|
this.toggleOverlay();
|
|
1972
2176
|
}
|
|
1973
2177
|
};
|
|
2178
|
+
if (!hasParentFrame()) return;
|
|
1974
2179
|
this.sdk.iframeMessenger.addEventListener(
|
|
1975
2180
|
IFRAME_MESSAGE_TYPE5.TAKE_FOCUS,
|
|
1976
2181
|
takeFocus
|
|
@@ -3301,7 +3506,76 @@ _P2PManager.MEMORY_WARNING_THRESHOLD_BYTES = 128 * 1024 * 1024;
|
|
|
3301
3506
|
var P2PManager = _P2PManager;
|
|
3302
3507
|
|
|
3303
3508
|
// src/services/paidContent.ts
|
|
3304
|
-
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE6 } from "@wvdsh/api";
|
|
3509
|
+
import { api as api7, IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE6 } from "@wvdsh/api";
|
|
3510
|
+
|
|
3511
|
+
// src/utils/devPaywall.ts
|
|
3512
|
+
var Z_INDEX = 2147483647;
|
|
3513
|
+
function showDevPaywall(contentIdentifier) {
|
|
3514
|
+
if (typeof document === "undefined") return Promise.resolve(false);
|
|
3515
|
+
return new Promise((resolve) => {
|
|
3516
|
+
const overlay = document.createElement("div");
|
|
3517
|
+
overlay.style.cssText = "position:fixed;inset:0;z-index:" + Z_INDEX + ";display:flex;align-items:center;justify-content:center;background:rgba(8,10,18,0.72);font:14px ui-sans-serif,system-ui,sans-serif;color:#e2e8f0";
|
|
3518
|
+
const card = document.createElement("div");
|
|
3519
|
+
card.style.cssText = "max-width:420px;width:calc(100% - 48px);background:#11151f;border:1px solid #2a3344;border-radius:12px;padding:24px;box-shadow:0 20px 60px rgba(0,0,0,0.5);box-sizing:border-box";
|
|
3520
|
+
const badge = document.createElement("p");
|
|
3521
|
+
badge.textContent = "wavedash dev \u2014 simulated purchase";
|
|
3522
|
+
badge.style.cssText = "margin:0 0 12px;font-size:11px;letter-spacing:0.08em;text-transform:uppercase;color:#7c8aa5";
|
|
3523
|
+
const title = document.createElement("p");
|
|
3524
|
+
title.textContent = "Unlock paid content?";
|
|
3525
|
+
title.style.cssText = "margin:0 0 8px;font-size:18px;font-weight:600;color:#f1f5f9";
|
|
3526
|
+
const body = document.createElement("p");
|
|
3527
|
+
body.style.cssText = "margin:0 0 20px;line-height:1.5;color:#cbd5e1";
|
|
3528
|
+
body.append(
|
|
3529
|
+
document.createTextNode("This game is requesting purchase of \u201C")
|
|
3530
|
+
);
|
|
3531
|
+
const id = document.createElement("code");
|
|
3532
|
+
id.textContent = contentIdentifier;
|
|
3533
|
+
id.style.cssText = "background:#1c2433;border-radius:4px;padding:1px 6px;color:#93c5fd";
|
|
3534
|
+
body.append(
|
|
3535
|
+
id,
|
|
3536
|
+
document.createTextNode(
|
|
3537
|
+
"\u201D. No real payment happens in dev \u2014 simulate the outcome to test your flow."
|
|
3538
|
+
)
|
|
3539
|
+
);
|
|
3540
|
+
const buttons = document.createElement("div");
|
|
3541
|
+
buttons.style.cssText = "display:flex;gap:12px;justify-content:flex-end";
|
|
3542
|
+
const baseBtn = "border-radius:8px;padding:9px 18px;font-size:14px;font-weight:600;cursor:pointer;border:1px solid transparent";
|
|
3543
|
+
const cancel = document.createElement("button");
|
|
3544
|
+
cancel.type = "button";
|
|
3545
|
+
cancel.textContent = "Cancel";
|
|
3546
|
+
cancel.style.cssText = baseBtn + ";background:transparent;border-color:#374151;color:#cbd5e1";
|
|
3547
|
+
const buy = document.createElement("button");
|
|
3548
|
+
buy.type = "button";
|
|
3549
|
+
buy.textContent = "Simulate purchase";
|
|
3550
|
+
buy.style.cssText = baseBtn + ";background:#2563eb;color:#fff";
|
|
3551
|
+
buttons.append(cancel, buy);
|
|
3552
|
+
card.append(badge, title, body, buttons);
|
|
3553
|
+
overlay.append(card);
|
|
3554
|
+
let settled = false;
|
|
3555
|
+
const finish = (purchased) => {
|
|
3556
|
+
if (settled) return;
|
|
3557
|
+
settled = true;
|
|
3558
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
3559
|
+
overlay.remove();
|
|
3560
|
+
resolve(purchased);
|
|
3561
|
+
};
|
|
3562
|
+
const onKeyDown = (event) => {
|
|
3563
|
+
if (event.key === "Escape") {
|
|
3564
|
+
event.preventDefault();
|
|
3565
|
+
finish(false);
|
|
3566
|
+
}
|
|
3567
|
+
};
|
|
3568
|
+
cancel.addEventListener("click", () => finish(false));
|
|
3569
|
+
buy.addEventListener("click", () => finish(true));
|
|
3570
|
+
overlay.addEventListener("click", (event) => {
|
|
3571
|
+
if (event.target === overlay) finish(false);
|
|
3572
|
+
});
|
|
3573
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
3574
|
+
document.body.append(overlay);
|
|
3575
|
+
});
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
// src/services/paidContent.ts
|
|
3305
3579
|
var PAYWALL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
3306
3580
|
function decodeJwtPayload(jwt) {
|
|
3307
3581
|
try {
|
|
@@ -3343,6 +3617,22 @@ var PaidContentManager = class extends WavedashManager {
|
|
|
3343
3617
|
}
|
|
3344
3618
|
this.paywallOpen = true;
|
|
3345
3619
|
this.restorePointerLock = suspendPointerLock();
|
|
3620
|
+
if (!hasParentFrame()) {
|
|
3621
|
+
let purchased;
|
|
3622
|
+
try {
|
|
3623
|
+
purchased = await showDevPaywall(contentIdentifier);
|
|
3624
|
+
} finally {
|
|
3625
|
+
this.restorePointerLock?.();
|
|
3626
|
+
this.restorePointerLock = void 0;
|
|
3627
|
+
this.paywallOpen = false;
|
|
3628
|
+
}
|
|
3629
|
+
if (!purchased) return false;
|
|
3630
|
+
await this.sdk.convexClient.mutation(api7.sdk.paidContent.mockPurchase, {
|
|
3631
|
+
contentIdentifier
|
|
3632
|
+
});
|
|
3633
|
+
await this.sdk.ensureGameplayJwt(true);
|
|
3634
|
+
return true;
|
|
3635
|
+
}
|
|
3346
3636
|
let response;
|
|
3347
3637
|
try {
|
|
3348
3638
|
response = await this.sdk.iframeMessenger.requestFromParent(
|
|
@@ -3369,7 +3659,7 @@ var PaidContentManager = class extends WavedashManager {
|
|
|
3369
3659
|
};
|
|
3370
3660
|
|
|
3371
3661
|
// src/services/stats.ts
|
|
3372
|
-
import { api as
|
|
3662
|
+
import { api as api8 } from "@wvdsh/api";
|
|
3373
3663
|
import throttle2 from "lodash.throttle";
|
|
3374
3664
|
var STORE_THROTTLE_MS = 1e3;
|
|
3375
3665
|
var StatsManager = class extends WavedashManager {
|
|
@@ -3427,7 +3717,7 @@ var StatsManager = class extends WavedashManager {
|
|
|
3427
3717
|
subscribe() {
|
|
3428
3718
|
this.subscriptions.push(
|
|
3429
3719
|
this.sdk.convexClient.onUpdate(
|
|
3430
|
-
|
|
3720
|
+
api8.sdk.gameAchievements.listStatIdentifiers,
|
|
3431
3721
|
{},
|
|
3432
3722
|
(ids) => {
|
|
3433
3723
|
this.knownStatIds = new Set(ids);
|
|
@@ -3437,7 +3727,7 @@ var StatsManager = class extends WavedashManager {
|
|
|
3437
3727
|
}
|
|
3438
3728
|
),
|
|
3439
3729
|
this.sdk.convexClient.onUpdate(
|
|
3440
|
-
|
|
3730
|
+
api8.sdk.gameAchievements.listAchievementIdentifiers,
|
|
3441
3731
|
{},
|
|
3442
3732
|
(ids) => {
|
|
3443
3733
|
this.knownAchievementIds = new Set(ids);
|
|
@@ -3447,7 +3737,7 @@ var StatsManager = class extends WavedashManager {
|
|
|
3447
3737
|
}
|
|
3448
3738
|
),
|
|
3449
3739
|
this.sdk.convexClient.onUpdate(
|
|
3450
|
-
|
|
3740
|
+
api8.sdk.gameAchievements.getMyAchievementsForGame,
|
|
3451
3741
|
{},
|
|
3452
3742
|
(achievements) => {
|
|
3453
3743
|
this.loaded.achievements = true;
|
|
@@ -3463,7 +3753,7 @@ var StatsManager = class extends WavedashManager {
|
|
|
3463
3753
|
}
|
|
3464
3754
|
async requestStats() {
|
|
3465
3755
|
const newStats = await this.sdk.convexClient.query(
|
|
3466
|
-
|
|
3756
|
+
api8.sdk.gameAchievements.getMyStatsForGame,
|
|
3467
3757
|
{}
|
|
3468
3758
|
);
|
|
3469
3759
|
this.loaded.stats = true;
|
|
@@ -3494,11 +3784,11 @@ var StatsManager = class extends WavedashManager {
|
|
|
3494
3784
|
if (!pending) return;
|
|
3495
3785
|
this.inFlightPersist = Promise.all([
|
|
3496
3786
|
pending.stats.length > 0 ? this.sdk.convexClient.mutation(
|
|
3497
|
-
|
|
3787
|
+
api8.sdk.gameAchievements.setUserGameStats,
|
|
3498
3788
|
{ stats: pending.stats }
|
|
3499
3789
|
) : Promise.resolve(),
|
|
3500
3790
|
pending.achievements.length > 0 ? this.sdk.convexClient.mutation(
|
|
3501
|
-
|
|
3791
|
+
api8.sdk.gameAchievements.setUserGameAchievements,
|
|
3502
3792
|
{ achievements: pending.achievements }
|
|
3503
3793
|
) : Promise.resolve()
|
|
3504
3794
|
]).then(() => {
|
|
@@ -3575,14 +3865,14 @@ var StatsManager = class extends WavedashManager {
|
|
|
3575
3865
|
};
|
|
3576
3866
|
|
|
3577
3867
|
// src/services/ugc.ts
|
|
3578
|
-
import { api as
|
|
3868
|
+
import { api as api9 } from "@wvdsh/api";
|
|
3579
3869
|
var UGCManager = class extends WavedashManager {
|
|
3580
3870
|
constructor(sdk) {
|
|
3581
3871
|
super(sdk);
|
|
3582
3872
|
}
|
|
3583
3873
|
async createUGCItem(ugcType, title, description, visibility, filePath) {
|
|
3584
3874
|
const { ugcId, uploadUrl } = await this.sdk.convexClient.mutation(
|
|
3585
|
-
|
|
3875
|
+
api9.sdk.userGeneratedContent.createUGCItem,
|
|
3586
3876
|
{
|
|
3587
3877
|
ugcType,
|
|
3588
3878
|
title,
|
|
@@ -3609,7 +3899,7 @@ var UGCManager = class extends WavedashManager {
|
|
|
3609
3899
|
async updateUGCItem(ugcId, updates = {}) {
|
|
3610
3900
|
const { title, description, visibility, filePath } = updates;
|
|
3611
3901
|
const { uploadUrl } = await this.sdk.convexClient.mutation(
|
|
3612
|
-
|
|
3902
|
+
api9.sdk.userGeneratedContent.updateUGCItem,
|
|
3613
3903
|
{
|
|
3614
3904
|
ugcId,
|
|
3615
3905
|
title,
|
|
@@ -3635,14 +3925,14 @@ var UGCManager = class extends WavedashManager {
|
|
|
3635
3925
|
}
|
|
3636
3926
|
async deleteUGCItem(ugcId) {
|
|
3637
3927
|
await this.sdk.convexClient.mutation(
|
|
3638
|
-
|
|
3928
|
+
api9.sdk.userGeneratedContent.deleteUGCItem,
|
|
3639
3929
|
{ ugcId }
|
|
3640
3930
|
);
|
|
3641
3931
|
return ugcId;
|
|
3642
3932
|
}
|
|
3643
3933
|
async downloadUGCItem(ugcId, filePath) {
|
|
3644
3934
|
const downloadUrl = await this.sdk.convexClient.query(
|
|
3645
|
-
|
|
3935
|
+
api9.sdk.userGeneratedContent.getUGCItemDownloadUrl,
|
|
3646
3936
|
{ ugcId }
|
|
3647
3937
|
);
|
|
3648
3938
|
try {
|
|
@@ -3657,7 +3947,7 @@ var UGCManager = class extends WavedashManager {
|
|
|
3657
3947
|
const { createdBy, ugcType, titleSearch, numItems, continueCursor } = args;
|
|
3658
3948
|
const filters = createdBy !== void 0 || ugcType !== void 0 || titleSearch !== void 0 ? { createdBy, ugcType, titleSearch } : void 0;
|
|
3659
3949
|
return await this.sdk.convexClient.query(
|
|
3660
|
-
|
|
3950
|
+
api9.sdk.userGeneratedContent.listUGCItems,
|
|
3661
3951
|
{
|
|
3662
3952
|
filters,
|
|
3663
3953
|
numItems,
|
|
@@ -3667,15 +3957,6 @@ var UGCManager = class extends WavedashManager {
|
|
|
3667
3957
|
}
|
|
3668
3958
|
};
|
|
3669
3959
|
|
|
3670
|
-
// src/utils/parentOrigin.ts
|
|
3671
|
-
var _parentOrigin = "";
|
|
3672
|
-
function setParentOrigin(origin) {
|
|
3673
|
-
_parentOrigin = origin;
|
|
3674
|
-
}
|
|
3675
|
-
function getParentOrigin() {
|
|
3676
|
-
return _parentOrigin;
|
|
3677
|
-
}
|
|
3678
|
-
|
|
3679
3960
|
// src/utils/iframeMessenger.ts
|
|
3680
3961
|
var RESPONSE_TIMEOUT_MS = 15e3;
|
|
3681
3962
|
var IFrameMessenger = class {
|
|
@@ -5119,6 +5400,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
5119
5400
|
const refreshQuery = new URLSearchParams({
|
|
5120
5401
|
[UrlParams.Caller]: PlayRouteCaller.Wavedash
|
|
5121
5402
|
});
|
|
5403
|
+
if (forceRefresh) refreshQuery.set("fresh", "1");
|
|
5122
5404
|
const refreshPath = `/auth/refresh?${refreshQuery.toString()}`;
|
|
5123
5405
|
const response = await fetch(refreshPath, {
|
|
5124
5406
|
method: "POST",
|