@wvdsh/sdk-js 1.2.2 → 1.2.4
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 +96 -10
- 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"];
|
|
@@ -72,7 +73,7 @@ interface RemoteFileMetadata {
|
|
|
72
73
|
}
|
|
73
74
|
interface EngineInstance {
|
|
74
75
|
type: (typeof GAME_ENGINE)[keyof typeof GAME_ENGINE];
|
|
75
|
-
SendMessage(objectName: string, methodName: WavedashEvent, value?: string | number
|
|
76
|
+
SendMessage(objectName: string, methodName: WavedashEvent, value?: string | number): void;
|
|
76
77
|
FS: {
|
|
77
78
|
readFile(path: string, opts?: Record<string, unknown>): string | Uint8Array;
|
|
78
79
|
writeFile(path: string, data: string | ArrayBufferView, opts?: Record<string, unknown>): void;
|
|
@@ -184,6 +185,10 @@ interface BackendConnectionPayload {
|
|
|
184
185
|
connectionCount: number;
|
|
185
186
|
connectionRetries: number;
|
|
186
187
|
}
|
|
188
|
+
/** Payload for FullscreenChanged event - emitted when fullscreen state flips */
|
|
189
|
+
interface FullscreenChangedPayload {
|
|
190
|
+
isFullscreen: boolean;
|
|
191
|
+
}
|
|
187
192
|
interface P2PPeer {
|
|
188
193
|
userId: GenericId<"users">;
|
|
189
194
|
username: string;
|
|
@@ -585,11 +590,68 @@ declare class GameEventManager {
|
|
|
585
590
|
private sdk;
|
|
586
591
|
private eventQueue;
|
|
587
592
|
constructor(sdk: WavedashSDK);
|
|
588
|
-
notifyGame(event: WavedashEvent, payload: string | number |
|
|
593
|
+
notifyGame(event: WavedashEvent, payload: string | number | object): void;
|
|
589
594
|
private sendGameEvent;
|
|
590
595
|
flushEventQueue(): void;
|
|
591
596
|
}
|
|
592
597
|
|
|
598
|
+
/**
|
|
599
|
+
* FullscreenManager
|
|
600
|
+
*
|
|
601
|
+
* Wavedash owns the fullscreen target (a wrapper DIV on the host page that
|
|
602
|
+
* contains both the game iframe and our overlay UI). The SDK inside the iframe
|
|
603
|
+
* therefore can't call `requestFullscreen` directly — it asks the parent to
|
|
604
|
+
* do it via postMessage, and the parent broadcasts state changes back through
|
|
605
|
+
* FULLSCREEN_CHANGED so we can keep a local mirror of `isFullscreen`.
|
|
606
|
+
*
|
|
607
|
+
* User activation: browsers require a fresh user gesture to enter fullscreen.
|
|
608
|
+
* The click happens in the iframe, User Activation v2 propagates transient
|
|
609
|
+
* activation to ancestor frames, and the parent's message handler runs within
|
|
610
|
+
* the ~5s window — so the parent's requestFullscreen call stays activated.
|
|
611
|
+
*
|
|
612
|
+
* Legacy compat: games that call `element.requestFullscreen()` or listen for
|
|
613
|
+
* `fullscreenchange` directly are monkey-patched in the constructor so those
|
|
614
|
+
* calls route through us. The iframe isn't granted the fullscreen feature
|
|
615
|
+
* policy anymore, so without these shims those calls would silently reject.
|
|
616
|
+
*/
|
|
617
|
+
declare class FullscreenManager {
|
|
618
|
+
private _isFullscreen;
|
|
619
|
+
private listeners;
|
|
620
|
+
private sdk;
|
|
621
|
+
constructor(sdk: WavedashSDK);
|
|
622
|
+
isFullscreen(): boolean;
|
|
623
|
+
/**
|
|
624
|
+
* Ask the host to enter (true) or exit (false) fullscreen. Resolves to
|
|
625
|
+
* `true` if the host reports the operation succeeded, `false` otherwise
|
|
626
|
+
* (e.g. browser rejected for lack of user activation).
|
|
627
|
+
*/
|
|
628
|
+
requestFullscreen(fullscreen: boolean): Promise<boolean>;
|
|
629
|
+
toggleFullscreen(): Promise<boolean>;
|
|
630
|
+
/** Subscribe to state flips. Returns an unsubscribe fn. */
|
|
631
|
+
subscribe(listener: (isFullscreen: boolean) => void): () => void;
|
|
632
|
+
private setState;
|
|
633
|
+
private installCompatShims;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* OverlayManager
|
|
638
|
+
*
|
|
639
|
+
* Owns the iframe ↔ parent interactions for the Wavedash overlay UI:
|
|
640
|
+
* - Shift+Tab inside the iframe toggles the overlay on the host page
|
|
641
|
+
* (the host owns the overlay, so we postMessage up).
|
|
642
|
+
* - When the parent closes the overlay it sends TAKE_FOCUS so keyboard
|
|
643
|
+
* input goes back to the game; we walk the DOM for a focusable target.
|
|
644
|
+
* - `takeFocus()` is also called after load completes so the game starts
|
|
645
|
+
* with keyboard focus without the player clicking first.
|
|
646
|
+
*/
|
|
647
|
+
declare class OverlayManager {
|
|
648
|
+
private sdk;
|
|
649
|
+
constructor(sdk: WavedashSDK);
|
|
650
|
+
toggleOverlay(): void;
|
|
651
|
+
takeFocus(): void;
|
|
652
|
+
private handleKeyDown;
|
|
653
|
+
}
|
|
654
|
+
|
|
593
655
|
/**
|
|
594
656
|
* Friends service
|
|
595
657
|
*
|
|
@@ -659,18 +721,22 @@ declare class WavedashLogger implements Logger {
|
|
|
659
721
|
* TODO: Look into Vercel's BIDC for this https://github.com/vercel/bidc
|
|
660
722
|
*/
|
|
661
723
|
|
|
724
|
+
type PushType = keyof IFrameEventPayloadMap;
|
|
725
|
+
type PushListener<T extends PushType> = (data: IFrameEventPayloadMap[T]) => void;
|
|
662
726
|
declare class IFrameMessenger {
|
|
663
727
|
private pendingRequests;
|
|
664
728
|
private requestIdCounter;
|
|
729
|
+
private listeners;
|
|
665
730
|
constructor();
|
|
666
|
-
private handleMessage;
|
|
667
|
-
postToParent(requestType: (typeof IFRAME_MESSAGE_TYPE)[keyof typeof IFRAME_MESSAGE_TYPE], data: Record<string, string | number | boolean>): boolean;
|
|
668
731
|
/**
|
|
669
|
-
* Register
|
|
670
|
-
*
|
|
671
|
-
*
|
|
732
|
+
* Register a handler for a one-way (no requestId) push from the parent —
|
|
733
|
+
* e.g. FULLSCREEN_CHANGED or TAKE_FOCUS. Multiple handlers per type are
|
|
734
|
+
* supported; `data` is typed from IFramePushMap.
|
|
672
735
|
*/
|
|
673
|
-
|
|
736
|
+
addEventListener<T extends PushType>(type: T, listener: PushListener<T>): void;
|
|
737
|
+
removeEventListener<T extends PushType>(type: T, listener: PushListener<T>): void;
|
|
738
|
+
private handleMessage;
|
|
739
|
+
postToParent(requestType: (typeof IFRAME_MESSAGE_TYPE)[keyof typeof IFRAME_MESSAGE_TYPE], data: Record<string, string | number | boolean>): boolean;
|
|
674
740
|
requestFromParent<T extends keyof IFrameEventPayloadMap>(requestType: T, data?: Record<string, unknown>): Promise<IFrameEventPayloadMap[T]>;
|
|
675
741
|
}
|
|
676
742
|
|
|
@@ -700,6 +766,7 @@ declare class WavedashSDK extends EventTarget {
|
|
|
700
766
|
readonly BACKEND_CONNECTED: "BackendConnected";
|
|
701
767
|
readonly BACKEND_DISCONNECTED: "BackendDisconnected";
|
|
702
768
|
readonly BACKEND_RECONNECTING: "BackendReconnecting";
|
|
769
|
+
readonly FULLSCREEN_CHANGED: "FullscreenChanged";
|
|
703
770
|
};
|
|
704
771
|
protected lobbyManager: LobbyManager;
|
|
705
772
|
protected statsManager: StatsManager;
|
|
@@ -718,6 +785,8 @@ declare class WavedashSDK extends EventTarget {
|
|
|
718
785
|
logger: WavedashLogger;
|
|
719
786
|
iframeMessenger: IFrameMessenger;
|
|
720
787
|
p2pManager: P2PManager;
|
|
788
|
+
fullscreenManager: FullscreenManager;
|
|
789
|
+
overlayManager: OverlayManager;
|
|
721
790
|
private gameplayJwt;
|
|
722
791
|
private gameplayJwtPromise;
|
|
723
792
|
ugcHost: string;
|
|
@@ -735,6 +804,23 @@ declare class WavedashSDK extends EventTarget {
|
|
|
735
804
|
loadComplete(): void;
|
|
736
805
|
get gameLoaded(): boolean;
|
|
737
806
|
toggleOverlay(): void;
|
|
807
|
+
/**
|
|
808
|
+
* Whether the game is currently presented in fullscreen. Mirrored from the
|
|
809
|
+
* Wavedash host page, which owns the real fullscreen target so our overlay
|
|
810
|
+
* UI stays on top of the game.
|
|
811
|
+
*/
|
|
812
|
+
isFullscreen(): boolean;
|
|
813
|
+
/**
|
|
814
|
+
* Ask the host page to enter (true) or exit (false) fullscreen. Entering
|
|
815
|
+
* must happen inside a user gesture handler (click / keydown / pointerdown)
|
|
816
|
+
* for the browser to permit it.
|
|
817
|
+
*/
|
|
818
|
+
requestFullscreen(fullscreen: boolean): Promise<boolean>;
|
|
819
|
+
/**
|
|
820
|
+
* Toggle fullscreen. Like `requestFullscreen(true)`, this must run inside
|
|
821
|
+
* a user gesture handler when entering fullscreen.
|
|
822
|
+
*/
|
|
823
|
+
toggleFullscreen(): Promise<boolean>;
|
|
738
824
|
getUser(): SDKUser;
|
|
739
825
|
/**
|
|
740
826
|
* Get a username. Returns the logged in user's username if no ID is passed.
|
|
@@ -969,4 +1055,4 @@ declare class WavedashSDK extends EventTarget {
|
|
|
969
1055
|
|
|
970
1056
|
declare function setupWavedashSDK(): WavedashSDK;
|
|
971
1057
|
|
|
972
|
-
export { AVATAR_SIZE_LARGE, AVATAR_SIZE_MEDIUM, AVATAR_SIZE_SMALL, type BackendConnectionPayload, type EngineInstance, type Friend, type Leaderboard, type LeaderboardDisplayType, type LeaderboardEntries, type LeaderboardSortOrder, type Lobby, type LobbyDataUpdatedPayload, type LobbyInvite, type LobbyInvitePayload, type LobbyJoinResponse, type LobbyJoinedPayload, type LobbyKickedPayload, LobbyKickedReason, type LobbyMessage, type LobbyMessagePayload, type LobbyUser, LobbyUserChangeType, type LobbyUsersUpdatedPayload, type LobbyVisibility, type P2PConfig, type P2PConnection, type P2PConnectionEstablishedPayload, type P2PConnectionFailedPayload, type P2PMessage, type P2PPacketDropReason, type P2PPacketDroppedPayload, type P2PPeer, type P2PPeerDisconnectedPayload, type P2PPeerReconnectedPayload, type P2PPeerReconnectingPayload, type P2PSignalingMessage, type P2PTurnCredentials, type RemoteFileMetadata, type StatsStoredPayload, type UGCType, type UGCVisibility, type UpsertedLeaderboardEntry, type WavedashConfig, type WavedashEvent, WavedashEvents, type WavedashResponse, WavedashSDK, setupWavedashSDK };
|
|
1058
|
+
export { AVATAR_SIZE_LARGE, AVATAR_SIZE_MEDIUM, AVATAR_SIZE_SMALL, type BackendConnectionPayload, type EngineInstance, type Friend, type FullscreenChangedPayload, type Leaderboard, type LeaderboardDisplayType, type LeaderboardEntries, type LeaderboardSortOrder, type Lobby, type LobbyDataUpdatedPayload, type LobbyInvite, type LobbyInvitePayload, type LobbyJoinResponse, type LobbyJoinedPayload, type LobbyKickedPayload, LobbyKickedReason, type LobbyMessage, type LobbyMessagePayload, type LobbyUser, LobbyUserChangeType, type LobbyUsersUpdatedPayload, type LobbyVisibility, type P2PConfig, type P2PConnection, type P2PConnectionEstablishedPayload, type P2PConnectionFailedPayload, type P2PMessage, type P2PPacketDropReason, type P2PPacketDroppedPayload, type P2PPeer, type P2PPeerDisconnectedPayload, type P2PPeerReconnectedPayload, type P2PPeerReconnectingPayload, type P2PSignalingMessage, type P2PTurnCredentials, type RemoteFileMetadata, type StatsStoredPayload, type UGCType, type UGCVisibility, type UpsertedLeaderboardEntry, type WavedashConfig, type WavedashEvent, WavedashEvents, type WavedashResponse, WavedashSDK, setupWavedashSDK };
|
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
|
+
{ isFullscreen: 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.4",
|
|
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
|
}
|