@wvdsh/sdk-js 1.2.2 → 1.2.3
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 +89 -7
- package/dist/index.js +203 -78
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ConvexClient } from 'convex/browser';
|
|
|
2
2
|
import { GenericId } from 'convex/values';
|
|
3
3
|
export { GenericId as Id } from 'convex/values';
|
|
4
4
|
import { FunctionReturnType } from 'convex/server';
|
|
5
|
-
import { PublicApiType, api, GAME_ENGINE, SDKUser,
|
|
5
|
+
import { PublicApiType, api, GAME_ENGINE, SDKUser, IFrameEventPayloadMap, IFRAME_MESSAGE_TYPE, SDKConfig, GameLaunchParams } from '@wvdsh/api';
|
|
6
6
|
export { GameLaunchParams } from '@wvdsh/api';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -30,6 +30,7 @@ declare const WavedashEvents: {
|
|
|
30
30
|
readonly BACKEND_CONNECTED: "BackendConnected";
|
|
31
31
|
readonly BACKEND_DISCONNECTED: "BackendDisconnected";
|
|
32
32
|
readonly BACKEND_RECONNECTING: "BackendReconnecting";
|
|
33
|
+
readonly FULLSCREEN_CHANGED: "FullscreenChanged";
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
type LobbyVisibility = PublicApiType["sdk"]["gameLobby"]["createAndJoinLobby"]["_args"]["visibility"];
|
|
@@ -590,6 +591,63 @@ declare class GameEventManager {
|
|
|
590
591
|
flushEventQueue(): void;
|
|
591
592
|
}
|
|
592
593
|
|
|
594
|
+
/**
|
|
595
|
+
* FullscreenManager
|
|
596
|
+
*
|
|
597
|
+
* Wavedash owns the fullscreen target (a wrapper DIV on the host page that
|
|
598
|
+
* contains both the game iframe and our overlay UI). The SDK inside the iframe
|
|
599
|
+
* therefore can't call `requestFullscreen` directly — it asks the parent to
|
|
600
|
+
* do it via postMessage, and the parent broadcasts state changes back through
|
|
601
|
+
* FULLSCREEN_CHANGED so we can keep a local mirror of `isFullscreen`.
|
|
602
|
+
*
|
|
603
|
+
* User activation: browsers require a fresh user gesture to enter fullscreen.
|
|
604
|
+
* The click happens in the iframe, User Activation v2 propagates transient
|
|
605
|
+
* activation to ancestor frames, and the parent's message handler runs within
|
|
606
|
+
* the ~5s window — so the parent's requestFullscreen call stays activated.
|
|
607
|
+
*
|
|
608
|
+
* Legacy compat: games that call `element.requestFullscreen()` or listen for
|
|
609
|
+
* `fullscreenchange` directly are monkey-patched in the constructor so those
|
|
610
|
+
* calls route through us. The iframe isn't granted the fullscreen feature
|
|
611
|
+
* policy anymore, so without these shims those calls would silently reject.
|
|
612
|
+
*/
|
|
613
|
+
declare class FullscreenManager {
|
|
614
|
+
private _isFullscreen;
|
|
615
|
+
private listeners;
|
|
616
|
+
private sdk;
|
|
617
|
+
constructor(sdk: WavedashSDK);
|
|
618
|
+
isFullscreen(): boolean;
|
|
619
|
+
/**
|
|
620
|
+
* Ask the host to enter (true) or exit (false) fullscreen. Resolves to
|
|
621
|
+
* `true` if the host reports the operation succeeded, `false` otherwise
|
|
622
|
+
* (e.g. browser rejected for lack of user activation).
|
|
623
|
+
*/
|
|
624
|
+
requestFullscreen(fullscreen: boolean): Promise<boolean>;
|
|
625
|
+
toggleFullscreen(): Promise<boolean>;
|
|
626
|
+
/** Subscribe to state flips. Returns an unsubscribe fn. */
|
|
627
|
+
subscribe(listener: (isFullscreen: boolean) => void): () => void;
|
|
628
|
+
private setState;
|
|
629
|
+
private installCompatShims;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* OverlayManager
|
|
634
|
+
*
|
|
635
|
+
* Owns the iframe ↔ parent interactions for the Wavedash overlay UI:
|
|
636
|
+
* - Shift+Tab inside the iframe toggles the overlay on the host page
|
|
637
|
+
* (the host owns the overlay, so we postMessage up).
|
|
638
|
+
* - When the parent closes the overlay it sends TAKE_FOCUS so keyboard
|
|
639
|
+
* input goes back to the game; we walk the DOM for a focusable target.
|
|
640
|
+
* - `takeFocus()` is also called after load completes so the game starts
|
|
641
|
+
* with keyboard focus without the player clicking first.
|
|
642
|
+
*/
|
|
643
|
+
declare class OverlayManager {
|
|
644
|
+
private sdk;
|
|
645
|
+
constructor(sdk: WavedashSDK);
|
|
646
|
+
toggleOverlay(): void;
|
|
647
|
+
takeFocus(): void;
|
|
648
|
+
private handleKeyDown;
|
|
649
|
+
}
|
|
650
|
+
|
|
593
651
|
/**
|
|
594
652
|
* Friends service
|
|
595
653
|
*
|
|
@@ -659,18 +717,22 @@ declare class WavedashLogger implements Logger {
|
|
|
659
717
|
* TODO: Look into Vercel's BIDC for this https://github.com/vercel/bidc
|
|
660
718
|
*/
|
|
661
719
|
|
|
720
|
+
type PushType = keyof IFrameEventPayloadMap;
|
|
721
|
+
type PushListener<T extends PushType> = (data: IFrameEventPayloadMap[T]) => void;
|
|
662
722
|
declare class IFrameMessenger {
|
|
663
723
|
private pendingRequests;
|
|
664
724
|
private requestIdCounter;
|
|
725
|
+
private listeners;
|
|
665
726
|
constructor();
|
|
666
|
-
private handleMessage;
|
|
667
|
-
postToParent(requestType: (typeof IFRAME_MESSAGE_TYPE)[keyof typeof IFRAME_MESSAGE_TYPE], data: Record<string, string | number | boolean>): boolean;
|
|
668
727
|
/**
|
|
669
|
-
* Register
|
|
670
|
-
*
|
|
671
|
-
*
|
|
728
|
+
* Register a handler for a one-way (no requestId) push from the parent —
|
|
729
|
+
* e.g. FULLSCREEN_CHANGED or TAKE_FOCUS. Multiple handlers per type are
|
|
730
|
+
* supported; `data` is typed from IFramePushMap.
|
|
672
731
|
*/
|
|
673
|
-
|
|
732
|
+
addEventListener<T extends PushType>(type: T, listener: PushListener<T>): void;
|
|
733
|
+
removeEventListener<T extends PushType>(type: T, listener: PushListener<T>): void;
|
|
734
|
+
private handleMessage;
|
|
735
|
+
postToParent(requestType: (typeof IFRAME_MESSAGE_TYPE)[keyof typeof IFRAME_MESSAGE_TYPE], data: Record<string, string | number | boolean>): boolean;
|
|
674
736
|
requestFromParent<T extends keyof IFrameEventPayloadMap>(requestType: T, data?: Record<string, unknown>): Promise<IFrameEventPayloadMap[T]>;
|
|
675
737
|
}
|
|
676
738
|
|
|
@@ -700,6 +762,7 @@ declare class WavedashSDK extends EventTarget {
|
|
|
700
762
|
readonly BACKEND_CONNECTED: "BackendConnected";
|
|
701
763
|
readonly BACKEND_DISCONNECTED: "BackendDisconnected";
|
|
702
764
|
readonly BACKEND_RECONNECTING: "BackendReconnecting";
|
|
765
|
+
readonly FULLSCREEN_CHANGED: "FullscreenChanged";
|
|
703
766
|
};
|
|
704
767
|
protected lobbyManager: LobbyManager;
|
|
705
768
|
protected statsManager: StatsManager;
|
|
@@ -718,6 +781,8 @@ declare class WavedashSDK extends EventTarget {
|
|
|
718
781
|
logger: WavedashLogger;
|
|
719
782
|
iframeMessenger: IFrameMessenger;
|
|
720
783
|
p2pManager: P2PManager;
|
|
784
|
+
fullscreenManager: FullscreenManager;
|
|
785
|
+
overlayManager: OverlayManager;
|
|
721
786
|
private gameplayJwt;
|
|
722
787
|
private gameplayJwtPromise;
|
|
723
788
|
ugcHost: string;
|
|
@@ -735,6 +800,23 @@ declare class WavedashSDK extends EventTarget {
|
|
|
735
800
|
loadComplete(): void;
|
|
736
801
|
get gameLoaded(): boolean;
|
|
737
802
|
toggleOverlay(): void;
|
|
803
|
+
/**
|
|
804
|
+
* Whether the game is currently presented in fullscreen. Mirrored from the
|
|
805
|
+
* Wavedash host page, which owns the real fullscreen target so our overlay
|
|
806
|
+
* UI stays on top of the game.
|
|
807
|
+
*/
|
|
808
|
+
isFullscreen(): boolean;
|
|
809
|
+
/**
|
|
810
|
+
* Ask the host page to enter (true) or exit (false) fullscreen. Entering
|
|
811
|
+
* must happen inside a user gesture handler (click / keydown / pointerdown)
|
|
812
|
+
* for the browser to permit it.
|
|
813
|
+
*/
|
|
814
|
+
requestFullscreen(fullscreen: boolean): Promise<boolean>;
|
|
815
|
+
/**
|
|
816
|
+
* Toggle fullscreen. Like `requestFullscreen(true)`, this must run inside
|
|
817
|
+
* a user gesture handler when entering fullscreen.
|
|
818
|
+
*/
|
|
819
|
+
toggleFullscreen(): Promise<boolean>;
|
|
738
820
|
getUser(): SDKUser;
|
|
739
821
|
/**
|
|
740
822
|
* Get a username. Returns the logged in user's username if no ID is passed.
|
package/dist/index.js
CHANGED
|
@@ -4218,8 +4218,11 @@ var WavedashEvents = {
|
|
|
4218
4218
|
// connected to Convex backend
|
|
4219
4219
|
BACKEND_DISCONNECTED: "BackendDisconnected",
|
|
4220
4220
|
// disconnected from Convex backend
|
|
4221
|
-
BACKEND_RECONNECTING: "BackendReconnecting"
|
|
4221
|
+
BACKEND_RECONNECTING: "BackendReconnecting",
|
|
4222
4222
|
// attempting to reconnect to backend
|
|
4223
|
+
// Fullscreen events
|
|
4224
|
+
FULLSCREEN_CHANGED: "FullscreenChanged"
|
|
4225
|
+
// fullscreen state changed
|
|
4223
4226
|
// TODO: Future events to implement
|
|
4224
4227
|
// P2P_CONNECTION_REQUESTED: 'P2PConnectionRequested', // for now we always connect all lobby members
|
|
4225
4228
|
};
|
|
@@ -6294,16 +6297,13 @@ var _P2PManager = class _P2PManager {
|
|
|
6294
6297
|
}
|
|
6295
6298
|
}
|
|
6296
6299
|
emitPacketDropped(tracker, droppedCount) {
|
|
6297
|
-
this.sdk.gameEventManager.notifyGame(
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
droppedTotal: tracker.droppedTotal
|
|
6305
|
-
}
|
|
6306
|
-
);
|
|
6300
|
+
this.sdk.gameEventManager.notifyGame(WavedashEvents.P2P_PACKET_DROPPED, {
|
|
6301
|
+
channel: tracker.channel,
|
|
6302
|
+
direction: tracker.direction,
|
|
6303
|
+
reason: tracker.reason,
|
|
6304
|
+
droppedCount,
|
|
6305
|
+
droppedTotal: tracker.droppedTotal
|
|
6306
|
+
});
|
|
6307
6307
|
}
|
|
6308
6308
|
clearPacketDropTrackers() {
|
|
6309
6309
|
for (const tracker of this.packetDropTrackers.values()) {
|
|
@@ -6585,11 +6585,10 @@ var StatsManager = class {
|
|
|
6585
6585
|
// Debounced persist — used by storeNow in setters to batch rapid calls.
|
|
6586
6586
|
// Leading+trailing: first call fires immediately, subsequent calls within
|
|
6587
6587
|
// the window are batched into one trailing call.
|
|
6588
|
-
this.debouncedPersist = (0, import_lodash2.default)(
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
);
|
|
6588
|
+
this.debouncedPersist = (0, import_lodash2.default)(() => this.persist(), STORE_DEBOUNCE_MS, {
|
|
6589
|
+
leading: true,
|
|
6590
|
+
trailing: true
|
|
6591
|
+
});
|
|
6593
6592
|
this.sdk = sdk;
|
|
6594
6593
|
this.subscribe();
|
|
6595
6594
|
this.requestStats().catch((error) => {
|
|
@@ -6956,6 +6955,135 @@ var GameEventManager = class {
|
|
|
6956
6955
|
}
|
|
6957
6956
|
};
|
|
6958
6957
|
|
|
6958
|
+
// src/services/fullscreen.ts
|
|
6959
|
+
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE3 } from "@wvdsh/api";
|
|
6960
|
+
var FullscreenManager = class {
|
|
6961
|
+
constructor(sdk) {
|
|
6962
|
+
this._isFullscreen = false;
|
|
6963
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
6964
|
+
this.sdk = sdk;
|
|
6965
|
+
this.sdk.iframeMessenger.addEventListener(
|
|
6966
|
+
IFRAME_MESSAGE_TYPE3.FULLSCREEN_CHANGED,
|
|
6967
|
+
(data) => {
|
|
6968
|
+
this.sdk.gameEventManager.notifyGame(
|
|
6969
|
+
WavedashEvents.FULLSCREEN_CHANGED,
|
|
6970
|
+
data.isFullscreen
|
|
6971
|
+
);
|
|
6972
|
+
this.setState(data.isFullscreen);
|
|
6973
|
+
}
|
|
6974
|
+
);
|
|
6975
|
+
this.installCompatShims();
|
|
6976
|
+
}
|
|
6977
|
+
isFullscreen() {
|
|
6978
|
+
return this._isFullscreen;
|
|
6979
|
+
}
|
|
6980
|
+
/**
|
|
6981
|
+
* Ask the host to enter (true) or exit (false) fullscreen. Resolves to
|
|
6982
|
+
* `true` if the host reports the operation succeeded, `false` otherwise
|
|
6983
|
+
* (e.g. browser rejected for lack of user activation).
|
|
6984
|
+
*/
|
|
6985
|
+
async requestFullscreen(fullscreen) {
|
|
6986
|
+
const response = await this.sdk.iframeMessenger.requestFromParent(
|
|
6987
|
+
IFRAME_MESSAGE_TYPE3.SET_FULLSCREEN,
|
|
6988
|
+
{ fullscreen }
|
|
6989
|
+
);
|
|
6990
|
+
return response.success;
|
|
6991
|
+
}
|
|
6992
|
+
async toggleFullscreen() {
|
|
6993
|
+
const response = await this.sdk.iframeMessenger.requestFromParent(
|
|
6994
|
+
IFRAME_MESSAGE_TYPE3.TOGGLE_FULLSCREEN
|
|
6995
|
+
);
|
|
6996
|
+
return response.success;
|
|
6997
|
+
}
|
|
6998
|
+
/** Subscribe to state flips. Returns an unsubscribe fn. */
|
|
6999
|
+
subscribe(listener) {
|
|
7000
|
+
this.listeners.add(listener);
|
|
7001
|
+
return () => this.listeners.delete(listener);
|
|
7002
|
+
}
|
|
7003
|
+
setState(isFullscreen) {
|
|
7004
|
+
if (this._isFullscreen === isFullscreen) return;
|
|
7005
|
+
this._isFullscreen = isFullscreen;
|
|
7006
|
+
for (const listener of this.listeners) listener(isFullscreen);
|
|
7007
|
+
}
|
|
7008
|
+
installCompatShims() {
|
|
7009
|
+
if (typeof document === "undefined") return;
|
|
7010
|
+
const fullscreenElementGetter = () => this._isFullscreen ? document.body : null;
|
|
7011
|
+
Object.defineProperty(document, "fullscreenElement", {
|
|
7012
|
+
configurable: true,
|
|
7013
|
+
get: fullscreenElementGetter
|
|
7014
|
+
});
|
|
7015
|
+
Object.defineProperty(document, "webkitFullscreenElement", {
|
|
7016
|
+
configurable: true,
|
|
7017
|
+
get: fullscreenElementGetter
|
|
7018
|
+
});
|
|
7019
|
+
const enter = async () => {
|
|
7020
|
+
if (this._isFullscreen) return;
|
|
7021
|
+
const ok = await this.requestFullscreen(true);
|
|
7022
|
+
if (!ok) throw new Error("Fullscreen request was denied");
|
|
7023
|
+
};
|
|
7024
|
+
const exit = async () => {
|
|
7025
|
+
if (!this._isFullscreen) return;
|
|
7026
|
+
const ok = await this.requestFullscreen(false);
|
|
7027
|
+
if (!ok) throw new Error("Exit fullscreen request was denied");
|
|
7028
|
+
};
|
|
7029
|
+
Element.prototype.requestFullscreen = function() {
|
|
7030
|
+
return enter();
|
|
7031
|
+
};
|
|
7032
|
+
Element.prototype.webkitRequestFullscreen = function() {
|
|
7033
|
+
return enter();
|
|
7034
|
+
};
|
|
7035
|
+
Document.prototype.exitFullscreen = function() {
|
|
7036
|
+
return exit();
|
|
7037
|
+
};
|
|
7038
|
+
Document.prototype.webkitExitFullscreen = function() {
|
|
7039
|
+
return exit();
|
|
7040
|
+
};
|
|
7041
|
+
this.subscribe(() => {
|
|
7042
|
+
document.dispatchEvent(new Event("fullscreenchange", { bubbles: true }));
|
|
7043
|
+
});
|
|
7044
|
+
}
|
|
7045
|
+
};
|
|
7046
|
+
|
|
7047
|
+
// src/services/overlay.ts
|
|
7048
|
+
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE4 } from "@wvdsh/api";
|
|
7049
|
+
var OverlayManager = class {
|
|
7050
|
+
constructor(sdk) {
|
|
7051
|
+
this.handleKeyDown = (event) => {
|
|
7052
|
+
if (event.defaultPrevented) return;
|
|
7053
|
+
if (event.key === "Tab" && event.shiftKey) {
|
|
7054
|
+
event.preventDefault();
|
|
7055
|
+
this.toggleOverlay();
|
|
7056
|
+
}
|
|
7057
|
+
};
|
|
7058
|
+
this.sdk = sdk;
|
|
7059
|
+
this.sdk.iframeMessenger.addEventListener(
|
|
7060
|
+
IFRAME_MESSAGE_TYPE4.TAKE_FOCUS,
|
|
7061
|
+
() => this.takeFocus()
|
|
7062
|
+
);
|
|
7063
|
+
if (typeof window !== "undefined") {
|
|
7064
|
+
window.addEventListener("keydown", this.handleKeyDown);
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
toggleOverlay() {
|
|
7068
|
+
this.sdk.iframeMessenger.postToParent(
|
|
7069
|
+
IFRAME_MESSAGE_TYPE4.TOGGLE_OVERLAY,
|
|
7070
|
+
{}
|
|
7071
|
+
);
|
|
7072
|
+
}
|
|
7073
|
+
takeFocus() {
|
|
7074
|
+
if (typeof document === "undefined") return;
|
|
7075
|
+
const gameFocusTargets = document.getElementsByClassName("game-focus-target");
|
|
7076
|
+
if (gameFocusTargets.length > 0) {
|
|
7077
|
+
gameFocusTargets[0].focus();
|
|
7078
|
+
return;
|
|
7079
|
+
}
|
|
7080
|
+
const focusableElement = document.querySelector(
|
|
7081
|
+
"canvas, input, button, [tabindex]:not([tabindex='-1'])"
|
|
7082
|
+
);
|
|
7083
|
+
focusableElement?.focus();
|
|
7084
|
+
}
|
|
7085
|
+
};
|
|
7086
|
+
|
|
6959
7087
|
// src/services/friends.ts
|
|
6960
7088
|
import { api as api8 } from "@wvdsh/api";
|
|
6961
7089
|
|
|
@@ -7069,26 +7197,6 @@ var WavedashLogger = class {
|
|
|
7069
7197
|
}
|
|
7070
7198
|
};
|
|
7071
7199
|
|
|
7072
|
-
// src/utils/iframeMessenger.ts
|
|
7073
|
-
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE3 } from "@wvdsh/api";
|
|
7074
|
-
|
|
7075
|
-
// src/utils/focusManager.ts
|
|
7076
|
-
function takeFocus() {
|
|
7077
|
-
if (typeof document !== "undefined") {
|
|
7078
|
-
const gameFocusTargets = document.getElementsByClassName("game-focus-target");
|
|
7079
|
-
if (gameFocusTargets.length > 0) {
|
|
7080
|
-
gameFocusTargets[0].focus();
|
|
7081
|
-
} else {
|
|
7082
|
-
const focusableElement = document.querySelector(
|
|
7083
|
-
"canvas, input, button, [tabindex]:not([tabindex='-1'])"
|
|
7084
|
-
);
|
|
7085
|
-
if (focusableElement) {
|
|
7086
|
-
focusableElement.focus();
|
|
7087
|
-
}
|
|
7088
|
-
}
|
|
7089
|
-
}
|
|
7090
|
-
}
|
|
7091
|
-
|
|
7092
7200
|
// src/utils/parentOrigin.ts
|
|
7093
7201
|
function deriveParentOrigin() {
|
|
7094
7202
|
if (typeof window === "undefined") return "";
|
|
@@ -7122,46 +7230,42 @@ var IFrameMessenger = class {
|
|
|
7122
7230
|
this.pendingRequests.delete(event.data.requestId);
|
|
7123
7231
|
pending.resolve(event.data.data);
|
|
7124
7232
|
}
|
|
7125
|
-
|
|
7126
|
-
takeFocus();
|
|
7233
|
+
return;
|
|
7127
7234
|
}
|
|
7235
|
+
const messageType = event.data?.type;
|
|
7236
|
+
if (!messageType) return;
|
|
7237
|
+
const set = this.listeners.get(messageType);
|
|
7238
|
+
if (!set) return;
|
|
7239
|
+
for (const listener of set) listener(event.data);
|
|
7128
7240
|
};
|
|
7129
7241
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
7130
7242
|
this.requestIdCounter = 0;
|
|
7243
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
7131
7244
|
if (typeof window !== "undefined") {
|
|
7132
7245
|
window.addEventListener("message", this.handleMessage);
|
|
7133
7246
|
}
|
|
7134
7247
|
}
|
|
7248
|
+
/**
|
|
7249
|
+
* Register a handler for a one-way (no requestId) push from the parent —
|
|
7250
|
+
* e.g. FULLSCREEN_CHANGED or TAKE_FOCUS. Multiple handlers per type are
|
|
7251
|
+
* supported; `data` is typed from IFramePushMap.
|
|
7252
|
+
*/
|
|
7253
|
+
addEventListener(type, listener) {
|
|
7254
|
+
let set = this.listeners.get(type);
|
|
7255
|
+
if (!set) {
|
|
7256
|
+
set = /* @__PURE__ */ new Set();
|
|
7257
|
+
this.listeners.set(type, set);
|
|
7258
|
+
}
|
|
7259
|
+
set.add(listener);
|
|
7260
|
+
}
|
|
7261
|
+
removeEventListener(type, listener) {
|
|
7262
|
+
this.listeners.get(type)?.delete(listener);
|
|
7263
|
+
}
|
|
7135
7264
|
postToParent(requestType, data) {
|
|
7136
7265
|
if (typeof window === "undefined" || !parentOrigin) return false;
|
|
7137
7266
|
window.parent.postMessage({ type: requestType, ...data }, parentOrigin);
|
|
7138
7267
|
return true;
|
|
7139
7268
|
}
|
|
7140
|
-
/**
|
|
7141
|
-
* Register global keyboard/mouse handlers for iframe communication.
|
|
7142
|
-
* Handles F3 prevention, initial interaction signaling, and Tab+Shift overlay toggle.
|
|
7143
|
-
* Called once during SDK setup.
|
|
7144
|
-
*/
|
|
7145
|
-
registerEventHandlers() {
|
|
7146
|
-
let sentInitialInteraction = false;
|
|
7147
|
-
const handleInteraction = () => {
|
|
7148
|
-
if (!sentInitialInteraction) {
|
|
7149
|
-
sentInitialInteraction = true;
|
|
7150
|
-
this.postToParent(IFRAME_MESSAGE_TYPE3.INITIAL_INTERACTION, {});
|
|
7151
|
-
}
|
|
7152
|
-
};
|
|
7153
|
-
window.addEventListener("keydown", (event) => {
|
|
7154
|
-
if (event.key === "F3") {
|
|
7155
|
-
event.preventDefault();
|
|
7156
|
-
}
|
|
7157
|
-
handleInteraction();
|
|
7158
|
-
if (event.key === "Tab" && event.shiftKey) {
|
|
7159
|
-
event.preventDefault();
|
|
7160
|
-
this.postToParent(IFRAME_MESSAGE_TYPE3.TOGGLE_OVERLAY, {});
|
|
7161
|
-
}
|
|
7162
|
-
});
|
|
7163
|
-
window.addEventListener("mousedown", handleInteraction);
|
|
7164
|
-
}
|
|
7165
7269
|
async requestFromParent(requestType, data) {
|
|
7166
7270
|
return new Promise((resolve, reject) => {
|
|
7167
7271
|
if (typeof window === "undefined" || !parentOrigin) {
|
|
@@ -7193,7 +7297,7 @@ var IFrameMessenger = class {
|
|
|
7193
7297
|
// src/index.ts
|
|
7194
7298
|
import {
|
|
7195
7299
|
GAME_ENGINE,
|
|
7196
|
-
IFRAME_MESSAGE_TYPE as
|
|
7300
|
+
IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE5,
|
|
7197
7301
|
LEADERBOARD_DISPLAY_TYPE,
|
|
7198
7302
|
LEADERBOARD_SORT_ORDER,
|
|
7199
7303
|
LOBBY_VISIBILITY,
|
|
@@ -7206,9 +7310,7 @@ import {
|
|
|
7206
7310
|
var CONVEX_ID_REGEX = /^[0-9a-z]{31,37}$/;
|
|
7207
7311
|
var vString = (value, path) => {
|
|
7208
7312
|
if (typeof value !== "string") {
|
|
7209
|
-
throw new Error(
|
|
7210
|
-
`${path}: expected string, got ${describeValue(value)}`
|
|
7211
|
-
);
|
|
7313
|
+
throw new Error(`${path}: expected string, got ${describeValue(value)}`);
|
|
7212
7314
|
}
|
|
7213
7315
|
return value;
|
|
7214
7316
|
};
|
|
@@ -7222,9 +7324,7 @@ var vNumber = (value, path) => {
|
|
|
7222
7324
|
};
|
|
7223
7325
|
var vBoolean = (value, path) => {
|
|
7224
7326
|
if (typeof value !== "boolean") {
|
|
7225
|
-
throw new Error(
|
|
7226
|
-
`${path}: expected boolean, got ${describeValue(value)}`
|
|
7227
|
-
);
|
|
7327
|
+
throw new Error(`${path}: expected boolean, got ${describeValue(value)}`);
|
|
7228
7328
|
}
|
|
7229
7329
|
return value;
|
|
7230
7330
|
};
|
|
@@ -7287,9 +7387,7 @@ function vUnion(...variants) {
|
|
|
7287
7387
|
} catch {
|
|
7288
7388
|
}
|
|
7289
7389
|
}
|
|
7290
|
-
throw new Error(
|
|
7291
|
-
`${path}: no variant matched, got ${describeValue(value)}`
|
|
7292
|
-
);
|
|
7390
|
+
throw new Error(`${path}: no variant matched, got ${describeValue(value)}`);
|
|
7293
7391
|
};
|
|
7294
7392
|
}
|
|
7295
7393
|
function validateArgs(methodName, specs, values) {
|
|
@@ -7349,6 +7447,8 @@ var WavedashSDK = class extends EventTarget {
|
|
|
7349
7447
|
this.leaderboardManager = new LeaderboardManager(this);
|
|
7350
7448
|
this.friendsManager = new FriendsManager(this);
|
|
7351
7449
|
this.gameEventManager = new GameEventManager(this);
|
|
7450
|
+
this.fullscreenManager = new FullscreenManager(this);
|
|
7451
|
+
this.overlayManager = new OverlayManager(this);
|
|
7352
7452
|
this.friendsManager.cacheUsers([
|
|
7353
7453
|
{
|
|
7354
7454
|
userId: this.wavedashUser.id,
|
|
@@ -7426,7 +7526,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
7426
7526
|
[["progress", vNumber]],
|
|
7427
7527
|
[progress]
|
|
7428
7528
|
);
|
|
7429
|
-
iframeMessenger.postToParent(
|
|
7529
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE5.PROGRESS_UPDATE, {
|
|
7430
7530
|
progress
|
|
7431
7531
|
});
|
|
7432
7532
|
}
|
|
@@ -7434,14 +7534,40 @@ var WavedashSDK = class extends EventTarget {
|
|
|
7434
7534
|
if (this.gameFinishedLoading) return;
|
|
7435
7535
|
this.gameFinishedLoading = true;
|
|
7436
7536
|
this.heartbeatManager.start();
|
|
7437
|
-
iframeMessenger.postToParent(
|
|
7438
|
-
takeFocus();
|
|
7537
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE5.LOADING_COMPLETE, {});
|
|
7538
|
+
this.overlayManager.takeFocus();
|
|
7439
7539
|
}
|
|
7440
7540
|
get gameLoaded() {
|
|
7441
7541
|
return this.gameFinishedLoading;
|
|
7442
7542
|
}
|
|
7443
7543
|
toggleOverlay() {
|
|
7444
|
-
|
|
7544
|
+
this.overlayManager.toggleOverlay();
|
|
7545
|
+
}
|
|
7546
|
+
// ==========
|
|
7547
|
+
// Fullscreen
|
|
7548
|
+
// ==========
|
|
7549
|
+
/**
|
|
7550
|
+
* Whether the game is currently presented in fullscreen. Mirrored from the
|
|
7551
|
+
* Wavedash host page, which owns the real fullscreen target so our overlay
|
|
7552
|
+
* UI stays on top of the game.
|
|
7553
|
+
*/
|
|
7554
|
+
isFullscreen() {
|
|
7555
|
+
return this.fullscreenManager.isFullscreen();
|
|
7556
|
+
}
|
|
7557
|
+
/**
|
|
7558
|
+
* Ask the host page to enter (true) or exit (false) fullscreen. Entering
|
|
7559
|
+
* must happen inside a user gesture handler (click / keydown / pointerdown)
|
|
7560
|
+
* for the browser to permit it.
|
|
7561
|
+
*/
|
|
7562
|
+
async requestFullscreen(fullscreen) {
|
|
7563
|
+
return this.fullscreenManager.requestFullscreen(fullscreen);
|
|
7564
|
+
}
|
|
7565
|
+
/**
|
|
7566
|
+
* Toggle fullscreen. Like `requestFullscreen(true)`, this must run inside
|
|
7567
|
+
* a user gesture handler when entering fullscreen.
|
|
7568
|
+
*/
|
|
7569
|
+
async toggleFullscreen() {
|
|
7570
|
+
return this.fullscreenManager.toggleFullscreen();
|
|
7445
7571
|
}
|
|
7446
7572
|
// ============
|
|
7447
7573
|
// User methods
|
|
@@ -8237,7 +8363,6 @@ var WavedashSDK = class extends EventTarget {
|
|
|
8237
8363
|
function setupWavedashSDK() {
|
|
8238
8364
|
const existing = window.WavedashJS;
|
|
8239
8365
|
if (existing) return existing;
|
|
8240
|
-
iframeMessenger.registerEventHandlers();
|
|
8241
8366
|
const raw = new URLSearchParams(window.location.search).get(
|
|
8242
8367
|
UrlParams.SdkConfig
|
|
8243
8368
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wvdsh/sdk-js",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Wavedash JavaScript SDK",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"typescript-eslint": "^8.52.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@wvdsh/api": "^0.1.
|
|
43
|
+
"@wvdsh/api": "^0.1.1",
|
|
44
44
|
"convex": "^1.28.0",
|
|
45
45
|
"lodash.debounce": "^4.0.8"
|
|
46
46
|
}
|