@indiegems/gem-web-sdk 20.0.0 → 20.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,20 @@ Its entries are grouped under up to five headings:
16
16
  - **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
17
17
  - **New capability**: something a game can now do. Nothing changes unless you use it.
18
18
 
19
+ ## 20.1.0
20
+
21
+ Upgrading from 20.0.0. Impact: required.
22
+
23
+ ### Rebuild
24
+
25
+ - The list of capability names a game can advertise gained one value, p2pRelayDrain. Recompile. If the game switches exhaustively over capability names, add a case for it. Otherwise nothing else changes.
26
+ - The list of messages the Arcade page sends to the game frame gained one entry, gem.relayDraining, and the parent message type gained the matching member. Recompile. If the game handles every parent message in an exhaustive switch, add a case for the new one. Otherwise nothing else changes.
27
+
28
+ ### New capability
29
+
30
+ - A game can now be told when a relay carrying its peer-to-peer match is draining and by when. Pass onRelayDraining in the frame options, or implement it on the frame callbacks, to receive the notice. The SDK already moves the connection off the draining relay by itself, so use this only to inform the player or log it. The notice is sent only by an Arcade page that supports it.
31
+ - Telemetry samples can now carry labels, so a game that reads or forwards telemetry samples can see per-sample label values such as outcome and reason.
32
+
19
33
  ## 20.0.0
20
34
 
21
35
  Upgrading from 19.0.2. Impact: required.
package/dist/game.d.ts CHANGED
@@ -2244,6 +2244,15 @@ interface GemSignalEvent {
2244
2244
  readonly matchId: string;
2245
2245
  readonly data: unknown;
2246
2246
  }
2247
+ interface GemRelayDraining {
2248
+ readonly gem: typeof PROTOCOL;
2249
+ readonly type: "gem.relayDraining";
2250
+ readonly matchId: string;
2251
+ readonly clusterTargetId: string;
2252
+ readonly relayIps: string[];
2253
+ readonly drainingSince: string;
2254
+ readonly deadline: string;
2255
+ }
2247
2256
  interface GemDedicatedEvent {
2248
2257
  readonly gem: typeof PROTOCOL;
2249
2258
  readonly type: "gem.dedicatedEvent";
@@ -2560,6 +2569,7 @@ interface TelemetrySample {
2560
2569
  readonly type: string;
2561
2570
  readonly value?: number;
2562
2571
  readonly transport?: TransportLabel;
2572
+ readonly labels?: Readonly<Record<string, string>>;
2563
2573
  }
2564
2574
  interface P2pApiOptions {
2565
2575
  readonly client: GemClient;
@@ -2676,9 +2686,12 @@ interface IceCandidateLike {
2676
2686
  readonly sdpMid?: string | null;
2677
2687
  readonly sdpMLineIndex?: number | null;
2678
2688
  }
2689
+ type EventListenerLike = {
2690
+ bivarianceHack(event: unknown): void;
2691
+ }["bivarianceHack"];
2679
2692
  interface EventTargetLike {
2680
- addEventListener(type: string, listener: (event: never) => void): void;
2681
- removeEventListener(type: string, listener: (event: never) => void): void;
2693
+ addEventListener(type: string, listener: EventListenerLike): void;
2694
+ removeEventListener(type: string, listener: EventListenerLike): void;
2682
2695
  }
2683
2696
  interface DataChannelLike extends EventTargetLike {
2684
2697
  readonly readyState: string;
@@ -2693,6 +2706,16 @@ interface DataChannelInitLike {
2693
2706
  readonly negotiated?: boolean;
2694
2707
  readonly id?: number;
2695
2708
  }
2709
+ interface IceConfigurationLike {
2710
+ iceServers?: {
2711
+ urls: string | string[];
2712
+ username?: string;
2713
+ credential?: string;
2714
+ }[];
2715
+ }
2716
+ interface StatsReportLike {
2717
+ forEach(callback: (value: unknown, key: string) => void): void;
2718
+ }
2696
2719
  interface PeerConnectionLike extends EventTargetLike {
2697
2720
  readonly localDescription: SessionDescriptionLike | null;
2698
2721
  readonly remoteDescription: SessionDescriptionLike | null;
@@ -2705,6 +2728,10 @@ interface PeerConnectionLike extends EventTargetLike {
2705
2728
  setRemoteDescription(description: SessionDescriptionLike): Promise<void>;
2706
2729
  addIceCandidate(candidate: IceCandidateLike): Promise<void>;
2707
2730
  close(): void;
2731
+ setConfiguration?(configuration: IceConfigurationLike): void;
2732
+ getConfiguration?(): IceConfigurationLike;
2733
+ restartIce?(): void;
2734
+ getStats?(): Promise<StatsReportLike>;
2708
2735
  }
2709
2736
  interface PeerConnectionConfig {
2710
2737
  readonly iceServers: readonly {
@@ -2739,6 +2766,7 @@ interface P2pSessionOptions {
2739
2766
  readonly baseUrl: string;
2740
2767
  readonly signaling: SignalingClient;
2741
2768
  readonly clusterTargetId?: ClusterTargetLike;
2769
+ readonly clusterTargetIds?: readonly string[];
2742
2770
  readonly peerConnectionFactory?: PeerConnectionFactory;
2743
2771
  readonly certificate?: unknown;
2744
2772
  readonly connectTimeoutMs?: number;
@@ -2755,7 +2783,11 @@ declare class P2pSession {
2755
2783
  private readonly keys;
2756
2784
  private localPlayerId;
2757
2785
  private migration;
2758
- private iceServers;
2786
+ private lease;
2787
+ private clusterTargetIds;
2788
+ private leaseRenewal;
2789
+ private leaseAskedAt;
2790
+ private leaseRenewalWarned;
2759
2791
  private credential;
2760
2792
  private nextPeerId;
2761
2793
  private ended;
@@ -2769,6 +2801,7 @@ declare class P2pSession {
2769
2801
  private readonly answeredSessions;
2770
2802
  private credentialGeneration;
2771
2803
  private credentialDeadline;
2804
+ private readonly drainTimers;
2772
2805
  constructor(options: P2pSessionOptions);
2773
2806
  get matchId(): string;
2774
2807
  get playerId(): string;
@@ -2794,6 +2827,7 @@ declare class P2pSession {
2794
2827
  private fetchCredential;
2795
2828
  private waitForCredential;
2796
2829
  private onOffer;
2830
+ private answer;
2797
2831
  private onAnswer;
2798
2832
  private hostVerified;
2799
2833
  private maybeAuthenticate;
@@ -2804,6 +2838,17 @@ declare class P2pSession {
2804
2838
  private watchMigration;
2805
2839
  private onMatchEnded;
2806
2840
  private onSignalingError;
2841
+ private onRelayDraining;
2842
+ private onDrainDeadline;
2843
+ private withoutDrainingRelays;
2844
+ private restartIceToHost;
2845
+ private offerIceRestart;
2846
+ private progressRestart;
2847
+ private reportRestart;
2848
+ private takeLease;
2849
+ private refreshLease;
2850
+ private reconfigureLinks;
2851
+ private iceServers;
2807
2852
  private openLink;
2808
2853
  private flushCandidates;
2809
2854
  private onReliableOpen;
@@ -2812,6 +2857,8 @@ declare class P2pSession {
2812
2857
  private refuseLink;
2813
2858
  private finishAdmission;
2814
2859
  private onIceState;
2860
+ private recoverHostLink;
2861
+ private voteHostUnreachable;
2815
2862
  private onConnectionState;
2816
2863
  private expose;
2817
2864
  private dropLink;
@@ -3070,6 +3117,7 @@ interface FrameCallbacks {
3070
3117
  onTicketEvent?(msg: GemTicketEvent): void;
3071
3118
  onSocketState?(msg: GemSocketState): void;
3072
3119
  onSignalEvent?(msg: GemSignalEvent): void;
3120
+ onRelayDraining?(msg: GemRelayDraining): void;
3073
3121
  onDedicatedEvent?(msg: GemDedicatedEvent): void;
3074
3122
  onClusterTarget?(msg: GemClusterTarget): void;
3075
3123
  onPartyResult?(msg: GemPartyResult): void;
@@ -1,2 +1,2 @@
1
- var GemWebSdkBridgeBundle=(function(){'use strict';var L="GemWebSdkHandshake";function oe(){return typeof document>"u"?void 0:document.readyState}var U=()=>Date.now();function H(n={}){let e=n.win??window,t=e,r=t[L];if(r!==void 0)return r;let s=n.readyState??oe,i=n.now??U,d=[],a=[],u=y=>{let c=y.data;!c||c.type!=="gem.handshake"||(d.length>=4&&(d.shift(),a.shift(),p.dropped+=1),d.push(y),a.push(i()));};e.addEventListener("message",u);let p={events:d,times:a,dropped:0,late:s()==="complete",uninstall(){e.removeEventListener("message",u);}};return t[L]=p,p}function B(n,e,t={}){let r=n[L];if(r===void 0)return null;let s=t.ttlMs??9500,i=t.now??U,d=r.events.splice(0),a=r.times?.splice(0),u=0,p=0,y=false,c=i()-s;for(let f=d.length-1;f>=0;f--){let S=a?.[f];if(S!==void 0&&S<c){p+=1;continue}if(u+=1,e(d[f])===true){y=true;break}}return r.uninstall(),{capture:r,delivered:u,stale:p,accepted:y}}var l="gem-web-bridge",R={min:1,max:1},ae=65536,N=8,G=5e3,W=1024;var b=Object.create(null),O="__malformed";var j=Object.create(null),se=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"]]);function x(n){let e=j[n];if(e!==void 0)return e;for(let[t,r]of se)if(n.startsWith(t))return r}function ie(n){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(n);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function le(n){let e;try{e=new URL(n);}catch{return false}return e.username!==""||e.password!==""?false:e.protocol==="https:"?true:e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1")}function de(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function me(n){return /^[a-z_]+$/.test(n)}function ue(n){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(n)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(n)&&!n.includes("--")}function _(n,e,t){if(e>N)return `nesting deeper than ${N}`;if(++t.nodes>G)return `more than ${G} values`;if(t.bytes>t.cap)return `over the ${t.cap} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let s=n;if(t.seen.has(s))return "a repeated or cyclic object reference";if(t.seen.add(s),Array.isArray(n)){if(n.length>W)return `an array of ${n.length} entries, over the ${W} limit`;t.bytes+=2;for(let a of n){let u=_(a,e+1,t);if(u)return u}return null}let i=Object.getPrototypeOf(n);if(i!==Object.prototype&&i!==null)return "a value that is not a plain object";t.bytes+=2;let d=n;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++t.nodes>G)return `more than ${G} values`;if(t.bytes+=a.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let u=_(d[a],e+1,t);if(u)return u}return null}function ce(n,e=ae){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=_(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var ge={origin:[ie,"not a well-formed origin"],url:[le,"not a safe absolute http(s) URL"],bcp47:[de,"not a well-formed BCP 47 tag"],code:[me,"not a code of lower-case letters and underscores"],gameRef:[ue,"not a game id or web address"]};function V(n,e,t){for(let r of e){let s=t===""?r[0]:`${t}.${r[0]}`,i=n[r[0]];if(i==null){if(r[1]<"a")continue;return `${s}: required`}let d=M(i,r,s);if(d)return d}return null}function M(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:M(n,typeof n=="string"?[e[0],"s",e[2]]:[e[0],"n",e[3],e[4]],t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e[2])return `${t}: exceeds ${e[2]} characters`;if(e[3]){let[s,i]=ge[e[3]];if(!s(n))return `${t}: ${i}`}return null}case "n":case "i":return typeof n!="number"||!(r=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${r=="i"?"integer":"number"}`:n<e[2]||n>e[3]?`${t}: out of range ${e[2]}..${e[3]}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let s=e[2];return s.includes(n)?null:`${t}: not one of ${s.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:V(n,e[2],t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e[2])return `${t}: exceeds ${e[2]} items`;let s=e[3],i=`${t}[]`;for(let d of n){if(d==null){if(s[1]<"a")continue;return `${i}: required`}let a=M(d,s,i);if(a)return a}return null}default:return `${t}: unknown kind ${String(r)}`}}var z=Object.create(null);function o(n,e,t,r,s){return b[n]=t,r&&(j[n]=r),z[n]=i=>{let d=ce(i,s);if(d)return d;if(typeof i!="object"||i===null||Array.isArray(i))return "message: expected object";let a=i;return a.gem!==l?"gem: wrong protocol marker":a.type!==n?`type: expected ${n}`:V(a,e,"")}}var m=["requestId","s",64],h=["outcome","e",["ok","declined","unavailable"]],v=["reason","S",64,"code"];o("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,["","s",64]],["embedder","s",2048,"origin"],["unbidden","B"]],30);o("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,["","s",64]]],5);o("gem.token",[["token","s",4096],["requestId","S",64],["generation","i",0,2147483647],["apiBaseUrl","S",512,"url"],["playerId","s",64],["gameId","s",64],["channel","e",["dev","test","release"]],["gameVersion","S",64],["expiresAt","i",0,4102444800],["chainEndsAt","i",0,4102444800],["allowDevTools","B"]],30);o("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);o("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);o("gem.pause",[["paused","b"]],120);o("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);o("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");o("gem.earnAllowanceChanged",[],30,"earnAllowance");o("gem.viewport",[["insets","o",[["top","i",0,4096],["right","i",0,4096],["bottom","i",0,4096],["left","i",0,4096]]],["reserved","A",4,["","o",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384],["control","O",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384]]]]]]],60,"viewport");o("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");o("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);o("gem.roomEvent",[["event","e",["updated","match_started","match_ready","match_failed","version_stale"]],["roomId","s",64],["matchId","S",64],["version","I",0,2147483647],["reason","S",64],["platform","E",["universal","mobile"]]],120,"rooms");o("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");o("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");o("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");o("gem.ticketEvent",[["event","e",["queued","matched","expired","cancelled","failed"]],["ticketId","s",64],["partySize","I",1,64],["queuedAt","I",0,4102444800],["expiresAt","I",0,4102444800],["reason","S",64],["roomId","S",64]],60,"ticketEvents");o("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],["matchId","s",64],["data","j"]],1800,"p2p");o("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],["matchId","s",64],["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");o("gem.partyResult",[m,["partyId","S",64]],30,"parties");o("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,["","s",64]]],12,"clusterTarget");o("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");o("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");o("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");o("gem.requestContext",[],12,"feedbackContext");o("gem.gameContext",[["context","j"]],12,"feedbackContext");o("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],["matchId","s",64],["data","j"]],1200,"p2p");o("gem.dedicatedOffer",[["matchId","s",64],["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");o("gem.partyRequest",[m],12,"parties");o("gem.ready",[],5);o("gem.engaged",[],5,"engagement");o("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);o("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);o("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");o("gem.localServer",[m,["serverId","S",128]],30,"localHost");o("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");o("gem.exit",[["reason","S",64]],5);o("gem.openExternal",[["url","s",2048,"url"]],6);o("gem.requestFullscreen",[["enabled","b"]],30);o("gem.audioState",[["muted","b"]],120,"audioState");o("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");o("gem.walletDisplay",[["visible","b"],["currencies","A",8,["","s",64]],["score","B"]],60,"walletDisplay");o("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");o("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");o("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");o("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");o("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");o("gem.settingsDeclare",[["settings","a",8,["","o",[["id","s",40],["label","s",40],["kind","e",["toggle","slider","choice"]],["value","u",40,-1e6,1e6],["quick","B"],["icon","S",16,"code"],["min","N",-1e6,1e6],["max","N",-1e6,1e6],["step","N",0,1e6],["options","A",4,["","o",[["id","s",40],["label","s",40]]]]]]]],120,"gameSettings");o("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");o("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");o("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");o("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");o("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");o("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");o("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);o("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");o("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");o("gem.purchaseResult",[m,h,v,["purchaseId","S",64]],30,"purchases");o("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");o("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");o("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");o("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");o("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");o("gem.momentResult",[m,["shown","b"]],12,"moments");o("gem.requestSignIn",[m],6,"signIn");o("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");o("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");o("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");o("gem.requestAppLinks",[m],12,"appLinks");o("gem.appLinksResult",[m,h,v,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");o("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");o("gem.openAppResult",[m,h,v],30,"appLinks");o("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");o("gem.openGameResult",[m,h,v],30,"openGame");o("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");o("gem.similarGamesResult",[m,h,v,["games","a",24,["","o",[["gameId","s",36,"gameRef"],["slug","S",40,"gameRef"],["iconUrl","s",1024,"url"],["capsuleLandscapeUrl","s",1024,"url"],["capsulePortraitUrl","s",1024,"url"],["previewVideoUrl","S",1024,"url"],["previewPosterUrl","S",1024,"url"],["untrusted","o",[["name","s",64],["tagline","s",64],["shortDescription","s",320],["tags","a",10,["","s",32]]]]]]]],30,"openGame",524288);o("gem.requestStoreOffers",[m],12,"store");o("gem.storeOffersResult",[m,h,v,["offers","a",32,["","o",[["skuId","s",64],["kind","e",["gem_pack","boost","pass","bundle"]],["name","s",80],["description","s",400],["imageUrl","S",1024,"url"],["priceDisplay","s",32],["grant","O",[["currency","s",32],["amount","i",1,1e9]]]]]]],30,"store",262144);o("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");o("gem.storePurchaseResult",[m,h,v],30,"store");o("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");o("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");o("gem.storeOfferSheetResult",[m,h,v],30,"storeOfferSheet");var P=Object.freeze(z);b.__malformed=60;Object.freeze(b);Object.freeze(j);var pe=/^[a-z0-9][a-z0-9_-]{0,39}$/;function ye(n){return typeof n=="string"&&pe.test(n)}function K(n){if(n===null||ye(n))return n;let e=typeof n=="string"?JSON.stringify(n.length>48?`${n.slice(0,48)}\u2026`:n):`a value of type ${typeof n}`;console.warn(`[gem] showPortraitBorders: ${e} is not a border name, so nothing was sent. A name is 1 to 40 of a-z, 0-9, _ and -, starting with a letter or digit, exactly as your store page declares it under media.portrait_borders. Pass null for no art.`);}var D={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)},w=class{constructor(e=D){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let t=b[e];if(t===void 0)return false;let r=this.clock.now(),s=this.lastRefill.get(e)??r,i=this.tokens.get(e)??t,d=Math.max(0,r-s),a=Math.min(t,i+d*t/6e4);return a<1?(this.tokens.set(e,a),this.lastRefill.set(e,r),false):(this.tokens.set(e,a-1),this.lastRefill.set(e,r),true)}};function T(n,e){return n.toLowerCase()===e.toLowerCase()}function C(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function Y(n,e){if(typeof e!="string"||e==="")return false;let t,r;try{t=new URL(n),r=new URL(e);}catch{return false}return t.protocol!=="http:"&&t.protocol!=="https:"||r.protocol!=="http:"&&r.protocol!=="https:"||t.username!==""||t.password!==""||t.pathname!=="/"||t.search!==""||t.hash!==""||t.port===""?false:C(t.hostname)&&C(r.hostname)}function J(n,e){if(n.min>n.max||e.min>e.max)return null;let t=Math.min(n.max,e.max),r=Math.max(n.min,e.min);return t>=r?t:null}var Z="[gem: use gem.text.toPlain()]",Q=new Set,X;function fe(n){try{if(Q.has(n))return;let e=globalThis.location;if(X??=!!e?.port&&C(e.hostname??""),!X)return;Q.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Z}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
2
- ${new Error().stack}`);}catch{}}var F=class{#e;#t;constructor(e,t="player text"){this.#e=e,this.#t=t,Object.freeze(this);}toString(){return fe(this.#t),Z}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function $(n,e){return new F(n,e)}function ee(n){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function he(n,e){if(n!==true)return {visible:false};let t=[],r=false;for(let s of Array.isArray(e)?e:[])s==="score"?r=true:typeof s=="string"&&s.length>0&&s.length<=64&&!t.includes(s)&&t.push(s);return !r&&t.length===0?{visible:true}:{visible:true,currencies:t.slice(0,8),score:r}}function ve(n){let e=[];for(let t of Array.isArray(n)?n:[]){if(e.length===4)break;if(typeof t!="object"||t===null)continue;let{id:r,label:s}=t;typeof r!="string"||r.length===0||r.length>32||typeof s!="string"||s.trim().length===0||e.some(i=>i.id===r)||e.push({id:r,label:s.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function te(n,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function Se(n){let e=n.untrusted,t=$(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,s=r?$(r,"gem.player.displayTag"):void 0,i=n.discriminator||n.displayTag,d=i&&/^[0-9]{1,8}$/.test(i)?i:void 0,a=n.avatarIconUrl||n.avatarUrl,u=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,p=n.avatarIconId,y=n.avatar3dId,c={displayName:t,displayHandle:s??t,badges:(n.badges??[]).map(g=>({itemId:g.itemId,iconUrl:g.iconUrl}))};n.playerId&&(c.playerId=n.playerId),d&&(c.discriminator=d),s&&(c.displayTag=s),a&&(c.avatarIconUrl=c.avatarUrl=a),p&&u.test(p)&&(c.avatarIconId=p),y&&u.test(y)&&(c.avatar3dId=y),n.avatar3dModelUrl&&(c.avatar3dModelUrl=n.avatar3dModelUrl),n.avatar3dIconUrl&&(c.avatar3dIconUrl=n.avatar3dIconUrl),n.avatars3d&&(c.avatars3d=n.avatars3d.filter(g=>u.test(g.id)&&/^[a-z][a-z0-9_]*$/.test(g.family)).filter((g,q,I)=>I.findIndex(A=>A.family==g.family)==q).map(({family:g,id:q,modelUrl:I,iconUrl:A})=>({family:g,id:q,modelUrl:I,iconUrl:A})));let f=n.boost,S=(f?.multipliers??[]).map(g=>({currencyId:g.currencyId,currencySlug:g.currencySlug,multiplier:g.multiplierBp/1e4})),E=S.some(g=>g.multiplier>1);return c.boost=E&&f?.expiresInDays?{active:E,multipliers:S,expiresInDays:f.expiresInDays}:{active:E,multipliers:S},c}var Re=1e4,be=["appLinks","openGame","store","storeOfferSheet"],we=new WeakMap;var k=class{constructor(e){this.options=e;if(e.expectedSource===void 0||e.expectedSource===null)throw new Error("GemFrame: expectedSource is required and must not be null \u2014 pass window.parent. The handshake transfers the MessagePort, and origin alone does not say WHICH document on that origin sent it.");this.expectedSource=e.expectedSource,this.clock=e.clock??D,this.inbound=new w(this.clock),this.outbound=new w(this.clock);}options;state="waiting";port=null;version=null;peerCapabilities=[];unbidden=false;engagedSent=false;readyPending=false;readySent=false;settleWaiters=[];clock;inbound;outbound;expectedSource;get currentState(){return this.state}get parentCapabilities(){return this.peerCapabilities}parentSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(e){if(e.source!==this.expectedSource)return this.refuse(e,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>T(a,e.origin))||Y(e.origin,this.options.selfOrigin)||(we.get(this)??[]).some(a=>T(a,e.origin))))return this.refuse(e,`embedder '${e.origin}' is not in allowedEmbedders`),this.state==="waiting"&&this.transition("refused"),false;if(this.state==="connected")return this.fail("handshake_duplicate","this document already has a port"),false;if(this.state!=="waiting")return false;let r=P["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let s=e.data;if(!T(s.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${s.embedder}' but arrived from '${e.origin}'`),false;let i=e.ports?.[0];if(!i)return this.refuse(e,"handshake transferred no MessagePort"),false;let d=J(R,s.versions);return d===null?(i.onmessage=null,i.postMessage({gem:l,type:"gem.error",code:"version_mismatch",message:`frame supports v${R.min}..v${R.max}`}),i.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=i,this.version=d,i.onmessage=a=>this.receive(a.data,a.ports??[]),i.start?.(),this.peerCapabilities=s.capabilities??[],this.unbidden=s.unbidden===true,i.postMessage({gem:l,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:l,type:"gem.ready"});}settled(e=Re){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(t=>{let r=i=>{this.clock.clearTimeout(s),t(i);},s=this.clock.setTimeout(()=>{let i=this.settleWaiters.indexOf(r);i>=0&&this.settleWaiters.splice(i,1),t(this.state);},e);this.settleWaiters.push(r);})}progress(e,t){this.send({gem:l,type:"gem.progress",loaded:e,total:t});}requestToken(e,t){this.send({gem:l,type:"gem.requestToken",requestId:e,reason:t});}requestLocalServer(e,t){return this.parentSupports("localHost")?(this.send({gem:l,type:"gem.requestLocalServer",requestId:e,gameRoomId:t}),true):false}exit(e){this.send(e===void 0?{gem:l,type:"gem.exit"}:{gem:l,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:l,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:l,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:l,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:l,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let t={gem:l,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return e.expected!==void 0&&Number.isFinite(e.expected)&&e.expected>=1&&(t.expected=Math.min(1e9,Math.round(e.expected))),e.origin&&(t.origin={x:ee(e.origin.x),y:ee(e.origin.y)}),this.send(t),true}showPortraitBorders(e){let t=K(e);return t===void 0||!this.gated("portraitBorders")?false:this.send(t===null?{gem:l,type:"gem.portraitBorders"}:{gem:l,type:"gem.portraitBorders",name:t})}setWalletVisible(e,t){return this.gated("walletDisplay")?this.send({gem:l,type:"gem.walletDisplay",...he(e,t?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:l,type:"gem.menu",...ve(e)}):false}declareSettings(e){return this.gated("gameSettings")?this.send({gem:l,type:"gem.settingsDeclare",settings:e}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:l,type:"gem.gemCorner",corner:e}):false}setMenuStyle(e){return this.gated("menuStyle")?this.send(e===null?{gem:l,type:"gem.menuStyle"}:{gem:l,type:"gem.menuStyle",ground:e.ground,accent:e.accent}):false}requestShare(e){return this.gated("share")?this.send({gem:l,type:"gem.requestShare",params:e.map(({key:t,value:r})=>({key:t,value:r}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:l,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:l,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:l,type:"gem.frameStats",fps:te(e.fps,1e3),frameTimeP95Ms:te(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:l,type:"gem.gameContext",context:e}),true)}signal(e,t,r){this.send({gem:l,type:"gem.signal",signal:e,matchId:t,data:r});}dedicatedOffer(e,t,r){this.send({gem:l,type:"gem.dedicatedOffer",matchId:e,sdp:t,intent:r});}requestParty(e){return this.send({gem:l,type:"gem.partyRequest",requestId:e})}requestPurchase(e,t,r,s,i,d){return this.send({gem:l,type:"gem.requestPurchase",requestId:e,purchaseKind:t,targetId:r,currencySlug:s,...i===void 0?{}:{quantity:i},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(e,t,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:l,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:t,currencySlug:r}):false}putDeviceSettings(e,t){return this.send({gem:l,type:"gem.deviceSettings.put",requestId:e,settings:t})}requestAd(e,t,r){return this.send({gem:l,type:"gem.requestAd",requestId:e,placement:t,format:r})}signalMoment(e,t,r,s){return this.send({gem:l,type:"gem.signalMoment",requestId:e,kind:t,...r===void 0?{}:{placement:r},...s===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:l,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,t,r,s){return this.send({gem:l,type:"gem.requestFriendChallenge",requestId:e,playerId:t,template:r,...s===void 0?{}:{params:s}})}requestArcade(e){return this.gated(x(e.type))&&this.send({gem:l,...e})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(e){if(this.state!==e){this.state=e;for(let t of this.settleWaiters.splice(0))t(e);this.options.onStateChange?.(e);}}refuseMalformed(e,t){if(!this.inbound.allow(O)){this.options.onDropped?.(e,t);return}this.fail(e,t);}dropUnknownType(e){this.inbound.allow(O)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,t){e.ports?.[0]?.close?.(),this.inbound.allow(O)&&this.options.onRefused?.(t);}fail(e,t){this.options.onError?.(e,t),this.state==="connected"&&this.port&&this.send({gem:l,type:"gem.error",code:e,...t===void 0?{}:{message:t}});}gated(e){return this.state==="connected"&&this.parentSupports(e)}send(e){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let t=e.type;if(!this.outbound.allow(t))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${t}`),false;let r=P[t]?.(e);return r?(this.options.onError?.("internal",`refusing to send malformed ${t}: ${r}`),false):(this.port.postMessage(this.version===null?e:{...e,v:this.version}),true)}receive(e,t=[]){let r=false;try{r=this.dispatchInbound(e,t);}finally{r||ne(t);}}dispatchInbound(e,t){if(this.state!=="connected")return false;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let s=P[r];if(!s)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let i=x(r);if(i!==void 0&&!this.options.capabilities?.includes(i))return this.fail("unsupported_capability",`${r} requires the '${i}' capability, which this frame did not advertise`),false;let d=s(e);if(d)return this.fail("invalid_message",`${r}: ${d}`),false;let a=e;if(a.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.settingChange":this.options.onSettingChange?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let u=t[0];if(!u){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let p={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){u.close(),this.screenshotDenied("not_wired",false);break}return ne(t.slice(1)),this.options.onScreenshot(p,u,(y,c)=>this.screenshotDenied(y,c)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(Se(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:if(be.includes(x(r))){this.options.onArcadeAnswer?.(a);break}this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,t){this.send({gem:l,type:"gem.screenshot.denied",reason:e,retryable:t});}};function ne(n){for(let e of n)try{e?.close?.();}catch{}}H();function ke(n){if((n.expectedSource??window.parent)===window)throw new Error("GemWebSdkBridge.connect: this document is not framed (window.parent === window), so there is no embedder to hand it a port. A game runs inside the Gem Arcade iframe; opening it directly will never complete a handshake.");let e=new k({...n,expectedSource:n.expectedSource??window.parent}),t=r=>{let s=e.handleWindowMessage({data:r.data,origin:r.origin,source:r.source,ports:r.ports});return s&&window.removeEventListener("message",t),s};return window.addEventListener("message",t),B(window,t),e}var re={connect:ke,GemFrame:k,PROTOCOL:l,SUPPORTED_VERSIONS:R};window.GemWebSdkBridge=re;var yn=re;return yn;})();
1
+ var GemWebSdkBridgeBundle=(function(){'use strict';var D="GemWebSdkHandshake";function se(){return typeof document>"u"?void 0:document.readyState}var B=()=>Date.now();function N(n={}){let e=n.win??window,t=e,r=t[D];if(r!==void 0)return r;let s=n.readyState??se,i=n.now??B,d=[],a=[],u=y=>{let c=y.data;!c||c.type!=="gem.handshake"||(d.length>=4&&(d.shift(),a.shift(),p.dropped+=1),d.push(y),a.push(i()));};e.addEventListener("message",u);let p={events:d,times:a,dropped:0,late:s()==="complete",uninstall(){e.removeEventListener("message",u);}};return t[D]=p,p}function W(n,e,t={}){let r=n[D];if(r===void 0)return null;let s=t.ttlMs??9500,i=t.now??B,d=r.events.splice(0),a=r.times?.splice(0),u=0,p=0,y=false,c=i()-s;for(let f=d.length-1;f>=0;f--){let R=a?.[f];if(R!==void 0&&R<c){p+=1;continue}if(u+=1,e(d[f])===true){y=true;break}}return r.uninstall(),{capture:r,delivered:u,stale:p,accepted:y}}var l="gem-web-bridge",S={min:1,max:1},ie=65536,V=8,x=5e3,z=1024;var b=Object.create(null),P="__malformed";var F=Object.create(null),le=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"]]);function C(n){let e=F[n];if(e!==void 0)return e;for(let[t,r]of le)if(n.startsWith(t))return r}function de(n){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(n);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function me(n){let e;try{e=new URL(n);}catch{return false}return e.username!==""||e.password!==""?false:e.protocol==="https:"?true:e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1")}function ue(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function ce(n){return /^[a-z_]+$/.test(n)}function ge(n){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(n)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(n)&&!n.includes("--")}function M(n,e,t){if(e>V)return `nesting deeper than ${V}`;if(++t.nodes>x)return `more than ${x} values`;if(t.bytes>t.cap)return `over the ${t.cap} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let s=n;if(t.seen.has(s))return "a repeated or cyclic object reference";if(t.seen.add(s),Array.isArray(n)){if(n.length>z)return `an array of ${n.length} entries, over the ${z} limit`;t.bytes+=2;for(let a of n){let u=M(a,e+1,t);if(u)return u}return null}let i=Object.getPrototypeOf(n);if(i!==Object.prototype&&i!==null)return "a value that is not a plain object";t.bytes+=2;let d=n;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++t.nodes>x)return `more than ${x} values`;if(t.bytes+=a.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let u=M(d[a],e+1,t);if(u)return u}return null}function pe(n,e=ie){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=M(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var ye={origin:[de,"not a well-formed origin"],url:[me,"not a safe absolute http(s) URL"],bcp47:[ue,"not a well-formed BCP 47 tag"],code:[ce,"not a code of lower-case letters and underscores"],gameRef:[ge,"not a game id or web address"]};function K(n,e,t){for(let r of e){let s=t===""?r[0]:`${t}.${r[0]}`,i=n[r[0]];if(i==null){if(r[1]<"a")continue;return `${s}: required`}let d=j(i,r,s);if(d)return d}return null}function j(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:j(n,typeof n=="string"?[e[0],"s",e[2]]:[e[0],"n",e[3],e[4]],t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e[2])return `${t}: exceeds ${e[2]} characters`;if(e[3]){let[s,i]=ye[e[3]];if(!s(n))return `${t}: ${i}`}return null}case "n":case "i":return typeof n!="number"||!(r=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${r=="i"?"integer":"number"}`:n<e[2]||n>e[3]?`${t}: out of range ${e[2]}..${e[3]}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let s=e[2];return s.includes(n)?null:`${t}: not one of ${s.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:K(n,e[2],t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e[2])return `${t}: exceeds ${e[2]} items`;let s=e[3],i=`${t}[]`;for(let d of n){if(d==null){if(s[1]<"a")continue;return `${i}: required`}let a=j(d,s,i);if(a)return a}return null}default:return `${t}: unknown kind ${String(r)}`}}var Y=Object.create(null);function o(n,e,t,r,s){return b[n]=t,r&&(F[n]=r),Y[n]=i=>{let d=pe(i,s);if(d)return d;if(typeof i!="object"||i===null||Array.isArray(i))return "message: expected object";let a=i;return a.gem!==l?"gem: wrong protocol marker":a.type!==n?`type: expected ${n}`:K(a,e,"")}}var w=["","s",64],m=["requestId","s",64],k=["matchId","s",64],h=["outcome","e",["ok","declined","unavailable"]],v=["reason","S",64,"code"];o("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,w],["embedder","s",2048,"origin"],["unbidden","B"]],30);o("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,w]],5);o("gem.token",[["token","s",4096],["requestId","S",64],["generation","i",0,2147483647],["apiBaseUrl","S",512,"url"],["playerId","s",64],["gameId","s",64],["channel","e",["dev","test","release"]],["gameVersion","S",64],["expiresAt","i",0,4102444800],["chainEndsAt","i",0,4102444800],["allowDevTools","B"]],30);o("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);o("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);o("gem.pause",[["paused","b"]],120);o("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);o("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");o("gem.earnAllowanceChanged",[],30,"earnAllowance");o("gem.viewport",[["insets","o",[["top","i",0,4096],["right","i",0,4096],["bottom","i",0,4096],["left","i",0,4096]]],["reserved","A",4,["","o",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384],["control","O",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384]]]]]]],60,"viewport");o("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");o("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);o("gem.roomEvent",[["event","e",["updated","match_started","match_ready","match_failed","version_stale"]],["roomId","s",64],["matchId","S",64],["version","I",0,2147483647],["reason","S",64],["platform","E",["universal","mobile"]]],120,"rooms");o("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");o("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");o("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");o("gem.ticketEvent",[["event","e",["queued","matched","expired","cancelled","failed"]],["ticketId","s",64],["partySize","I",1,64],["queuedAt","I",0,4102444800],["expiresAt","I",0,4102444800],["reason","S",64],["roomId","S",64]],60,"ticketEvents");o("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],k,["data","j"]],1800,"p2p");o("gem.relayDraining",[k,["clusterTargetId","s",64],["relayIps","a",16,w],["drainingSince","s",40],["deadline","s",40]],60,"p2pRelayDrain");o("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],k,["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");o("gem.partyResult",[m,["partyId","S",64]],30,"parties");o("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,w]],12,"clusterTarget");o("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");o("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");o("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");o("gem.requestContext",[],12,"feedbackContext");o("gem.gameContext",[["context","j"]],12,"feedbackContext");o("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],k,["data","j"]],1200,"p2p");o("gem.dedicatedOffer",[k,["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");o("gem.partyRequest",[m],12,"parties");o("gem.ready",[],5);o("gem.engaged",[],5,"engagement");o("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);o("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);o("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");o("gem.localServer",[m,["serverId","S",128]],30,"localHost");o("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");o("gem.exit",[["reason","S",64]],5);o("gem.openExternal",[["url","s",2048,"url"]],6);o("gem.requestFullscreen",[["enabled","b"]],30);o("gem.audioState",[["muted","b"]],120,"audioState");o("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");o("gem.walletDisplay",[["visible","b"],["currencies","A",8,w],["score","B"]],60,"walletDisplay");o("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");o("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");o("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");o("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");o("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");o("gem.settingsDeclare",[["settings","a",8,["","o",[["id","s",40],["label","s",40],["kind","e",["toggle","slider","choice"]],["value","u",40,-1e6,1e6],["quick","B"],["icon","S",16,"code"],["min","N",-1e6,1e6],["max","N",-1e6,1e6],["step","N",0,1e6],["options","A",4,["","o",[["id","s",40],["label","s",40]]]]]]]],120,"gameSettings");o("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");o("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");o("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");o("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");o("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");o("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");o("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);o("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");o("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");o("gem.purchaseResult",[m,h,v,["purchaseId","S",64]],30,"purchases");o("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");o("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");o("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");o("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");o("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");o("gem.momentResult",[m,["shown","b"]],12,"moments");o("gem.requestSignIn",[m],6,"signIn");o("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");o("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");o("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");o("gem.requestAppLinks",[m],12,"appLinks");o("gem.appLinksResult",[m,h,v,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");o("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");o("gem.openAppResult",[m,h,v],30,"appLinks");o("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");o("gem.openGameResult",[m,h,v],30,"openGame");o("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");o("gem.similarGamesResult",[m,h,v,["games","a",24,["","o",[["gameId","s",36,"gameRef"],["slug","S",40,"gameRef"],["iconUrl","s",1024,"url"],["capsuleLandscapeUrl","s",1024,"url"],["capsulePortraitUrl","s",1024,"url"],["previewVideoUrl","S",1024,"url"],["previewPosterUrl","S",1024,"url"],["untrusted","o",[["name","s",64],["tagline","s",64],["shortDescription","s",320],["tags","a",10,["","s",32]]]]]]]],30,"openGame",524288);o("gem.requestStoreOffers",[m],12,"store");o("gem.storeOffersResult",[m,h,v,["offers","a",32,["","o",[["skuId","s",64],["kind","e",["gem_pack","boost","pass","bundle"]],["name","s",80],["description","s",400],["imageUrl","S",1024,"url"],["priceDisplay","s",32],["grant","O",[["currency","s",32],["amount","i",1,1e9]]]]]]],30,"store",262144);o("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");o("gem.storePurchaseResult",[m,h,v],30,"store");o("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");o("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");o("gem.storeOfferSheetResult",[m,h,v],30,"storeOfferSheet");var T=Object.freeze(Y);b.__malformed=60;Object.freeze(b);Object.freeze(F);var fe=/^[a-z0-9][a-z0-9_-]{0,39}$/;function he(n){return typeof n=="string"&&fe.test(n)}function J(n){if(n===null||he(n))return n;let e=typeof n=="string"?JSON.stringify(n.length>48?`${n.slice(0,48)}\u2026`:n):`a value of type ${typeof n}`;console.warn(`[gem] showPortraitBorders: ${e} is not a border name, so nothing was sent. A name is 1 to 40 of a-z, 0-9, _ and -, starting with a letter or digit, exactly as your store page declares it under media.portrait_borders. Pass null for no art.`);}var $={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)},G=class{constructor(e=$){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let t=b[e];if(t===void 0)return false;let r=this.clock.now(),s=this.lastRefill.get(e)??r,i=this.tokens.get(e)??t,d=Math.max(0,r-s),a=Math.min(t,i+d*t/6e4);return a<1?(this.tokens.set(e,a),this.lastRefill.set(e,r),false):(this.tokens.set(e,a-1),this.lastRefill.set(e,r),true)}};function q(n,e){return n.toLowerCase()===e.toLowerCase()}function E(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function Q(n,e){if(typeof e!="string"||e==="")return false;let t,r;try{t=new URL(n),r=new URL(e);}catch{return false}return t.protocol!=="http:"&&t.protocol!=="https:"||r.protocol!=="http:"&&r.protocol!=="https:"||t.username!==""||t.password!==""||t.pathname!=="/"||t.search!==""||t.hash!==""||t.port===""?false:E(t.hostname)&&E(r.hostname)}function X(n,e){if(n.min>n.max||e.min>e.max)return null;let t=Math.min(n.max,e.max),r=Math.max(n.min,e.min);return t>=r?t:null}var te="[gem: use gem.text.toPlain()]",Z=new Set,ee;function ve(n){try{if(Z.has(n))return;let e=globalThis.location;if(ee??=!!e?.port&&E(e.hostname??""),!ee)return;Z.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${te}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
2
+ ${new Error().stack}`);}catch{}}var U=class{#e;#t;constructor(e,t="player text"){this.#e=e,this.#t=t,Object.freeze(this);}toString(){return ve(this.#t),te}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function H(n,e){return new U(n,e)}function ne(n){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function Re(n,e){if(n!==true)return {visible:false};let t=[],r=false;for(let s of Array.isArray(e)?e:[])s==="score"?r=true:typeof s=="string"&&s.length>0&&s.length<=64&&!t.includes(s)&&t.push(s);return !r&&t.length===0?{visible:true}:{visible:true,currencies:t.slice(0,8),score:r}}function Se(n){let e=[];for(let t of Array.isArray(n)?n:[]){if(e.length===4)break;if(typeof t!="object"||t===null)continue;let{id:r,label:s}=t;typeof r!="string"||r.length===0||r.length>32||typeof s!="string"||s.trim().length===0||e.some(i=>i.id===r)||e.push({id:r,label:s.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function re(n,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function be(n){let e=n.untrusted,t=H(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,s=r?H(r,"gem.player.displayTag"):void 0,i=n.discriminator||n.displayTag,d=i&&/^[0-9]{1,8}$/.test(i)?i:void 0,a=n.avatarIconUrl||n.avatarUrl,u=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,p=n.avatarIconId,y=n.avatar3dId,c={displayName:t,displayHandle:s??t,badges:(n.badges??[]).map(g=>({itemId:g.itemId,iconUrl:g.iconUrl}))};n.playerId&&(c.playerId=n.playerId),d&&(c.discriminator=d),s&&(c.displayTag=s),a&&(c.avatarIconUrl=c.avatarUrl=a),p&&u.test(p)&&(c.avatarIconId=p),y&&u.test(y)&&(c.avatar3dId=y),n.avatar3dModelUrl&&(c.avatar3dModelUrl=n.avatar3dModelUrl),n.avatar3dIconUrl&&(c.avatar3dIconUrl=n.avatar3dIconUrl),n.avatars3d&&(c.avatars3d=n.avatars3d.filter(g=>u.test(g.id)&&/^[a-z][a-z0-9_]*$/.test(g.family)).filter((g,A,L)=>L.findIndex(_=>_.family==g.family)==A).map(({family:g,id:A,modelUrl:L,iconUrl:_})=>({family:g,id:A,modelUrl:L,iconUrl:_})));let f=n.boost,R=(f?.multipliers??[]).map(g=>({currencyId:g.currencyId,currencySlug:g.currencySlug,multiplier:g.multiplierBp/1e4})),I=R.some(g=>g.multiplier>1);return c.boost=I&&f?.expiresInDays?{active:I,multipliers:R,expiresInDays:f.expiresInDays}:{active:I,multipliers:R},c}var we=1e4,ke=["appLinks","openGame","store","storeOfferSheet"],Ge=new WeakMap;var O=class{constructor(e){this.options=e;if(e.expectedSource===void 0||e.expectedSource===null)throw new Error("GemFrame: expectedSource is required and must not be null \u2014 pass window.parent. The handshake transfers the MessagePort, and origin alone does not say WHICH document on that origin sent it.");this.expectedSource=e.expectedSource,this.clock=e.clock??$,this.inbound=new G(this.clock),this.outbound=new G(this.clock);}options;state="waiting";port=null;version=null;peerCapabilities=[];unbidden=false;engagedSent=false;readyPending=false;readySent=false;settleWaiters=[];clock;inbound;outbound;expectedSource;get currentState(){return this.state}get parentCapabilities(){return this.peerCapabilities}parentSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(e){if(e.source!==this.expectedSource)return this.refuse(e,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>q(a,e.origin))||Q(e.origin,this.options.selfOrigin)||(Ge.get(this)??[]).some(a=>q(a,e.origin))))return this.refuse(e,`embedder '${e.origin}' is not in allowedEmbedders`),this.state==="waiting"&&this.transition("refused"),false;if(this.state==="connected")return this.fail("handshake_duplicate","this document already has a port"),false;if(this.state!=="waiting")return false;let r=T["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let s=e.data;if(!q(s.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${s.embedder}' but arrived from '${e.origin}'`),false;let i=e.ports?.[0];if(!i)return this.refuse(e,"handshake transferred no MessagePort"),false;let d=X(S,s.versions);return d===null?(i.onmessage=null,i.postMessage({gem:l,type:"gem.error",code:"version_mismatch",message:`frame supports v${S.min}..v${S.max}`}),i.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=i,this.version=d,i.onmessage=a=>this.receive(a.data,a.ports??[]),i.start?.(),this.peerCapabilities=s.capabilities??[],this.unbidden=s.unbidden===true,i.postMessage({gem:l,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:l,type:"gem.ready"});}settled(e=we){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(t=>{let r=i=>{this.clock.clearTimeout(s),t(i);},s=this.clock.setTimeout(()=>{let i=this.settleWaiters.indexOf(r);i>=0&&this.settleWaiters.splice(i,1),t(this.state);},e);this.settleWaiters.push(r);})}progress(e,t){this.send({gem:l,type:"gem.progress",loaded:e,total:t});}requestToken(e,t){this.send({gem:l,type:"gem.requestToken",requestId:e,reason:t});}requestLocalServer(e,t){return this.parentSupports("localHost")?(this.send({gem:l,type:"gem.requestLocalServer",requestId:e,gameRoomId:t}),true):false}exit(e){this.send(e===void 0?{gem:l,type:"gem.exit"}:{gem:l,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:l,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:l,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:l,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:l,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let t={gem:l,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return e.expected!==void 0&&Number.isFinite(e.expected)&&e.expected>=1&&(t.expected=Math.min(1e9,Math.round(e.expected))),e.origin&&(t.origin={x:ne(e.origin.x),y:ne(e.origin.y)}),this.send(t),true}showPortraitBorders(e){let t=J(e);return t===void 0||!this.gated("portraitBorders")?false:this.send(t===null?{gem:l,type:"gem.portraitBorders"}:{gem:l,type:"gem.portraitBorders",name:t})}setWalletVisible(e,t){return this.gated("walletDisplay")?this.send({gem:l,type:"gem.walletDisplay",...Re(e,t?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:l,type:"gem.menu",...Se(e)}):false}declareSettings(e){return this.gated("gameSettings")?this.send({gem:l,type:"gem.settingsDeclare",settings:e}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:l,type:"gem.gemCorner",corner:e}):false}setMenuStyle(e){return this.gated("menuStyle")?this.send(e===null?{gem:l,type:"gem.menuStyle"}:{gem:l,type:"gem.menuStyle",ground:e.ground,accent:e.accent}):false}requestShare(e){return this.gated("share")?this.send({gem:l,type:"gem.requestShare",params:e.map(({key:t,value:r})=>({key:t,value:r}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:l,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:l,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:l,type:"gem.frameStats",fps:re(e.fps,1e3),frameTimeP95Ms:re(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:l,type:"gem.gameContext",context:e}),true)}signal(e,t,r){this.send({gem:l,type:"gem.signal",signal:e,matchId:t,data:r});}dedicatedOffer(e,t,r){this.send({gem:l,type:"gem.dedicatedOffer",matchId:e,sdp:t,intent:r});}requestParty(e){return this.send({gem:l,type:"gem.partyRequest",requestId:e})}requestPurchase(e,t,r,s,i,d){return this.send({gem:l,type:"gem.requestPurchase",requestId:e,purchaseKind:t,targetId:r,currencySlug:s,...i===void 0?{}:{quantity:i},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(e,t,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:l,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:t,currencySlug:r}):false}putDeviceSettings(e,t){return this.send({gem:l,type:"gem.deviceSettings.put",requestId:e,settings:t})}requestAd(e,t,r){return this.send({gem:l,type:"gem.requestAd",requestId:e,placement:t,format:r})}signalMoment(e,t,r,s){return this.send({gem:l,type:"gem.signalMoment",requestId:e,kind:t,...r===void 0?{}:{placement:r},...s===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:l,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,t,r,s){return this.send({gem:l,type:"gem.requestFriendChallenge",requestId:e,playerId:t,template:r,...s===void 0?{}:{params:s}})}requestArcade(e){return this.gated(C(e.type))&&this.send({gem:l,...e})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(e){if(this.state!==e){this.state=e;for(let t of this.settleWaiters.splice(0))t(e);this.options.onStateChange?.(e);}}refuseMalformed(e,t){if(!this.inbound.allow(P)){this.options.onDropped?.(e,t);return}this.fail(e,t);}dropUnknownType(e){this.inbound.allow(P)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,t){e.ports?.[0]?.close?.(),this.inbound.allow(P)&&this.options.onRefused?.(t);}fail(e,t){this.options.onError?.(e,t),this.state==="connected"&&this.port&&this.send({gem:l,type:"gem.error",code:e,...t===void 0?{}:{message:t}});}gated(e){return this.state==="connected"&&this.parentSupports(e)}send(e){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let t=e.type;if(!this.outbound.allow(t))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${t}`),false;let r=T[t]?.(e);return r?(this.options.onError?.("internal",`refusing to send malformed ${t}: ${r}`),false):(this.port.postMessage(this.version===null?e:{...e,v:this.version}),true)}receive(e,t=[]){let r=false;try{r=this.dispatchInbound(e,t);}finally{r||oe(t);}}dispatchInbound(e,t){if(this.state!=="connected")return false;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let s=T[r];if(!s)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let i=C(r);if(i!==void 0&&!this.options.capabilities?.includes(i))return this.fail("unsupported_capability",`${r} requires the '${i}' capability, which this frame did not advertise`),false;let d=s(e);if(d)return this.fail("invalid_message",`${r}: ${d}`),false;let a=e;if(a.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.settingChange":this.options.onSettingChange?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let u=t[0];if(!u){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let p={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){u.close(),this.screenshotDenied("not_wired",false);break}return oe(t.slice(1)),this.options.onScreenshot(p,u,(y,c)=>this.screenshotDenied(y,c)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(be(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.relayDraining":this.options.onRelayDraining?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:if(ke.includes(C(r))){this.options.onArcadeAnswer?.(a);break}this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,t){this.send({gem:l,type:"gem.screenshot.denied",reason:e,retryable:t});}};function oe(n){for(let e of n)try{e?.close?.();}catch{}}N();function Oe(n){if((n.expectedSource??window.parent)===window)throw new Error("GemWebSdkBridge.connect: this document is not framed (window.parent === window), so there is no embedder to hand it a port. A game runs inside the Gem Arcade iframe; opening it directly will never complete a handshake.");let e=new O({...n,expectedSource:n.expectedSource??window.parent}),t=r=>{let s=e.handleWindowMessage({data:r.data,origin:r.origin,source:r.source,ports:r.ports});return s&&window.removeEventListener("message",t),s};return window.addEventListener("message",t),W(window,t),e}var ae={connect:Oe,GemFrame:O,PROTOCOL:l,SUPPORTED_VERSIONS:S};window.GemWebSdkBridge=ae;var vn=ae;return vn;})();