@indiegems/gem-web-sdk 23.3.0 → 24.0.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 +30 -0
- package/dist/conformance.d.ts +5 -2
- package/dist/function.d.ts +22 -1
- package/dist/game.d.ts +1588 -1466
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +3 -3
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +5 -0
- package/dist/sdk.d.ts +1598 -1464
- package/dist/server.d.ts +44 -1
- package/docs/api/api-reference-function.md +194 -44
- package/docs/api/api-reference-game.md +722 -32
- package/docs/api/api-reference-server.md +477 -3
- package/docs/api/reference.json +1786 -99
- package/docs/arcade-and-store.md +166 -63
- package/docs/arcade-economy.md +184 -184
- package/docs/changelog.md +30 -0
- package/docs/deprecation-policy.md +2 -0
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +247 -76
- package/docs/manifest.json +20 -20
- package/docs/platform-rules.md +36 -27
- package/docs/player-sign-in.md +20 -11
- package/docs/rewarded-ads.md +1 -1
- package/docs/server-bundles.md +9 -7
- package/package.json +1 -1
- package/schema/bridge.v1.json +56 -7
package/dist/gem-web-sdk.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:23.3.0]";var g="gem-web-bridge",ne={min:1,max:1},Qi=65536,rr=8,mt=5e3,ir=1024,Qt=1e4,ge=Object.create(null),xe="__malformed",Za=["gem.handshake","gem.token","gem.token.denied","gem.player","gem.pause","gem.prefs","gem.cohorts","gem.earnAllowanceChanged","gem.inventoryChanged","gem.viewport","gem.deviceSettings","gem.willTerminate","gem.roomEvent","gem.socketState","gem.seatedEvent","gem.autoStartFailedEvent","gem.ticketEvent","gem.signalEvent","gem.relayDraining","gem.dedicatedEvent","gem.partyResult","gem.clusterTarget","gem.graphicsRoutes","gem.devtools","gem.screenshot","gem.requestContext","gem.localServer","gem.localServer.denied","gem.menuSelect","gem.settingChange","gem.shareResult","gem.error","gem.purchaseResult","gem.purchaseSettled","gem.deviceSettings.result","gem.adResult","gem.momentResult","gem.signInResult","gem.friendChallengeResult","gem.playInviteResult","gem.appLinksResult","gem.openAppResult","gem.openGameResult","gem.similarGamesResult","gem.storeOffersResult","gem.storePurchaseResult","gem.storePurchaseSettled","gem.storeOfferSheetResult"],el=["gem.handshake.accept","gem.screenshot.denied","gem.gameContext","gem.signal","gem.dedicatedOffer","gem.partyRequest","gem.ready","gem.engaged","gem.progress","gem.requestToken","gem.requestLocalServer","gem.exit","gem.openExternal","gem.requestFullscreen","gem.audioState","gem.rewardCue","gem.rewardPresentation","gem.walletDisplay","gem.controlsDeclare","gem.menu","gem.gemCorner","gem.menuStyle","gem.settingsDeclare","gem.requestShare","gem.requestAvatarPicker","gem.vibrate","gem.frameStats","gem.portraitBorders","gem.error","gem.requestPurchase","gem.requestAvatar3dPurchase","gem.deviceSettings.put","gem.requestAd","gem.signalMoment","gem.requestSignIn","gem.requestFriendChallenge","gem.requestPlayInvite","gem.inviteParams","gem.requestAppLinks","gem.requestOpenApp","gem.requestOpenGame","gem.requestSimilarGames","gem.requestStoreOffers","gem.requestStorePurchase","gem.requestStoreOfferSheet"],Zt=Object.create(null),Zi=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.textScale.","textScale"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"],["gem.share.","shareRemap"]]);function he(n){let e=Zt[n];if(e!==void 0)return e;for(let[t,r]of Zi)if(n.startsWith(t))return r}function gt(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 eo(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 to(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function no(n){return /^[a-z_]+$/.test(n)}function ro(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 Xt(n,e,t){if(e>rr)return `nesting deeper than ${rr}`;if(++t.nodes>mt)return `more than ${mt} 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 i=n;if(t.seen.has(i))return "a repeated or cyclic object reference";if(t.seen.add(i),Array.isArray(n)){if(n.length>ir)return `an array of ${n.length} entries, over the ${ir} limit`;t.bytes+=2;for(let s of n){let l=Xt(s,e+1,t);if(l)return l}return null}let o=Object.getPrototypeOf(n);if(o!==Object.prototype&&o!==null)return "a value that is not a plain object";t.bytes+=2;let a=n;for(let s in a){if(!Object.prototype.hasOwnProperty.call(a,s))continue;if(++t.nodes>mt)return `more than ${mt} values`;if(t.bytes+=s.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let l=Xt(a[s],e+1,t);if(l)return l}return null}function io(n,e=Qi){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=Xt(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var oo={origin:[gt,"not a well-formed origin"],url:[eo,"not a safe absolute http(s) URL"],bcp47:[to,"not a well-formed BCP 47 tag"],code:[no,"not a code of lower-case letters and underscores"],gameRef:[ro,"not a game id or web address"]};function or(n,e,t){for(let r of e){let i=t===""?r[0]:`${t}.${r[0]}`,o=n[r[0]];if(o==null){if(r[1]<"a")continue;return `${i}: required`}let a=Yt(o,r,i);if(a)return a}return null}function Yt(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:Yt(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[i,o]=oo[e[3]];if(!i(n))return `${t}: ${o}`}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 i=e[2];return i.includes(n)?null:`${t}: not one of ${i.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:or(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 i=e[3],o=`${t}[]`;for(let a of n){if(a==null){if(i[1]<"a")continue;return `${o}: required`}let s=Yt(a,i,o);if(s)return s}return null}default:return `${t}: unknown kind ${String(r)}`}}var sr=Object.create(null);function h(n,e,t,r,i){return ge[n]=t,r&&(Zt[n]=r),sr[n]=o=>{let a=io(o,i);if(a)return a;if(typeof o!="object"||o===null||Array.isArray(o))return "message: expected object";let s=o;return s.gem!==g?"gem: wrong protocol marker":s.type!==n?`type: expected ${n}`:or(s,e,"")}}var je=["","s",64],S=["requestId","s",64],Ue=["matchId","s",64],re=["reason","S",64,"code"],le=["outcome","e",["ok","declined","unavailable"]],tl=h("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,je],["embedder","s",2048,"origin"],["unbidden","B"]],30),nl=h("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,je]],5),rl=h("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),il=h("gem.token.denied",[S,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30),ol=h("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]]],["signInAvailable","B"]],12),sl=h("gem.pause",[["paused","b"]],120),al=h("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"],["textScale","I",100,200]]]],60),ll=h("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts"),dl=h("gem.earnAllowanceChanged",[],30,"earnAllowance"),cl=h("gem.inventoryChanged",[],30,"inventoryChanged"),ul=h("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"),pl=h("gem.deviceSettings",[["settings","J"]],12,"deviceSettings"),ml=h("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10),gl=h("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"),hl=h("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState"),fl=h("gem.seatedEvent",[["roomId","s",64]],60,"partySeating"),yl=h("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure"),bl=h("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"),vl=h("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],Ue,["data","j"]],1800,"p2p"),_l=h("gem.relayDraining",[Ue,["clusterTargetId","s",64],["relayIps","a",16,je],["drainingSince","s",40],["deadline","s",40]],60,"p2pRelayDrain"),Rl=h("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],Ue,["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated"),wl=h("gem.partyResult",[S,["partyId","S",64]],30,"parties"),kl=h("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,je]],12,"clusterTarget"),Sl=h("gem.graphicsRoutes",[["entries","a",32,["","o",[["gpu","s",32],["route","S",32],["host","S",32]]]]],12,"graphicsRoutes"),xl=h("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools"),Al=h("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot"),Pl=h("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot"),Il=h("gem.requestContext",[],12,"feedbackContext"),Tl=h("gem.gameContext",[["context","j"]],12,"feedbackContext"),El=h("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],Ue,["data","j"]],1200,"p2p"),Cl=h("gem.dedicatedOffer",[Ue,["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated"),Ml=h("gem.partyRequest",[S],12,"parties"),Ol=h("gem.ready",[],5),Gl=h("gem.engaged",[],5,"engagement"),Ll=h("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600),Dl=h("gem.requestToken",[S,["reason","e",["initial","expiring","rejected"]]],12),ql=h("gem.requestLocalServer",[S,["gameRoomId","s",64]],6,"localHost"),$l=h("gem.localServer",[S,["serverId","S",128]],30,"localHost"),Fl=h("gem.localServer.denied",[S,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost"),Nl=h("gem.exit",[["reason","S",64]],5),jl=h("gem.openExternal",[["url","s",2048,"url"]],6),Ul=h("gem.requestFullscreen",[["enabled","b"]],30),Wl=h("gem.audioState",[["muted","b"]],120,"audioState"),Hl=h("gem.rewardCue",[["currencySlug","s",32],["cueId","S",64],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue"),Bl=h("gem.rewardPresentation",[["mode","e",["arcade","game"]]],12,"rewardPresentation"),Vl=h("gem.walletDisplay",[["visible","b"],["currencies","A",8,je],["score","B"]],60,"walletDisplay"),Kl=h("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls"),Jl=h("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu"),zl=h("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu"),Xl=h("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner"),Yl=h("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle"),Ql=h("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"),Zl=h("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings"),ed=h("gem.requestShare",[["requestId","S",64],["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share"),td=h("gem.shareResult",[S,["outcome","e",["accepted","refused"]],re,["key","S",64]],30,"shareResult"),nd=h("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker"),rd=h("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration"),id=h("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats"),od=h("gem.portraitBorders",[["name","S",40]],60,"portraitBorders"),sd=h("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60),ad=h("gem.requestPurchase",[S,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64],["origin","E",["sheet","direct_refused"]]],12,"purchases"),ld=h("gem.requestAvatar3dPurchase",[S,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases"),dd=h("gem.purchaseResult",[S,le,re,["purchaseId","S",64]],30,"purchases"),cd=h("gem.purchaseSettled",[["purchaseKind","e",["item","recipe"]],["targetId","s",64],["catalogId","S",64],["quantity","i",1,10],["purchaseId","S",64]],30,"purchaseSettled"),ud=h("gem.deviceSettings.put",[S,["settings","j"]],30,"deviceSettings"),pd=h("gem.deviceSettings.result",[S,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings"),md=h("gem.requestAd",[S,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads"),gd=h("gem.adResult",[S,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads"),hd=h("gem.signalMoment",[S,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments"),fd=h("gem.momentResult",[S,["shown","b"]],12,"moments"),yd=h("gem.requestSignIn",[S],6,"signIn"),bd=h("gem.signInResult",[S,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn"),vd=h("gem.requestFriendChallenge",[S,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges"),_d=h("gem.friendChallengeResult",[S,["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"),Rd=h("gem.requestPlayInvite",[S,["params","A",16,["","o",[["key","s",64],["value","s",512]]]]],6,"playInvites"),wd=h("gem.playInviteResult",[S,["shown","b"]],12,"playInvites"),kd=h("gem.inviteParams",[["params","A",16,["","o",[["key","s",64],["value","s",512]]]]],30,"playInvites"),Sd=h("gem.requestAppLinks",[S],12,"appLinks"),xd=h("gem.appLinksResult",[S,le,re,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks"),Ad=h("gem.requestOpenApp",[S,["target","e",["arcade","game"]]],6,"appLinks"),Pd=h("gem.openAppResult",[S,le,re],30,"appLinks"),Id=h("gem.requestOpenGame",[S,["ref","s",40,"gameRef"]],6,"openGame"),Td=h("gem.openGameResult",[S,le,re],30,"openGame"),Ed=h("gem.requestSimilarGames",[S,["limit","I",1,24]],12,"openGame"),Cd=h("gem.similarGamesResult",[S,le,re,["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),Md=h("gem.requestStoreOffers",[S],12,"store"),Od=h("gem.storeOffersResult",[S,le,re,["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),Gd=h("gem.requestStorePurchase",[S,["skuId","s",64]],12,"store"),Ld=h("gem.storePurchaseResult",[S,le,re],30,"store"),Dd=h("gem.storePurchaseSettled",[["skuId","s",64]],30,"store"),qd=h("gem.requestStoreOfferSheet",[S,["skuId","s",64]],12,"storeOfferSheet"),$d=h("gem.storeOfferSheetResult",[S,le,re],30,"storeOfferSheet"),de=Object.freeze(sr);ge.__malformed=60;Object.freeze(ge);Object.freeze(Zt);var ht=3;function H(n){if(typeof n!="string")return;let e=n.trim();return e.length===0?void 0:e}function V(n){if(!Array.isArray(n))return [];let e=[];for(let t of n){let r=H(t);if(!(r===void 0||e.includes(r))&&(e.push(r),e.length===3))break}return e}var so=2e3,ao=5e3;function lo(n){let e=[],t=false,r=null,i=new Map,o=()=>{if(e.length>0)return Promise.resolve(e);if(t)return Promise.resolve([]);if(!n.bridge.parentSupports("clusterTarget"))return Promise.resolve([]);if(r!==null)return r.promise;let a,s=new Promise(l=>{let d=false,m=null;a=c=>{d||(d=true,r=null,n.clock.clearTimeout(m),l(c));},m=n.clock.setTimeout(()=>{t=true,a([]);},n.timeoutMs??so);});return r={promise:s,settle:a},s};return {resolveList:o,resolve(){return e.length>0||t||!n.bridge.parentSupports("clusterTarget")?Promise.resolve(e[0]):o().then(a=>a[0])},refresh(a){return !ar(e,a)||!n.bridge.parentSupports("clusterTarget")?Promise.resolve(e):new Promise(s=>{let l=null,d=m=>{i.delete(d)&&(n.clock.clearTimeout(l),s(m));};i.set(d,a),l=n.clock.setTimeout(()=>d(e),n.refreshTimeoutMs??ao);})},accept(a,s){let l=V(s),d=H(a),m=l.length>0?l:d===void 0?[]:[d];if(m.length!==0){e=m,r?.settle(m);for(let[c,p]of [...i])ar(m,p)||c(m);}}}}function ar(n,e){return n.length===e.length&&n.every((t,r)=>t===e[r])}var co=["angle_gles","native_gl","angle_opengl","angle_other","angle_d3d11","angle_d3d9","angle_metal"],uo=Object.freeze(co.map(n=>Object.freeze({gpu:"powervr_d_series",route:n})));var po=32,mo=32;function lr(n){if(!Array.isArray(n)||n.length>po)return null;let e=[];for(let t of n){if(t===null||typeof t!="object"||Array.isArray(t))return null;let{gpu:r,route:i,host:o}=t;if(!en(r)||i!==void 0&&!en(i)||o!==void 0&&!en(o))return null;e.push(Object.freeze({gpu:r,...i===void 0?{}:{route:i},...o===void 0?{}:{host:o}}));}return Object.freeze(e)}function en(n){return typeof n=="string"&&n!==""&&n.length<=mo}var ho=/^[a-z0-9][a-z0-9_-]{0,39}$/;function tn(n){return typeof n=="string"&&ho.test(n)}function dr(n){if(n===null||tn(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 cr=Object.freeze(["music","sfx","voice","eye","text","hint","speed","bright","controls","star"]),ur=/^[A-Za-z0-9_-]{1,40}$/;function nn(n){return typeof n=="number"&&Number.isFinite(n)&&Math.abs(n)<=1e6}function pr(n){if(typeof n!="string")return;let e=n.trim();return e.length===0?void 0:e.slice(0,40)}function mr(n,e,t,r){let i=s=>Number.parseFloat(s.toPrecision(12)),o=Math.min(t,Math.max(e,n)),a=i(e+Math.round((o-e)/r)*r);return a>t&&(a=i(a-r)),Math.min(t,Math.max(e,a))}function gr(n,e){switch(n.kind){case "toggle":return typeof e=="boolean"?e:void 0;case "slider":{if(typeof e!="number"||!Number.isFinite(e))return;let t=n.min??0,r=n.max??100;return mr(e,t,r,n.step??1)}case "choice":return typeof e=="string"&&(n.options??[]).some(t=>t.id===e)?e:void 0;default:return}}function hr(n,e=()=>{}){if(!Array.isArray(n))return null;let t=[],r=0;for(let i=0;i<n.length;i+=1){let o=n[i],a=`settings[${i}]`;if(t.length===8){e(`${a} and later were left out: at most 8 settings are shown.`);break}if(typeof o!="object"||o===null){e(`${a} was dropped: a setting is an object.`);continue}let s=o,l=s.id;if(typeof l!="string"||!ur.test(l)){e(`${a} was dropped: its id must be 1 to 40 of A-Z a-z 0-9 _ -.`);continue}let d=`${a} (${JSON.stringify(l)})`;if(t.some(p=>p.id===l)){e(`${d} was dropped: an earlier setting has the same id.`);continue}let m=pr(s.label);if(m===void 0){e(`${d} was dropped: its label is empty.`);continue}let c;switch(s.kind){case "toggle":{if(typeof s.value!="boolean"){e(`${d} was dropped: a toggle's value is true or false.`);continue}c={id:l,label:m,kind:"toggle",value:s.value},s.quick===true&&(r<2?(c.quick=true,r+=1):e(`${d} is not in the quick row: at most 2 toggles are, the first that ask.`));break}case "slider":{let p=s.min??0,y=s.max??100,b=s.step??1;if(!nn(p)||!nn(y)||!nn(b)||p>=y||b<=0){e(`${d} was dropped: a slider needs finite numbers within \xB11000000 with min below max and a step above 0.`);continue}if(typeof s.value!="number"||!Number.isFinite(s.value)){e(`${d} was dropped: a slider's value is a number.`);continue}c={id:l,label:m,kind:"slider",value:mr(s.value,p,y,b),min:p,max:y,step:b};break}case "choice":{let p=[];for(let y of Array.isArray(s.options)?s.options:[]){if(p.length===4)break;if(typeof y!="object"||y===null)continue;let b=y,v=pr(b.label);typeof b.id!="string"||!ur.test(b.id)||v===void 0||p.some(A=>A.id===b.id)||p.push({id:b.id,label:v});}if(p.length<2){e(`${d} was dropped: a choice needs 2 to 4 options, each with an id of 1 to 40 of A-Z a-z 0-9 _ - and a label.`);continue}if(typeof s.value!="string"||!p.some(y=>y.id===s.value)){e(`${d} was dropped: a choice's value is the id of one of its options.`);continue}c={id:l,label:m,kind:"choice",value:s.value,options:p};break}default:e(`${d} was dropped: its kind must be 'toggle', 'slider' or 'choice'.`);continue}s.icon!==void 0&&(cr.includes(s.icon)?c.icon=s.icon:e(`${d} keeps the Arcade's generic icon: ${JSON.stringify(s.icon)} is not one of ${cr.join(", ")}.`)),t.push(c);}return t}var We=16384,ft=24;function x(n){return e=>typeof e=="string"&&e.length>0&&e.length<=n?e:void 0}function ie(n,e){return t=>typeof t=="number"&&Number.isInteger(t)&&t>=n&&t<=e?t:void 0}function Ae(...n){return e=>typeof e=="string"&&n.includes(e)?e:void 0}function fr(n){return typeof n=="boolean"?n:void 0}var L=x(64),He=ie(0,2147483647);function yt(n){if(!Array.isArray(n))return;let e=[];for(let t of n){if(e.length>=64)break;let r=L(t);typeof r=="string"&&e.push(r);}return e}function P(n,e,t){return {to:n,from:e,read:t,required:true}}function I(n,e,t){return {to:n,from:e,read:t,required:false}}function Pe(n,e){let t={};for(let r of e){let i=r.read(n[r.from]);if(i===void 0){if(r.required)return null;continue}t[r.to]=i;}return t}function Be(n){if(typeof n!="object"||n===null||Array.isArray(n))return null;let e=n;return typeof e.type!="string"||typeof e.data!="object"||e.data===null||Array.isArray(e.data)?null:{type:e.type,data:e.data}}var yr=[P("sdp","sdp",x(10240)),P("signaling_intent","intent",Ae("initial","ice_restart","full_reconnect"))];function bt(n,e,t){if(typeof t!="object"||t===null||Array.isArray(t))return {reason:"offer: data must be an object"};let r=t,i=Pe(r,yr);return i===null?{reason:`offer: '${yr.find(a=>a.read(r[a.from])===void 0)?.from??"?"}' is missing or out of bounds`}:{frame:{type:"webrtc_offer",match_id:n,game_id:e,...i}}}var rn=Object.freeze(Object.assign(Object.create(null),{webrtc_sdp_answer:"answer",webrtc_error:"error",match_ended:"matchEnded"})),fo=new Set(["error"]),yo=Object.freeze(Object.assign(Object.create(null),{answer:[P("sdp","sdp",x(10240)),I("sessionId","session_id",x(64))],error:[I("code","code",x(64)),I("message","message",x(256))],matchEnded:[I("reason","reason",x(64))]}));function vt(n,e,t){let r=Be(n);if(r===null)return null;let i=rn[r.type];if(i===void 0)return null;let o=L(r.data.match_id);if(typeof o!="string"||!(fo.has(i)?t(o):r.data.game_id===e))return null;let s=Pe(r.data,yo[i]??[]);return s===null?null:{event:i,matchId:o,...s}}var bo=n=>{if(typeof n!="object"||n===null||Array.isArray(n))return;let e=n,t=Ae("disconnected","failed","closed")(e.iceState),r=ie(0,4102444800)(e.lastSeenAt);if(!(t===void 0&&r===void 0))return {...t===void 0?{}:{ice_state:t},...r===void 0?{}:{last_seen_timestamp:r}}},vo=Object.freeze(Object.assign(Object.create(null),{join:{type:"p2p_join",gameScoped:true,args:[]},offer:{type:"p2p_offer",gameScoped:false,args:[P("sdp","sdp",x(10240)),P("signaling_intent","intent",Ae("initial","ice_restart","full_reconnect")),I("sdp_hash","sdpHash",x(128))]},answer:{type:"p2p_answer",gameScoped:false,args:[P("peer_id","peerId",L),P("sdp","sdp",x(10240)),P("assigned_peer_id","assignedPeerId",ie(0,4294967295)),I("sdp_hash","sdpHash",x(128))]},ice:{type:"p2p_ice_candidate",gameScoped:false,args:[P("peer_id","peerId",L),P("candidate","candidate",x(512)),I("sdp_mid","sdpMid",x(64)),I("sdp_mline_index","sdpMLineIndex",ie(0,255))]},migrationAck:{type:"p2p_migration_ack",gameScoped:false,args:[P("host_epoch","hostEpoch",He)]},hostUnreachable:{type:"p2p_report_host_unreachable",gameScoped:false,args:[P("host_epoch","hostEpoch",He),I("evidence","evidence",bo)]}}));function _t(n,e,t,r){let i=vo[n];if(i===void 0)return {reason:`unknown signal '${n}'`};if(typeof r!="object"||r===null||Array.isArray(r))return {reason:`${n}: data must be an object`};let o=r,a=Pe(o,i.args);if(a===null){let s=i.args.find(l=>l.required&&l.read(o[l.from])===void 0);return {reason:`${n}: '${s?.from??"?"}' is missing or out of bounds`}}return {frame:{type:i.type,match_id:e,...i.gameScoped?{game_id:t}:{},...a}}}var _o=Object.freeze(Object.assign(Object.create(null),{p2p_hello:"hello",p2p_sdp_offer:"offer",p2p_sdp_answer:"answer",p2p_ice_candidate:"ice",p2p_peer_joined:"peerJoined",p2p_peer_left:"peerLeft",p2p_host_migration:"migration",p2p_match_ended:"matchEnded",p2p_error:"error",p2p_relay_draining:"relayDraining"})),Ro=16,wo=n=>yt(n)?.slice(0,Ro),ko=new Set(["error","relayDraining"]),So=Object.freeze(Object.assign(Object.create(null),{hello:[P("playerId","player_id",L),P("hostPlayerId","host_player_id",L),P("hostEpoch","host_epoch",He),I("members","members",yt)],offer:[P("fromPlayerId","from_player_id",L),P("sdp","sdp",x(10240)),I("sessionId","signaling_session_id",x(128)),I("sdpHash","sdp_hash",x(128)),I("iceUfrag","ice_ufrag",x(64)),I("intent","signaling_intent",Ae("initial","ice_restart","full_reconnect"))],answer:[P("fromPlayerId","from_player_id",L),P("sdp","sdp",x(10240)),I("sessionId","signaling_session_id",x(128)),I("sdpHash","sdp_hash",x(128)),I("iceUfrag","ice_ufrag",x(64)),I("assignedPeerId","assigned_peer_id",ie(0,4294967295))],ice:[P("fromPlayerId","from_player_id",L),P("candidate","candidate",x(512)),I("sdpMid","sdp_mid",x(64)),I("sdpMLineIndex","sdp_m_line_index",ie(0,255)),I("sdpMLineIndexAlt","sdp_mline_index",ie(0,255))],peerJoined:[P("playerId","player_id",L)],peerLeft:[P("playerId","player_id",L),I("reason","reason",x(64))],migration:[P("hostPlayerId","new_host_player_id",L),P("hostEpoch","host_epoch",He),I("reason","reason",x(64)),I("kickedPlayerIds","kicked_player_ids",yt),I("migrationComplete","migration_complete",fr)],matchEnded:[I("reason","reason",x(64))],error:[I("code","code",x(64)),I("message","message",x(256))],relayDraining:[P("clusterTargetId","cluster_target_id",L),P("relayIps","relay_ips",wo),P("drainingSince","draining_since",x(40)),P("deadline","deadline",x(40))]}));function Rt(n,e,t){let r=Be(n);if(r===null)return null;let i=_o[r.type];if(i===void 0)return null;let o=L(r.data.match_id);if(typeof o!="string"||!(ko.has(i)?t(o):r.data.game_id===e))return null;let s=Pe(r.data,So[i]??[]);return s===null?null:{event:i,matchId:o,data:s}}var xo=Object.freeze(Object.assign(Object.create(null),{party_invite_received:"inviteReceived",party_member_joined:"memberJoined",party_member_left:"memberLeft",party_member_kicked:"memberKicked",party_leader_transferred:"leaderTransferred",party_updated:"updated",party_disbanded:"disbanded",party_game_room_joined:"gameRoomJoined",party_game_room_left:"gameRoomLeft"}));function Ve(n){let e=Be(n);if(e===null)return null;let t=xo[e.type];if(t===void 0)return null;let r=e.data,i=L(r.party_id);if(typeof i!="string")return null;let o=ie(0,Number.MAX_SAFE_INTEGER)(r.version),a=L(r.game_room_id),s=L(r.game_id),l=L(r.inviter_player_id),d=x(80)(r.inviter_display_tag),m=x(64)(r.party_name);return {event:t,partyId:i,...o===void 0?{}:{version:o},...a===void 0?{}:{gameRoomId:a},...s===void 0?{}:{gameId:s},...l===void 0?{}:{inviterPlayerId:l},...d===void 0?{}:{inviterDisplayTag:d},...m===void 0?{}:{partyName:m}}}var ce={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)},oe=class{constructor(e=ce){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let t=ge[e];if(t===void 0)return false;let r=this.clock.now(),i=this.lastRefill.get(e)??r,o=this.tokens.get(e)??t,a=Math.max(0,r-i),s=Math.min(t,o+a*t/6e4);return s<1?(this.tokens.set(e,s),this.lastRefill.set(e,r),false):(this.tokens.set(e,s-1),this.lastRefill.set(e,r),true)}};function ye(n,e){return n.toLowerCase()===e.toLowerCase()}function Te(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function vr(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:Te(t.hostname)&&Te(r.hostname)}function on(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 Ao=7,sn=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,Po=/^[a-z][a-z0-9_]{0,31}$/,Io=8,Rr=256,To=["universal","mobile"],Eo={game_room_updated:"updated",game_room_match_started:"match_started",game_room_match_ready:"match_ready",game_room_match_failed:"match_failed",game_room_version_stale:"version_stale",match_ended:"updated",p2p_match_ended:"updated"},Co=new Set(["match_ended","p2p_match_ended"]),Mo="game_room_auto_start_failed",wr={matchmaking_ticket_queued:"queued",matchmaking_ticket_matched:"matched",matchmaking_ticket_expired:"expired",matchmaking_ticket_cancelled:"cancelled",matchmaking_ticket_failed:"failed"},Oo="game_inventory_changed",Ke=2e3,Go=64,kr=/^[a-z][a-z0-9_]{0,63}$/,Je=64,Lo=8,ln=class{constructor(e,t){this.clock=e;this.limit=t;}clock;limit;windowStart=0;used=0;allow(){let e=this.clock.now();return e-this.windowStart>=1e3&&(this.windowStart=e,this.used=0),this.used>=this.limit?false:(this.used+=1,true)}},Do={"gem.requestAppLinks":["onRequestAppLinks","gem.appLinksResult"],"gem.requestOpenApp":["onRequestOpenApp","gem.openAppResult"],"gem.requestOpenGame":["onRequestOpenGame","gem.openGameResult"],"gem.requestSimilarGames":["onRequestSimilarGames","gem.similarGamesResult"],"gem.requestStoreOffers":["onRequestStoreOffers","gem.storeOffersResult"],"gem.requestStorePurchase":["onRequestStorePurchase","gem.storePurchaseResult"],"gem.requestStoreOfferSheet":["onRequestStoreOfferSheet","gem.storeOfferSheetResult"]},qo=64,$o="error";function Fo(n){let e=n.toLowerCase().replace(/[^a-z_]/g,"_").slice(0,qo);return e===""?$o:e}function ue(n){return n===void 0?{}:{reason:Fo(String(n))}}function Ee(n){if(!(typeof n!="string"||n.length===0||n.length>Je))return n}function Sr(n){return typeof n=="number"&&Number.isSafeInteger(n)&&n>=0?n:void 0}function No(n){return n instanceof Error?n.message:String(n)}function xr(n,e){if(!n.has(e)){if(n.size>=Lo){let t=n.values().next().value;t!==void 0&&n.delete(t);}n.add(e);}}function an(){return new MessageChannel}var jo=6e4/(ge["gem.handshake"]??1),Uo=Object.freeze([100,115,130,150,180,200]),Wo=6e4,dn=class{constructor(e){this.options=e;if(!gt(e.frameOrigin))throw new Error(`GemHost: frameOrigin must be a concrete origin, got '${e.frameOrigin}'. '*' and 'null' are refused \u2014 the handshake transfers the MessagePort, so an imprecise targetOrigin gives the session away.`);if(!gt(e.embedderOrigin))throw new Error(`GemHost: embedderOrigin must be a concrete origin, got '${e.embedderOrigin}'.`);this.clock=e.clock??ce,this.inbound=new oe(this.clock),this.outbound=new oe(this.clock),this.socketWindow=new ln(this.clock,24);}options;state="idle";port=null;version=null;timeoutHandle=null;reofferHandle=null;makeChannel=an;offered=[];offersMade=0;localDenials=0;chainEndsAt=null;gameId=null;grantedChannel=null;inventoryHintAt=null;inventoryHintTimer=null;peerCapabilities=[];lastPrefs=null;prefsCalls=0;knownMatches=new Set;askedMatches=new Set;declaredSettings=[];clock;inbound;outbound;socketWindow;get currentState(){return this.state}get frameCapabilities(){return this.peerCapabilities}frameSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}start(e=an){if(this.state!=="idle"){this.fail("handshake_duplicate",`cannot start from state '${this.state}'`);return}if(typeof e=="function")this.makeChannel=e;else if(e&&e.port1&&e.port2){let t=e,r=false;this.makeChannel=()=>r?an():(r=true,t);}else throw new Error("GemHost.start: pass a function returning { port1, port2 } \u2014 omit it entirely to get a fresh MessageChannel per offer, which is what an embedder normally wants.");this.transition("handshaking"),this.offer(),this.scheduleReoffer(),this.timeoutHandle=this.clock.setTimeout(()=>{this.timeoutHandle=null,this.state==="handshaking"&&this.fail("handshake_timeout",`no answer from ${this.options.frameOrigin} to ${this.offersMade} handshake ${this.offersMade===1?"offer":"offers"} in the first ${Qt}ms; the offers continue for as long as this page holds the frame. This page presented embedder origin ${this.options.embedderOrigin}. A frame answers none of them if it carries no bridge at all, if it has not started listening yet, or if that origin is missing from the embedders it will accept \u2014 this side cannot tell which, and does not try.`);},Qt);}offer(){if(!this.outbound.allow("gem.handshake"))return false;let e=this.makeChannel(),t=e.port1;return t.onmessage=r=>this.receive(r.data,t),t.start?.(),this.offered.push({port:t,at:this.clock.now()}),this.offersMade+=1,this.options.frame.postMessage({gem:g,type:"gem.handshake",versions:{min:ne.min,max:ne.max},embedder:this.options.embedderOrigin,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]},...this.options.unbidden===true?{unbidden:true}:{}},this.options.frameOrigin,[e.port2]),true}scheduleReoffer(){this.reofferHandle=this.clock.setTimeout(()=>{this.reofferHandle=null,this.state==="handshaking"&&(this.offer(),this.pruneStaleOffers(),this.scheduleReoffer());},jo);}pruneStaleOffers(){let e=this.clock.now()-Wo;for(;this.offered.length>0&&(this.offered[0]?.at??e)<e;){let t=this.offered.shift();if(t===void 0)break;t.port.onmessage=null,t.port.close();}}adopt(e){for(let t of this.offered)t.port!==e&&(t.port.onmessage=null,t.port.close());this.offered.length=0,this.port=e;}static checkHandshakeOrigin(e,t,r){return ye(e.origin,t)?e.source!==r?"source is not the mounted frame":null:`origin '${e.origin}' is not the expected frame origin`}sendToken(e){if(!Number.isInteger(e.chainEndsAt)||e.chainEndsAt<0){this.fail("internal","refusing a grant whose chainEndsAt is not a whole number of seconds >= 0");return}let t=this.chainEndsAt??e.chainEndsAt;if(this.chainExpired()){this.fail("token_unavailable","token chain ceiling reached");return}this.send({gem:g,type:"gem.token",token:e.token,expiresAt:e.expiresAt,chainEndsAt:t,generation:e.generation,playerId:e.playerId,gameId:e.gameId,channel:e.channel,...e.gameVersion===void 0?{}:{gameVersion:e.gameVersion},...e.requestId===void 0?{}:{requestId:e.requestId},...e.apiBaseUrl===void 0?{}:{apiBaseUrl:e.apiBaseUrl},...e.allowDevTools===true?{allowDevTools:true}:{},...e.garden===void 0?{}:{garden:e.garden}})&&(this.chainEndsAt=t,this.gameId===null&&(this.gameId=e.gameId,this.grantedChannel=e.channel));}relayRoomEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(typeof t.type!="string")return false;let r=Eo[t.type];if(r===void 0||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let i=t.data;if(i.game_id!==this.gameId)return false;let o=Ee(i.game_room_id);if(o===void 0)return false;let a=Co.has(t.type)?void 0:Ee(i.match_id);a!==void 0&&this.rememberMatch(a);let s=typeof i.version=="number"?i.version:void 0,l=typeof i.reason=="string"?i.reason.slice(0,Je):void 0,d=r==="version_stale"&&To.includes(i.platform)?i.platform:void 0;return this.sendRoomEvent({event:r,roomId:o,...a===void 0?{}:{matchId:a},...s===void 0?{}:{version:s},...l===void 0?{}:{reason:l},...d===void 0?{}:{platform:d}})}sendRoomEvent(e){return this.frameSupports("rooms")?this.send({gem:g,type:"gem.roomEvent",event:e.event,roomId:e.roomId,...e.matchId===void 0?{}:{matchId:e.matchId},...e.version===void 0?{}:{version:e.version},...e.reason===void 0?{}:{reason:e.reason},...e.platform===void 0?{}:{platform:e.platform}}):false}sendSocketState(e){return this.frameSupports("socketState")?this.send({gem:g,type:"gem.socketState",connected:e.connected,...e.connected?{}:{reason:e.reason}}):false}sendSeatedEvent(e){return this.frameSupports("partySeating")?this.send({gem:g,type:"gem.seatedEvent",roomId:e}):false}relaySeatedEvent(e){if(this.gameId===null)return false;let t=Ve(e);if(t===null||t.event!=="gameRoomJoined"||t.gameId!==this.gameId)return false;let r=Ee(t.gameRoomId);return r===void 0?false:this.sendSeatedEvent(r)}sendAutoStartFailedEvent(e){return this.frameSupports("autoStartFailure")?this.send({gem:g,type:"gem.autoStartFailedEvent",roomId:e.roomId,code:e.code}):false}relayAutoStartFailedEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(t.type!==Mo||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let r=t.data;if(r.game_id!==this.gameId)return false;let i=Ee(r.game_room_id),o=typeof r.code=="string"&&kr.test(r.code)?r.code:void 0;return i===void 0||o===void 0?false:this.sendAutoStartFailedEvent({roomId:i,code:o})}sendTicketEvent(e){return this.frameSupports("ticketEvents")?this.send({gem:g,type:"gem.ticketEvent",event:e.event,ticketId:e.ticketId,...e.partySize===void 0?{}:{partySize:e.partySize},...e.queuedAt===void 0?{}:{queuedAt:e.queuedAt},...e.expiresAt===void 0?{}:{expiresAt:e.expiresAt},...e.reason===void 0?{}:{reason:e.reason},...e.roomId===void 0?{}:{roomId:e.roomId}}):false}relayTicketEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(typeof t.type!="string"||!Object.hasOwn(wr,t.type))return false;let r=wr[t.type];if(r===void 0||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let i=t.data;if(i.game_id!==this.gameId)return false;let o=Ee(i.ticket_id);if(o===void 0)return false;if(r==="queued"){let s=typeof i.party_size=="number"&&Number.isInteger(i.party_size)&&i.party_size>=1&&i.party_size<=Go?i.party_size:void 0,l=Sr(i.queued_at),d=Sr(i.expires_at);return this.sendTicketEvent({event:r,ticketId:o,...s===void 0?{}:{partySize:s},...l===void 0?{}:{queuedAt:l},...d===void 0?{}:{expiresAt:d}})}if(r==="matched"){let s=Ee(i.game_room_id);return this.sendTicketEvent({event:r,ticketId:o,...s===void 0?{}:{roomId:s}})}let a=typeof i.reason=="string"&&kr.test(i.reason)?i.reason:void 0;return this.sendTicketEvent({event:r,ticketId:o,...a===void 0?{}:{reason:a}})}relayInventoryEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(t.type!==Oo||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let r=t.data;return r.game_id!==this.gameId||r.channel!==this.grantedChannel?false:this.sendInventoryChanged()}sendInventoryChanged(){if(!this.frameSupports("inventoryChanged"))return false;if(this.state!=="connected")return this.flushInventoryHint();if(this.inventoryHintTimer!==null)return true;let e=this.inventoryHintAt===null?1/0:this.clock.now()-this.inventoryHintAt;return e>=Ke&&this.flushInventoryHint()||this.armInventoryHint(e>=Ke?Ke:Ke-e),true}flushInventoryHint(){let e=this.send({gem:g,type:"gem.inventoryChanged"});return e&&(this.inventoryHintAt=this.clock.now()),e}armInventoryHint(e){this.inventoryHintTimer=this.clock.setTimeout(()=>{this.inventoryHintTimer=null,this.state==="connected"&&(this.flushInventoryHint()||this.armInventoryHint(Ke));},e);}relaySocketEvent(e){let t=[];(this.relayRoomEvent(e)||this.relayAutoStartFailedEvent(e))&&t.push("room"),this.relaySignalEvent(e)&&t.push("signal"),this.relayDedicatedEvent(e)&&t.push("dedicated"),this.relayTicketEvent(e)&&t.push("ticket"),this.relayInventoryEvent(e)&&t.push("inventory");let r=Ve(e);return r!==null&&(this.options.onPartyEvent?.(r),this.relaySeatedEvent(e),t.push("party")),t}relaySignalEvent(e){if(this.gameId===null)return false;let t=Rt(e,this.gameId,r=>this.knowsMatch(r));return t===null?false:(t.event==="hello"&&this.rememberMatch(t.matchId),this.sendSignalEvent(t))}sendSignalEvent(e){if(!this.frameSupports("p2p"))return false;if(e.event==="relayDraining"){if(!this.frameSupports("p2pRelayDrain"))return false;let t=e.data;return this.send({gem:g,type:"gem.relayDraining",matchId:e.matchId,clusterTargetId:t.clusterTargetId,relayIps:t.relayIps,drainingSince:t.drainingSince,deadline:t.deadline})}return this.send({gem:g,type:"gem.signalEvent",event:e.event,matchId:e.matchId,data:e.data})}relayDedicatedEvent(e){if(this.gameId===null)return false;let t=vt(e,this.gameId,r=>this.knowsMatch(r));return t===null?false:this.sendDedicatedEvent(t)}sendDedicatedEvent(e){return this.frameSupports("dedicated")?this.send({gem:g,type:"gem.dedicatedEvent",event:e.event,matchId:e.matchId,...e.sdp===void 0?{}:{sdp:e.sdp},...e.sessionId===void 0?{}:{sessionId:e.sessionId},...e.code===void 0?{}:{code:e.code},...e.message===void 0?{}:{message:e.message},...e.reason===void 0?{}:{reason:e.reason}}):false}knowsMatch(e){return this.knownMatches.has(e)||this.askedMatches.has(e)}rememberMatch(e){xr(this.knownMatches,e);}rememberAsked(e){xr(this.askedMatches,e);}answerPartyRequest(e,t){return this.send({gem:g,type:"gem.partyResult",requestId:e,...t===void 0?{}:{partyId:t}})}answerPurchase(e,t,r,i){return this.send({gem:g,type:"gem.purchaseResult",requestId:e,outcome:t,...r===void 0||t!=="ok"?{}:{purchaseId:r},...i===void 0||t==="ok"?{}:{reason:i}})}sendPurchaseSettled(e){return !this.options.capabilities?.includes("purchaseSettled")||!this.frameSupports("purchaseSettled")?false:this.send({gem:g,type:"gem.purchaseSettled",purchaseKind:e.purchaseKind,targetId:e.targetId,...e.catalogId===void 0?{}:{catalogId:e.catalogId},quantity:e.quantity,...e.purchaseId===void 0?{}:{purchaseId:e.purchaseId}})}answerDeviceSettingsPut(e,t){return this.send({gem:g,type:"gem.deviceSettings.result",requestId:e,outcome:t})}sendDeviceSettings(e){return this.frameSupports("deviceSettings")?this.send({gem:g,type:"gem.deviceSettings",...e===void 0?{}:{settings:e}}):false}sendMenuSelect(e){return this.frameSupports("gameMenu")?this.send(e==="settings"?{gem:g,type:"gem.menuSelect",entry:"settings"}:{gem:g,type:"gem.menuSelect",entry:"item",id:e.id}):false}sendSettingChange(e,t){if(!this.frameSupports("gameSettings"))return false;let r=this.declaredSettings.find(o=>o.id===e);if(r===void 0)return false;let i=gr(r,t);return i===void 0?false:this.send({gem:g,type:"gem.settingChange",id:e,value:i})}answerAd(e,t){return this.send({gem:g,type:"gem.adResult",requestId:e,outcome:t})}answerMoment(e,t){return this.send({gem:g,type:"gem.momentResult",requestId:e,shown:t})}answerSignIn(e,t){return this.send({gem:g,type:"gem.signInResult",requestId:e,outcome:t})}answerFriendChallenge(e,t,r={}){let i={};if(t==="invalid"){let{code:o,param:a,channel:s,configVersion:l,detail:d}=r;typeof o=="string"&&o.length<=64&&(i.code=o),typeof a=="string"&&a.length<=64&&(i.param=a),typeof s=="string"&&s.length<=16&&(i.channel=s),typeof l=="number"&&Number.isSafeInteger(l)&&l>=0&&(i.configVersion=l),typeof d=="string"&&(i.detail=d.slice(0,300));}else if(t==="rate_limited"){let o=r.retryAfterMs;typeof o=="number"&&Number.isFinite(o)&&(i.retryAfterMs=Math.min(864e5,Math.max(0,Math.round(o))));}return this.send({gem:g,type:"gem.friendChallengeResult",requestId:e,outcome:t,...i})}answerAppLinks(e,t,r={}){return this.send({gem:g,type:"gem.appLinksResult",requestId:e,outcome:t,...ue(r.reason),...r.arcade===void 0?{}:{arcade:{store:r.arcade}},...r.game===void 0?{}:{game:{store:r.game}}})}answerShare(e,t,r={}){return !this.options.capabilities?.includes("shareResult")||!this.frameSupports("shareResult")?false:this.send({gem:g,type:"gem.shareResult",requestId:e,outcome:t,...t==="refused"?ue(r.reason):{},...t==="refused"&&r.key!==void 0?{key:r.key}:{}})}answerOpenApp(e,t,r){return this.send({gem:g,type:"gem.openAppResult",requestId:e,outcome:t,...ue(r)})}answerOpenGame(e,t,r){return this.send({gem:g,type:"gem.openGameResult",requestId:e,outcome:t,...ue(r)})}answerSimilarGames(e,t,r=[],i){return this.send({gem:g,type:"gem.similarGamesResult",requestId:e,outcome:t,...ue(i),games:t==="ok"?[...r]:[]})}answerStoreOffers(e,t,r=[],i){return this.send({gem:g,type:"gem.storeOffersResult",requestId:e,outcome:t,...ue(i),offers:t==="ok"?[...r]:[]})}answerStorePurchase(e,t,r){return this.send({gem:g,type:"gem.storePurchaseResult",requestId:e,outcome:t,...ue(r)})}answerStoreOfferSheet(e,t,r){return this.send({gem:g,type:"gem.storeOfferSheetResult",requestId:e,outcome:t,...ue(r)})}sendStorePurchaseSettled(e){return this.frameSupports("store")?this.send({gem:g,type:"gem.storePurchaseSettled",skuId:e}):false}answerPlayInvite(e,t){return this.send({gem:g,type:"gem.playInviteResult",requestId:e,shown:t})}denyToken(e){this.send({gem:g,type:"gem.token.denied",requestId:e.requestId,retryable:e.retryable,reason:e.reason,correlationId:e.correlationId,...e.retryAfterMs===void 0?{}:{retryAfterMs:e.retryAfterMs}});}grantLocalServer(e){return this.frameSupports("localHost")?this.send({gem:g,type:"gem.localServer",requestId:e.requestId,...e.serverId===void 0?{}:{serverId:e.serverId}}):false}denyLocalServer(e){return this.frameSupports("localHost")?this.send({gem:g,type:"gem.localServer.denied",requestId:e.requestId,retryable:e.retryable,reason:e.reason}):false}sendClusterTarget(e){let t=typeof e=="string"?[e]:e,r=Array.isArray(t)?t.length===0?"[]":t.find(o=>typeof o!="string"||o.trim().length===0||o.length>Je):String(t);if(r!==void 0)return this.fail("internal",`refusing to send '${String(r)}' as a cluster target \u2014 each id must be a non-blank string of at most ${Je} characters, and a list must name at least one. Omit the message entirely when there is no answer: the frame then names no cluster and gem-api chooses.`),false;if(t.length>3)return this.fail("internal",`refusing to send ${t.length} cluster targets \u2014 at most ${3} acceptable clusters cross, best first.`),false;if(!this.frameSupports("clusterTarget"))return false;let[i]=t;return this.send({gem:g,type:"gem.clusterTarget",clusterTargetId:i,...typeof e=="string"?{}:{clusterTargetIds:[...t]}})}sendGraphicsRoutes(e){let t=lr(e);return t===null?(this.fail("internal","refusing to send an unusable graphics route list \u2014 at most 32 entries, each with a `gpu` and optionally a `route` and a `host`, every one a non-empty string of at most 32 characters."),false):this.frameSupports("graphicsRoutes")?this.send({gem:g,type:"gem.graphicsRoutes",entries:t.map(r=>({...r}))}):false}sendPlayer(e){let{displayName:t,displayTag:r}=e.untrusted,i=e.badges?.map(({itemId:m,iconUrl:c})=>({itemId:m,iconUrl:c})),o=e.boost&&{multipliers:e.boost.multipliers.map(({currencyId:m,currencySlug:c,multiplierBp:p})=>({currencyId:m,currencySlug:c,multiplierBp:p})),...e.boost.expiresInDays===void 0?{}:{expiresInDays:Math.min(Ao,e.boost.expiresInDays)}},a=e.discriminator!==void 0&&/^[0-9]{1,8}$/.test(e.discriminator)?e.discriminator:void 0,s=e.avatarIconId!==void 0&&sn.test(e.avatarIconId)?e.avatarIconId:void 0,l=e.avatar3dId!==void 0&&sn.test(e.avatar3dId)?e.avatar3dId:void 0,d;if(e.avatars3d!==void 0){let m=new Set;d=[];for(let{family:c,id:p,modelUrl:y,iconUrl:b}of e.avatars3d){if(d.length===Io)break;!sn.test(p)||!Po.test(c)||m.has(c)||y.length>Rr||b.length>Rr||(m.add(c),d.push({family:c,id:p,modelUrl:y,iconUrl:b}));}}this.send({gem:g,type:"gem.player",untrusted:{displayName:t,...r===void 0?{}:{displayTag:r},displayHandle:r??t},...e.playerId===void 0?{}:{playerId:e.playerId},...a===void 0?{}:{discriminator:a},...e.avatarIconUrl===void 0?{}:{avatarIconUrl:e.avatarIconUrl},...s===void 0?{}:{avatarIconId:s},...l===void 0?{}:{avatar3dId:l},...e.avatar3dModelUrl===void 0?{}:{avatar3dModelUrl:e.avatar3dModelUrl},...e.avatar3dIconUrl===void 0?{}:{avatar3dIconUrl:e.avatar3dIconUrl},...d===void 0?{}:{avatars3d:d},...i===void 0?{}:{badges:i},...o===void 0?{}:{boost:o},...typeof e.signInAvailable=="boolean"?{signInAvailable:e.signInAvailable}:{},displayTag:a??"",...e.avatarIconUrl===void 0?{}:{avatarUrl:e.avatarIconUrl}});}chainExpired(){return this.chainEndsAt!==null&&this.clock.now()/1e3>=this.chainEndsAt}localCorrelationId(){return `local-${++this.localDenials}`}sendPause(e){this.send({gem:g,type:"gem.pause",paused:e});}sendDevtools(e){this.send({gem:g,type:"gem.devtools",action:e});}requestScreenshot(e,t){return this.frameSupports("screenshot")?this.send({gem:g,type:"gem.screenshot",maxWidth:t.maxWidth,maxHeight:t.maxHeight},[e]):false}requestContext(){return this.frameSupports("feedbackContext")?this.send({gem:g,type:"gem.requestContext"}):false}sendPrefs(e){this.lastPrefs={...e},this.prefsCalls+=1;let{recommended:t}=e;if(t===void 0||!("textScale"in t)){this.send({gem:g,type:"gem.prefs",...e});return}let{textScale:r,...i}=t,o={gem:g,type:"gem.prefs",...e,recommended:i};if(!(this.frameSupports("textScale")&&typeof r=="number"&&Uo.includes(r))){this.send(o);return}let s={...o,recommended:{...i,textScale:r}};de["gem.prefs"]?.(s)?this.send(o):this.send(s);}sendCohorts(e){return this.frameSupports("cohorts")?this.send({gem:g,type:"gem.cohorts",cohorts:e.map(t=>({experimentId:t.experimentId,variantKey:t.variantKey}))}):false}sendEarnAllowanceChanged(){return this.frameSupports("earnAllowance")?this.send({gem:g,type:"gem.earnAllowanceChanged"}):false}sendViewport(e){if(!this.frameSupports("viewport"))return false;let t=(e.reserved??[]).map(({x:o,y:a,width:s,height:l,control:d})=>({x:o,y:a,width:s,height:l,...d?{control:{x:d.x,y:d.y,width:d.width,height:d.height}}:{}})),r=this.frameSupports("reservedAreas"),i=e.insets.top;if(!r)for(let o of t)o.y===0&&(i=Math.max(i,o.height));return this.send({gem:g,type:"gem.viewport",insets:{top:i,right:e.insets.right,bottom:e.insets.bottom,left:e.insets.left},...r?{reserved:t}:{}})}sendWillTerminate(e,t){this.send({gem:g,type:"gem.willTerminate",reason:e,graceMs:t});}close(){this.timeoutHandle!==null&&(this.clock.clearTimeout(this.timeoutHandle),this.timeoutHandle=null),this.inventoryHintTimer!==null&&(this.clock.clearTimeout(this.inventoryHintTimer),this.inventoryHintTimer=null),this.reofferHandle!==null&&(this.clock.clearTimeout(this.reofferHandle),this.reofferHandle=null);for(let e of this.offered)e.port.onmessage=null,e.port.close();this.offered.length=0,this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="unsupported"&&this.transition("closed");}transition(e){this.state!==e&&(this.state=e,this.options.onStateChange?.(e));}fail(e,t){this.options.onError?.(e,t);}send(e,t){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let r=e.type;if(!this.outbound.allow(r))return this.fail("rate_limited",`parent exceeded its own limit for ${r}`),false;let i=de[r]?.(e);return i?(this.fail("internal",`refusing to send malformed ${r}: ${i}`),false):(t===void 0?this.port.postMessage({...e,v:this.version}):this.port.postMessage({...e,v:this.version},t),true)}receive(e,t){if(this.state==="closed"||this.state==="unsupported")return;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string"){this.refuseMalformed("invalid_message","message has no type");return}let i=de[r];if(!i){this.refuseMalformed("unsupported",`unknown message type ${JSON.stringify(r.slice(0,Je))}`);return}if(r==="gem.controlsDeclare")return;if(!this.inbound.allow(r)){this.fail("rate_limited",`frame exceeded the limit for ${r}`);return}let o=he(r);if(o!==void 0&&!this.options.capabilities?.includes(o)){this.fail("unsupported_capability",`${r} requires the '${o}' capability, which this parent did not advertise`);return}let a=i(e);if(a){this.fail("invalid_message",`${r}: ${a}`);return}let s=e;r!=="gem.handshake.accept"&&!this.versionMatches(s,r)||this.dispatch(r,s,t);}refuseMalformed(e,t){if(!this.inbound.allow(xe)){this.options.onDropped?.(e,t);return}this.fail(e,t);}versionMatches(e,t){return e.v===this.version?true:(this.fail("invalid_message",`${t}: expected v ${String(this.version)}, got ${String(e.v)}`),false)}forwardSignal(e){let t=this.options.onSendSignal;if(t===void 0){this.fail("unsupported","this parent advertised `p2p` but wired no onSendSignal, so signaling has nowhere to go");return}if(this.gameId===null){this.options.onDropped?.("internal","gem.signal before any token was issued");return}if(e.signal!=="join"&&!this.knownMatches.has(e.matchId)){this.fail("unsupported",`gem.signal names match '${e.matchId}', which this player's socket has not mentioned`);return}if(!this.socketWindow.allow()){this.fail("rate_limited","signaling exceeded this parent per-second socket budget");return}let r=_t(e.signal,e.matchId,this.gameId,e.data);if("reason"in r){this.fail("invalid_message",`gem.signal: ${r.reason}`);return}this.withinFrameSize("gem.signal",r.frame,"Trickle ICE candidates rather than gathering them into the offer.")&&(this.rememberAsked(e.matchId),t(r.frame));}forwardDedicatedOffer(e){let t=this.options.onSendSignal;if(t===void 0){this.fail("unsupported","this parent advertised `dedicated` but wired no onSendSignal, so the offer has nowhere to go");return}if(this.gameId===null){this.options.onDropped?.("internal","gem.dedicatedOffer before any token was issued");return}if(!this.socketWindow.allow()){this.fail("rate_limited","signaling exceeded this parent per-second socket budget");return}let r=bt(e.matchId,this.gameId,{sdp:e.sdp,intent:e.intent});if("reason"in r){this.fail("invalid_message",`gem.dedicatedOffer: ${r.reason}`);return}if(this.withinFrameSize("gem.dedicatedOffer",r.frame,"A complete gathered offer fits; one that does not is carrying media sections this path does not permit, or candidates from ICE servers this path does not use.")){this.rememberAsked(e.matchId);try{t(r.frame);}catch(i){this.sendDedicatedEvent({event:"error",matchId:e.matchId,code:"embedder_relay_unavailable",message:"the page could not put the offer on the player socket"}),this.fail("internal",`gem.dedicatedOffer: the socket refused the send: ${No(i)}`);}}}withinFrameSize(e,t,r){let i=JSON.stringify(t).length;return i<=16384?true:(this.fail("invalid_message",`${e} builds a ${i}-byte socket frame, over the ${16384}-byte limit gem-api enforces, so the socket would drop it and then fail to establish. ${r}`),false)}dispatch(e,t,r){if(e==="gem.handshake.accept"){if(this.state!=="handshaking"){this.fail("handshake_duplicate","handshake already settled");return}let i=t.v;if(i<ne.min||i>ne.max){this.transition("unsupported"),this.fail("version_mismatch",`frame chose v${i}, outside our supported range`),this.close();return}this.adopt(r),this.version=i,this.peerCapabilities=t.capabilities??[],this.timeoutHandle!==null&&(this.clock.clearTimeout(this.timeoutHandle),this.timeoutHandle=null),this.reofferHandle!==null&&(this.clock.clearTimeout(this.reofferHandle),this.reofferHandle=null),this.transition("connected");return}if(this.state!=="connected"){this.fail("invalid_message",`${e} received before the handshake completed`);return}switch(e){case "gem.ready":this.options.onReady?.();break;case "gem.engaged":this.options.onEngaged?.();break;case "gem.progress":this.options.onProgress?.(t);break;case "gem.requestToken":{let i=t;if(this.chainExpired()){this.denyToken({requestId:i.requestId,retryable:false,reason:"chain_exhausted",correlationId:this.localCorrelationId()});break}this.options.onRequestToken?.(i);break}case "gem.requestLocalServer":{this.options.onRequestLocalServer?.(t);break}case "gem.exit":this.options.onExit?.(t),this.close();break;case "gem.openExternal":this.options.onOpenExternal?.(t);break;case "gem.requestFullscreen":this.options.onRequestFullscreen?.(t);break;case "gem.audioState":{let i=t,o=this.prefsCalls;this.options.onAudioState?.(i),this.prefsCalls===o&&this.lastPrefs!==null&&this.lastPrefs.muted!==i.muted&&this.sendPrefs({...this.lastPrefs,muted:i.muted});break}case "gem.rewardCue":this.options.onRewardCue?.(t);break;case "gem.walletDisplay":this.options.onWalletDisplay?.(t);break;case "gem.menu":this.options.onMenu?.(t);break;case "gem.gemCorner":this.options.onGemCorner?.(t);break;case "gem.rewardPresentation":this.options.onRewardPresentation?.(t);break;case "gem.menuStyle":this.options.onMenuStyle?.(t);break;case "gem.inviteParams":this.options.onInviteParams?.(t);break;case "gem.settingsDeclare":{let i=hr(t.settings);this.declaredSettings=i??[],this.options.onSettingsDeclare?.({gem:g,type:"gem.settingsDeclare",settings:this.declaredSettings.map(o=>({...o}))});break}case "gem.frameStats":this.options.onFrameStats?.(t);break;case "gem.portraitBorders":{let{name:i}=t;i==null?this.options.onPortraitBorders?.(null):tn(i)&&this.options.onPortraitBorders?.(i);break}case "gem.screenshot.denied":this.options.onScreenshotDenied?.(t);break;case "gem.gameContext":this.options.onGameContext?.(t);break;case "gem.signal":this.forwardSignal(t);break;case "gem.dedicatedOffer":this.forwardDedicatedOffer(t);break;case "gem.partyRequest":{let i=t;if(this.options.onPartyRequest===void 0){this.answerPartyRequest(i.requestId);break}this.options.onPartyRequest(i);break}case "gem.requestPurchase":{let i=t;if(!this.frameSupports("purchases")){this.fail("unsupported_capability","refusing gem.requestPurchase: the frame did not advertise 'purchases', so it cannot receive gem.purchaseResult. Nothing was prompted and nothing was spent.");break}if(this.options.onRequestPurchase===void 0){this.answerPurchase(i.requestId,"unavailable");break}this.options.onRequestPurchase(i);break}case "gem.requestAvatar3dPurchase":{let i=t;if(!this.frameSupports("avatar3dPurchases")||!this.frameSupports("purchases")){this.fail("unsupported_capability","refusing gem.requestAvatar3dPurchase: the frame did not advertise 'avatar3dPurchases' and 'purchases', so it cannot receive gem.purchaseResult. Nothing was prompted and nothing was spent.");break}if(this.options.onRequestAvatar3dPurchase===void 0){this.answerPurchase(i.requestId,"unavailable");break}this.options.onRequestAvatar3dPurchase(i);break}case "gem.deviceSettings.put":{let i=t;if(!this.frameSupports("deviceSettings")){this.fail("unsupported_capability","refusing gem.deviceSettings.put: the frame did not advertise 'deviceSettings', so it cannot receive gem.deviceSettings.result. Nothing was stored.");break}if(this.options.onDeviceSettingsPut===void 0){this.answerDeviceSettingsPut(i.requestId,"unavailable");break}this.options.onDeviceSettingsPut(i);break}case "gem.requestAd":{let i=t;if(!this.frameSupports("ads")){this.fail("unsupported_capability","refusing gem.requestAd: the frame did not advertise 'ads', so it cannot receive gem.adResult. Nothing was rendered.");break}if(this.options.onRequestAd===void 0){this.answerAd(i.requestId,"no_ad");break}this.options.onRequestAd(i);break}case "gem.signalMoment":{let i=t;if(!this.frameSupports("moments")){this.fail("unsupported_capability","refusing gem.signalMoment: the frame did not advertise 'moments', so it cannot receive gem.momentResult. Nothing was rendered.");break}if(this.options.onSignalMoment===void 0){this.answerMoment(i.requestId,false);break}this.options.onSignalMoment(i);break}case "gem.requestSignIn":{let i=t;if(!this.frameSupports("signIn")){this.fail("unsupported_capability","refusing gem.requestSignIn: the frame did not advertise 'signIn', so it cannot receive gem.signInResult. Nothing was rendered.");break}if(this.options.onRequestSignIn===void 0){this.answerSignIn(i.requestId,"unavailable");break}this.options.onRequestSignIn(i);break}case "gem.requestFriendChallenge":{let i=t;if(!this.frameSupports("friendChallenges")){this.fail("unsupported_capability","refusing gem.requestFriendChallenge: the frame did not advertise 'friendChallenges', so it cannot receive gem.friendChallengeResult. Nothing was previewed, rendered or sent.");break}if(this.options.onRequestFriendChallenge===void 0){this.answerFriendChallenge(i.requestId,"unavailable");break}this.options.onRequestFriendChallenge(i);break}case "gem.requestAppLinks":case "gem.requestOpenApp":case "gem.requestOpenGame":case "gem.requestSimilarGames":case "gem.requestStoreOffers":case "gem.requestStorePurchase":case "gem.requestStoreOfferSheet":{let i=he(e);if(!this.frameSupports(i)){this.fail("unsupported_capability",`refusing ${e}: the frame did not advertise '${i}', so it cannot receive the answer. Nothing was rendered.`);break}let[o,a]=Do[e],s=this.options[o];if(s===void 0){this.send({gem:g,type:a,requestId:t.requestId,outcome:"unavailable",...a==="gem.similarGamesResult"?{games:[]}:{},...a==="gem.storeOffersResult"?{offers:[]}:{}});break}s.call(this.options,t);break}case "gem.requestPlayInvite":{let i=t;if(!this.frameSupports("playInvites")){this.fail("unsupported_capability","refusing gem.requestPlayInvite: the frame did not advertise 'playInvites', so it cannot receive gem.playInviteResult. Nothing was rendered.");break}if(this.options.onRequestPlayInvite===void 0){this.answerPlayInvite(i.requestId,false);break}this.options.onRequestPlayInvite(i);break}case "gem.requestShare":if(!this.frameSupports("share")){this.fail("unsupported_capability","refusing gem.requestShare: the frame did not advertise 'share'. Nothing was shared.");break}this.options.onRequestShare?.(t);break;case "gem.requestAvatarPicker":if(!this.frameSupports("avatarPicker")){this.fail("unsupported_capability","refusing gem.requestAvatarPicker: the frame did not advertise 'avatarPicker'. Nothing was opened.");break}{let i=t.tab;this.options.onRequestAvatarPicker?.(i==="2d"||i==="3d"?{tab:i}:{});}break;case "gem.vibrate":{if(!this.frameSupports("vibration")){this.fail("unsupported_capability","refusing gem.vibrate: the frame did not advertise 'vibration'. Nothing was played.");break}let i=t,o=i.pattern.reduce((a,s)=>a+s,0);if(o>1e4){this.fail("invalid_message",`refusing gem.vibrate: the pattern totals ${o} ms, over ${1e4}. Nothing was played.`);break}this.options.onVibrate?.(i);break}case "gem.error":this.fail(t.code,t.message);break;default:this.fail("unsupported",`no parent handler for '${e}'`);}}};var Ir="[gem: use gem.text.toPlain()]",Ar=new Set,Pr;function Ho(n){try{if(Ar.has(n))return;let e=globalThis.location;if(Pr??=!!e?.port&&Te(e.hostname??""),!Pr)return;Ar.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Ir}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
2
|
-
${new Error().stack}`);}catch{}}var B=class{#e;#t;constructor(e,t="player text"){this.#e=e,this.#t=t,Object.freeze(this);}toString(){return Ho(this.#t),Ir}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function be(n,e){return new B(n,e)}var Bo={toPlain(n){return B.unwrap(n)},compare(n,e,t){let r=n instanceof B?B.unwrap(n):n,i=e instanceof B?B.unwrap(e):e;if(typeof Intl>"u"||typeof Intl.Collator!="function")return r<i?-1:r>i?1:0;let o;try{o=new Intl.Collator(t);}catch{o=new Intl.Collator;}return o.compare(r,i)},equals(n,e){let t=n instanceof B?B.unwrap(n):n,r=e instanceof B?B.unwrap(e):e;return t.normalize("NFC")===r.normalize("NFC")}};function Tr(n){return typeof n=="string"&&/^[\w-]{1,64}$/.test(n)}function Er(n){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function Vo(n,e){if(n!==true)return {visible:false};let t=[],r=false;for(let i of Array.isArray(e)?e:[])i==="score"?r=true:typeof i=="string"&&i.length>0&&i.length<=64&&!t.includes(i)&&t.push(i);return !r&&t.length===0?{visible:true}:{visible:true,currencies:t.slice(0,8),score:r}}function Ko(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:i}=t;typeof r!="string"||r.length===0||r.length>32||typeof i!="string"||i.trim().length===0||e.some(o=>o.id===r)||e.push({id:r,label:i.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function Cr(n,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function Jo(n){let e=n.untrusted,t=be(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,i=r?be(r,"gem.player.displayTag"):void 0,o=n.discriminator||n.displayTag,a=o&&/^[0-9]{1,8}$/.test(o)?o:void 0,s=n.avatarIconUrl||n.avatarUrl,l=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,d=n.avatarIconId,m=n.avatar3dId,c={displayName:t,displayHandle:i??t,badges:(n.badges??[]).map(v=>({itemId:v.itemId,iconUrl:v.iconUrl}))};n.playerId&&(c.playerId=n.playerId),a&&(c.discriminator=a),i&&(c.displayTag=i),s&&(c.avatarIconUrl=c.avatarUrl=s),d&&l.test(d)&&(c.avatarIconId=d),m&&l.test(m)&&(c.avatar3dId=m),n.avatar3dModelUrl&&(c.avatar3dModelUrl=n.avatar3dModelUrl),n.avatar3dIconUrl&&(c.avatar3dIconUrl=n.avatar3dIconUrl),n.avatars3d&&(c.avatars3d=n.avatars3d.filter(v=>l.test(v.id)&&/^[a-z][a-z0-9_]*$/.test(v.family)).filter((v,A,M)=>M.findIndex(E=>E.family==v.family)==A).map(({family:v,id:A,modelUrl:M,iconUrl:E})=>({family:v,id:A,modelUrl:M,iconUrl:E})));let p=n.boost,y=(p?.multipliers??[]).map(v=>({currencyId:v.currencyId,currencySlug:v.currencySlug,multiplier:v.multiplierBp/1e4})),b=y.some(v=>v.multiplier>1);return c.boost=b&&p?.expiresInDays?{active:b,multipliers:y,expiresInDays:p.expiresInDays}:{active:b,multipliers:y},c}var zo=1e4,Xo=["appLinks","openGame","store","storeOfferSheet"],Yo=["token","pause","menuSelect","settingChange","prefs","cohorts","viewport","willTerminate","localServer","roomEvent","seatedEvent","autoStartFailedEvent","ticketEvent","socketState","signalEvent","relayDraining","dedicatedEvent","clusterTarget","graphicsRoutes","partyResult","purchaseResult","purchaseSettled","inventoryChanged","shareResult","deviceSettings","adResult","momentResult","signInResult","friendChallengeResult","playInviteResult"],Qo=new WeakMap;var cn=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??ce,this.inbound=new oe(this.clock),this.outbound=new oe(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(s=>ye(s,e.origin))||vr(e.origin,this.options.selfOrigin)||(Qo.get(this)??[]).some(s=>ye(s,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=de["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let i=e.data;if(!ye(i.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${i.embedder}' but arrived from '${e.origin}'`),false;let o=e.ports?.[0];if(!o)return this.refuse(e,"handshake transferred no MessagePort"),false;let a=on(ne,i.versions);return a===null?(o.onmessage=null,o.postMessage({gem:g,type:"gem.error",code:"version_mismatch",message:`frame supports v${ne.min}..v${ne.max}`}),o.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=o,this.version=a,o.onmessage=s=>this.receive(s.data,s.ports??[]),o.start?.(),this.peerCapabilities=i.capabilities??[],this.unbidden=i.unbidden===true,o.postMessage({gem:g,type:"gem.handshake.accept",v:a,...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:g,type:"gem.ready"});}settled(e=zo){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(t=>{let r=o=>{this.clock.clearTimeout(i),t(o);},i=this.clock.setTimeout(()=>{let o=this.settleWaiters.indexOf(r);o>=0&&this.settleWaiters.splice(o,1),t(this.state);},e);this.settleWaiters.push(r);})}progress(e,t){this.send({gem:g,type:"gem.progress",loaded:e,total:t});}requestToken(e,t){this.send({gem:g,type:"gem.requestToken",requestId:e,reason:t});}requestLocalServer(e,t){return this.parentSupports("localHost")?(this.send({gem:g,type:"gem.requestLocalServer",requestId:e,gameRoomId:t}),true):false}exit(e){this.send(e===void 0?{gem:g,type:"gem.exit"}:{gem:g,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:g,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:g,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:g,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:g,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let t={gem:g,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return Tr(e.cueId)&&(t.cueId=e.cueId),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:Er(e.origin.x),y:Er(e.origin.y)}),this.send(t),true}showPortraitBorders(e){let t=dr(e);return t===void 0||!this.gated("portraitBorders")?false:this.send(t===null?{gem:g,type:"gem.portraitBorders"}:{gem:g,type:"gem.portraitBorders",name:t})}setWalletVisible(e,t){return this.gated("walletDisplay")?this.send({gem:g,type:"gem.walletDisplay",...Vo(e,t?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:g,type:"gem.menu",...Ko(e)}):false}declareSettings(e){return this.gated("gameSettings")?this.send({gem:g,type:"gem.settingsDeclare",settings:e}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:g,type:"gem.gemCorner",corner:e}):false}setRewardPresentation(e){return this.gated("rewardPresentation")?this.send({gem:g,type:"gem.rewardPresentation",mode:e}):false}setMenuStyle(e){return this.gated("menuStyle")?this.send(e===null?{gem:g,type:"gem.menuStyle"}:{gem:g,type:"gem.menuStyle",ground:e.ground,accent:e.accent}):false}requestShare(e,t){return this.gated("share")?this.send({gem:g,type:"gem.requestShare",...t===void 0?{}:{requestId:t},params:e.map(({key:r,value:i})=>({key:r,value:i}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:g,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:g,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:g,type:"gem.frameStats",fps:Cr(e.fps,1e3),frameTimeP95Ms:Cr(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:g,type:"gem.gameContext",context:e}),true)}signal(e,t,r){this.send({gem:g,type:"gem.signal",signal:e,matchId:t,data:r});}dedicatedOffer(e,t,r){this.send({gem:g,type:"gem.dedicatedOffer",matchId:e,sdp:t,intent:r});}requestParty(e){return this.send({gem:g,type:"gem.partyRequest",requestId:e})}requestPurchase(e,t,r,i,o,a,s){return this.send({gem:g,type:"gem.requestPurchase",requestId:e,purchaseKind:t,targetId:r,currencySlug:i,...o===void 0?{}:{quantity:o},...a===void 0?{}:{catalogId:a},...s===void 0?{}:{origin:s}})}requestAvatar3dPurchase(e,t,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:g,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:t,currencySlug:r}):false}putDeviceSettings(e,t){return this.send({gem:g,type:"gem.deviceSettings.put",requestId:e,settings:t})}requestAd(e,t,r){return this.send({gem:g,type:"gem.requestAd",requestId:e,placement:t,format:r})}signalMoment(e,t,r,i){return this.send({gem:g,type:"gem.signalMoment",requestId:e,kind:t,...r===void 0?{}:{placement:r},...i===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:g,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,t,r,i){return this.send({gem:g,type:"gem.requestFriendChallenge",requestId:e,playerId:t,template:r,...i===void 0?{}:{params:i}})}requestArcade(e){return this.gated(he(e.type))&&this.send({gem:g,...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(xe)){this.options.onDropped?.(e,t);return}this.fail(e,t);}dropUnknownType(e){this.inbound.allow(xe)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,t){e.ports?.[0]?.close?.(),this.inbound.allow(xe)&&this.options.onRefused?.(t);}fail(e,t){this.options.onError?.(e,t),this.state==="connected"&&this.port&&this.send({gem:g,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=de[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||Mr(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 i=de[r];if(!i)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let o=he(r);if(o!==void 0&&!this.options.capabilities?.includes(o))return this.fail("unsupported_capability",`${r} requires the '${o}' capability, which this frame did not advertise`),false;let a=i(e);if(a)return this.fail("invalid_message",`${r}: ${a}`),false;let s=e;if(s.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.devtools":this.options.onDevtools?.(s.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let l=t[0];if(!l){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let d={maxWidth:s.maxWidth,maxHeight:s.maxHeight};if(!this.options.onScreenshot){l.close(),this.screenshotDenied("not_wired",false);break}return Mr(t.slice(1)),this.options.onScreenshot(d,l,(m,c)=>this.screenshotDenied(m,c)),true}case "gem.token.denied":this.options.onTokenDenied?.(s);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(s);break;case "gem.player":{let l=s;this.options.onPlayer?.(Jo(l),l.signInAvailable);break}case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(s);break;case "gem.error":this.options.onError?.(s.code,s.message);break;default:if(Yo.includes(r.slice(4))){this.options[`on${r[4].toUpperCase()}${r.slice(5)}`]?.(s);break}if(Xo.includes(he(r))){this.options.onArcadeAnswer?.(s);break}this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,t){this.send({gem:g,type:"gem.screenshot.denied",reason:e,retryable:t});}};function Mr(n){for(let e of n)try{e?.close?.();}catch{}}var Zo=16384;function es(n){if(typeof n!="object"||n===null||Array.isArray(n))return null;let e;try{e=JSON.stringify(n);}catch{return null}return typeof e!="string"?null:ts(e)}function ts(n){let e=0;for(let t=0;t<n.length;t+=1){let r=n.charCodeAt(t);if(r<128)e+=1;else if(r<2048)e+=2;else if(r>=55296&&r<=56319){let i=n.charCodeAt(t+1);i>=56320&&i<=57343?(e+=4,t+=1):e+=3;}else e+=3;}return e}var Gr=["p2p","dedicated","unknown"],wt=["connection_setup_duration_seconds","reconnect_latency_seconds","datachannel_rtt_seconds","p2p_join_to_play_latency_seconds","ice_restart_outcome_total","connection_failures_total","transport_permanent_failures_total","transport_quality_score","host_migration_outcome_total","turn_relay_usage_ratio"];var ve=class{constructor(e){this.options=e;}options;async post(e,t){let r=this.options.gameId();if(r===null||r==="")return;let i=ns(e,this.options.transport,(this.options.now??Date.now)());i.length!==0&&await this.options.client.post(`/v1/games/${encodeURIComponent(r)}/telemetry/p2p`,{events:i},t);}report(e){this.post(e).catch(()=>{});}};function ns(n,e,t){let r=Math.floor(t/1e3);return n.filter(i=>wt.includes(i.type)).slice(0,100).map(i=>{let o=is(i.transport??e);return {event_type:i.type,timestamp:r,...o===void 0?{}:{transport:o},data:{...rs(i.labels),...i.value===void 0?{}:{value:i.value}}}})}function rs(n){let e={};if(typeof n!="object"||n===null)return e;let t=0;for(let[r,i]of Object.entries(n)){if(t>=4)break;r==="value"||!Or(r)||!Or(i)||(e[r]=i,t+=1);}return e}function Or(n){return typeof n=="string"&&n.length>0&&n.length<=32}function is(n){if(n!==void 0)return Gr.includes(n)?n:"unknown"}function un(n){let e=atob(n),t=new Uint8Array(e.length);for(let r=0;r<e.length;r+=1)t[r]=e.charCodeAt(r);return t}var q=class extends Error{constructor(t,r,i,o,a){super(`${t} (${r}): ${i}`);this.reason=t;this.status=r;this.detail=i;this.retryAfterSeconds=o;this.code=a;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}};var ss=new Set(["timeout","credential_expired","credential_reused","join_rate_limited","signaling_failed","connection_failed","no_server_ready","start_rate_limited"]),u=class extends Error{constructor(t,r,i){super(`${t}: ${r}`,i);this.reason=t;this.name="DedicatedServerError",this.retryable=ss.has(t),this.platformCode=i?.platformCode;}reason;retryable;platformCode};var Lr=["explicit","stale_cleanup","game_room_closed","replaced","migration_failed","host_left","insufficient_players","all_left","expired","migration_rate_exceeded","game_room_gone","failed","idle","reclaimed"],as=new Set(Lr);function Ce(n){return typeof n=="string"&&as.has(n)?n:"unknown"}var ze="webrtc_dedicated",mn=["universal","mobile","web"];var ls="p2p_unavailable",ds="capacity_unavailable",cs=2,Dr=3e4,qr=1e3,$r=2e4;async function kt(n){let e=H(n.clusterTargetId),t=n.nextClusterTarget,r=0,i=n.clock??ce,o=n.retry??{},a=o.budgetMs??Dr,s=o.maxDelayMs??$r,l=o.initialDelayMs??qr,d=i.now(),m=d+a,c=0;for(;;){c++;try{let p=await hn(n.signal,"while allocating",()=>n.client.post(`${jr(n)}/start_match`,e===void 0?{}:{cluster_target_id:e},n.signal));return {matchId:p.match_id,status:p.status}}catch(p){if(!us(p)||a<=0)throw p;if(p.status===503&&p.code===ls)throw new u("p2p_unavailable",`room ${n.roomId} starts a peer-to-peer match, which allocates no server \u2014 so this refusal is not capacity and waiting cannot help: ${p.message}`,{cause:p});if(r=p.code===ds?r+1:0,t!==void 0&&r>=cs){let M=t;t=void 0;let E=H(await M());E!==void 0&&(e=E,r=0);}let y=Math.min(s,l*2**(c-1)),b=Math.min(s,(p.retryAfterSeconds??0)*1e3),A=Math.max(y/2,b)+Math.random()*(y/2);if(i.now()+A>m)throw ps(n,p,c,i.now()-d,a);if(o.onWaiting?.({attempt:c,elapsedMs:i.now()-d,budgetMs:a,nextAttemptInMs:A,status:p.status,message:p.message}),await At(i,A,n.signal),n.signal?.aborted)throw pn("while waiting to allocate")}}}function us(n){return n instanceof q?n.status===503?true:n.status===429&&n.code!=="too_many_concurrent_matches":false}function ps(n,e,t,r,i){let o=`${t} attempt${t===1?"":"s"} over ${r}ms of a ${i}ms budget`;return e.status===429?new u("start_rate_limited",`the platform was still limiting match starts for room ${n.roomId} after ${o}: ${e.message}`,{cause:e}):new u("no_server_ready",`the platform had no server ready for room ${n.roomId} after ${o}: ${e.message}`,{cause:e})}var ms=12e4,gs=1e3;async function St(n){let{clock:e,matchId:t}=n,r=n.pollIntervalMs??gs,i=n.timeoutMs??ms,o=e.now()+i,a=Fr(n),s={seen:n.knownToExist===true};for(;;){let l=await Nr(n,a,s);if(l.outcome!==null)return l.outcome;if(e.now()+r>o)throw new u("timeout",`match ${t} was still ${l.status} after ${i}ms`);await At(e,r,n.signal);}}async function gn(n){return (await Nr(n,Fr(n),{seen:n.knownToExist===true})).outcome}function Fr(n){let e=new URLSearchParams({platform:n.version.platform,game_version:n.version.gameVersion});return `${jr(n)}/matches/${encodeURIComponent(n.matchId)}/status?${e}`}async function Nr(n,e,t){let{matchId:r}=n,i=n.clock.now(),o;try{o=await hn(n.signal,"while polling match status",()=>n.client.get(e,n.signal));}catch(a){if(a instanceof q&&a.reason==="not_found")return {status:"not_found",outcome:t.seen?{status:"ended",matchId:r,reason:"unknown"}:{status:"failed",matchId:r,reason:"not_found"}};throw a}if(t.seen=true,o.status==="ready")return {status:"ready",outcome:hs(r,o,i)};if(o.status==="failed"){let a=o.ended_reason;return typeof a=="string"?{status:"failed",outcome:{status:"ended",matchId:r,reason:Ce(a)}}:{status:"failed",outcome:{status:"failed",matchId:r,reason:o.failure_reason??"unknown"}}}return {status:o.status,outcome:null}}async function xt(n){let e=await hn(n.signal,"while listing cluster targets",()=>n.client.get("/v1/ping/targets",n.signal)),t=[];for(let r of e.targets??[]){let i=r?.cluster_target_id;typeof i!="string"||i.length===0||t.push({id:i,pressure:typeof r.pressure=="string"?r.pressure:"unknown"});}return t}async function hn(n,e,t){if(n?.aborted)throw pn(e);try{return await t()}catch(r){throw n?.aborted?pn(e,r):r}}function pn(n,e){return new u("aborted",`the caller aborted ${n}`,e===void 0?void 0:{cause:e})}function hs(n,e,t){let r=e.join_credential,i=e.expires_in_seconds,o=typeof r=="string"&&typeof i=="number";return {status:"ready",matchId:n,protocol:e.protocol??"unknown",...o?{joinCredential:r,credentialExpiresAt:t+i*1e3}:{},...e.host===void 0?{}:{host:e.host},...e.port===void 0?{}:{port:e.port}}}function jr(n){return `/v1/games/${encodeURIComponent(n.gameId)}/game-rooms/${encodeURIComponent(n.roomId)}`}function At(n,e,t){return t?.aborted?Promise.resolve():new Promise(r=>{let i=()=>{n.clearTimeout(o),t?.removeEventListener("abort",i),r();},o=n.setTimeout(i,e);t?.addEventListener("abort",i);})}var fs=new Set(["invalid_sdp","age_restricted","match_not_found","not_match_member","account_banned"]),yn="embedder_relay_unavailable",ys="embedder_no_relay",bs=new Set([yn]),bn=new Set([ys]),Pt=class{constructor(e){this.options=e;}options;pending=new Map;channel(){return new fn(this)}handleEvent(e){let t=this.pending.get(e.matchId);if(t!==void 0){if(e.event==="matchEnded"){t.refuse(new u("match_ended",`the match ended while the offer was in flight (${e.reason??"no reason given"}), so no server is left to answer it`));return}if(e.event==="answer"){if(typeof e.sdp!="string"||e.sdp.length===0){t.refuse(new u("signaling_failed","the parent relayed an answer with no SDP in it"));return}t.settle({sdp:e.sdp});return}t.refuse(vs(e.code,e.message));}}register(e,t){this.pending.get(e)?.refuse(new u("signaling_failed","a newer offer for this match superseded the one in flight")),this.pending.set(e,t);}release(e,t){this.pending.get(e)===t&&this.pending.delete(e);}transport(){let e=this.options.bridge;if(e===null)throw new u("signaling_unavailable","this game is not running inside a page that holds the player's notification socket, so there is nothing to carry an offer. Dedicated signaling needs that socket and only the page can hold one \u2014 see docs/dedicated-server-flow.md \xA72.");if(!e.parentSupports("dedicated"))throw new u("signaling_unavailable","the page embedding this game did not advertise the `dedicated` capability, so it will not relay signaling for a dedicated server. See docs/dedicated-server-flow.md \xA72.");return e}};function vs(n,e){let t=e??"the offer was refused",r=n===void 0?t:`${t} (${n})`;return new u(_s(n),r,{platformCode:n})}function _s(n){return n===void 0||bs.has(n)?"signaling_failed":bn.has(n)?"offer_refused":fs.has(n)?n==="invalid_sdp"?"invalid_sdp":"offer_refused":"signaling_failed"}var fn=class{constructor(e){this.hub=e;}hub;closed=false;teardown=null;exchange(e,t){if(this.closed)return Promise.reject(new u("signaling_unavailable","this signaling channel is closed"));let r;try{r=this.hub.transport();}catch(i){return Promise.reject(i)}return t?.aborted?Promise.reject(new u("aborted","the caller aborted before the offer was sent")):new Promise((i,o)=>{let a=()=>{s(),o(new u("aborted","the caller aborted the signaling exchange"));},s=()=>{t?.removeEventListener("abort",a),this.hub.release(e.matchId,l),this.teardown===s&&(this.teardown=null);},l={settle:d=>{s(),i(d);},refuse:d=>{s(),o(d);}};this.teardown=s,t?.addEventListener("abort",a),this.hub.register(e.matchId,l);try{r.dedicatedOffer(e.matchId,e.sdp,e.intent);}catch(d){l.refuse(d instanceof u?d:new u("signaling_failed",d instanceof Error?d.message:String(d),{cause:d}));}})}close(){this.closed=true,this.teardown?.(),this.teardown=null;}};var Ur="match_ended:",Wr="server_shutdown:",Hr="kicked:",Rs=123;function ws(n){if(typeof n!="string"||n.length===0||n.length>Rs)return {kind:"other"};if(n.startsWith(Ur)){let e=Ce(n.slice(Ur.length));return e==="unknown"?{kind:"other"}:{kind:"match_ended",reason:e}}return n.startsWith(Wr)?{kind:"server_shutdown",cause:n.slice(Wr.length)}:n.startsWith(Hr)?{kind:"kicked",detail:n.slice(Hr.length)}:{kind:"other"}}function vn(n,e,t){let r=ws(t),i=r.kind==="kicked"?"kicked":"link_closed";n.notifyMatchDisconnected(i).catch(()=>{}),r.kind==="match_ended"&&n.reconcile({matchId:e,reason:r.reason}).catch(()=>{});}var ks="join",Ss="welcome",xs="rejected",_n=25e3,As=1e3,Rn=1;function wn(n){return typeof n=="number"&&Number.isFinite(n)&&n>0?n:void 0}var Ps={credential_expired:"credential_expired",credential_reused:"credential_reused",credential_timeout:"timeout",join_rate_limited:"join_rate_limited",expected_join:"join_refused",invalid_credential:"join_refused",credential_not_for_this_channel:"join_refused"};function kn(n){let e=typeof n=="string"?n:"unknown";return new u(Ps[e]??"join_refused",`the server runtime rejected the join credential: ${e}`)}function Xe(n){let{channel:e,clock:t}=n;return new Promise((r,i)=>{let o=false,a=Math.min(n.credentialExpiresAt-t.now(),n.ceilingMs??_n),s=t.setTimeout(()=>v(new u("timeout","the server runtime sent no welcome before the join credential expired. The channel was open, so this is the runtime not answering rather than a connection that never came up.")),Math.max(a,As)),l=A=>{let M=A.data;if(typeof M!="string"){c(M);return}let E;try{E=JSON.parse(M);}catch{c(M);return}if(o){c(M);return}if(E.t===Ss){let f=wn(E.heartbeat_interval);b({...typeof E.player_id=="string"?{playerId:E.player_id}:{},...typeof E.match_id=="string"?{matchId:E.match_id}:{},...f===void 0?{}:{heartbeatIntervalMs:f}});return}if(E.t===xs){v(kn(E.reason));return}c(M);},d=()=>{v(new u("connection_failed","the channel closed before the join/welcome exchange completed, so there is nothing left to present the credential on."));},m=()=>{v(new u("aborted","the caller aborted during the join exchange"));},c=A=>{o&&n.onMessage?.(A);},p=()=>{t.clearTimeout(s),e.removeEventListener("close",d),n.signal?.removeEventListener("abort",m);},y=()=>{e.removeEventListener("message",l);},b=A=>{o=true,p(),n.onMessage===void 0&&y(),r({welcome:A,detach:y});},v=A=>{o||(o=true,p(),y(),i(A));};if(e.addEventListener("message",l),e.addEventListener("close",d),n.signal?.aborted){m();return}n.signal?.addEventListener("abort",m);try{e.send(JSON.stringify({t:ks,credential:n.credential,...n.wire===void 0?{}:{wire:n.wire},...n.heartbeat===!0?{heartbeat:Rn}:{}}));}catch(A){v(new u("connection_failed",`the join frame could not be sent on the open channel: ${String(A)}`,{cause:A}));}})}function It(){return {exchange(){return Promise.reject(new u("signaling_unavailable","no signaling channel is wired. The exchange rides the notification WebSocket, and POST /v1/ws/ticket accepts only account credentials \u2014 a game frame holds a game-scoped token and cannot mint one \u2014 so it has to be relayed by the page embedding this game. Run inside a parent that advertises `dedicated`, or pass your own `signaling` implementation. See docs/dedicated-server-flow.md \xA72."))},close(){}}}var _e=1,Ye=2,Sn=4,Pn=8,Is=2,Ts=1e3,In="heartbeat timeout",Tt="gem-uu",Et=2,Vr="gem_transports";function Qe(n){if(!Array.isArray(n))return null;let e=[];for(let t of n){if(typeof t!="string")continue;let r=t.toLowerCase();!Es(r)||e.includes(r)||e.push(r);}return e}function Es(n){return n==="webtransport"||n==="webrtc"||n==="websocket"}function Ct(n,e){if(typeof n!="object"||n===null)return null;let t=n[Vr];if(typeof t!="object"||t===null)return null;let r=t;return typeof r.v!="number"||r.v<1||typeof r.host!="string"||typeof r.port!="number"||e!==void 0&&r.match_id!==e?null:r}function Mt(n){return n instanceof Error?n.message===""?n.name:`${n.name}: ${n.message}`:String(n)}var Kr=new TextEncoder,Jr=new TextDecoder;function Tn(n,e){let t=new Uint8Array(e.byteLength+1);return t[0]=n,t.set(e,1),t}function En(n){let e=0;return n?.reliable===false&&(e|=_e),n?.ordered===false&&(e|=Ye),e}function Cn(n){return {reliable:(n&_e)===0,ordered:(n&Ye)===0}}var Mn={reliable:true,ordered:true};function zr(n){return JSON.stringify({t:"join",credential:n,wire:Et,heartbeat:Rn})}function Xr(n){let e;try{e=JSON.parse(n);}catch{throw new u("join_refused","the server answered the join with something unreadable")}if(e.t==="welcome"){let t=wn(e.heartbeat_interval);return {...e.player_id===void 0?{}:{playerId:e.player_id},...e.match_id===void 0?{}:{matchId:e.match_id},...t===void 0?{}:{heartbeatIntervalMs:t}}}throw e.t==="rejected"?kn(e.reason):new u("join_refused","the server answered the join out of protocol")}function On(){return typeof globalThis.WebTransport=="function"&&Gn===null}function Ot(){return Gn}var Gn=null;var Br="webtransport has no datagram lane; refusing the rung \u2014 server-to-client unreliable traffic would be silently lost";function Cs(n){let e=un(n);if(e.byteLength!==32)throw new u("misconfigured",`the advertised certificate hash decodes to ${e.byteLength} bytes; a SHA-256 is 32`);return e}var xn=class{buffered=new Uint8Array(0);push(e){let t=new Uint8Array(this.buffered.byteLength+e.byteLength);t.set(this.buffered,0),t.set(e,this.buffered.byteLength),this.buffered=t;let r=[];for(;!(this.buffered.byteLength<4);){let o=new DataView(this.buffered.buffer,this.buffered.byteOffset,4).getUint32(0,true);if(o===0||o>1<<20)throw new u("connection_failed",`a control-stream record claims ${o} bytes`);if(this.buffered.byteLength<4+o)break;let a=this.buffered.slice(4,4+o);this.buffered=this.buffered.slice(4+o),r.push({flags:a[0]??0,payload:a.slice(1)});}return r}};function An(n,e){let t=new Uint8Array(5+e.byteLength);return new DataView(t.buffer).setUint32(0,e.byteLength+1,true),t[4]=n,t.set(e,5),t}async function Ze(n){let e=Math.min(n.timeoutMs??1e4,Math.max(n.credentialExpiresAt-n.clock.now(),1e3)),t=n.webTransport;if(t===void 0){let s=globalThis.WebTransport;if(s===void 0)throw new u("connection_failed","this environment has no WebTransport");t=n.certHashBase64===void 0?new s(n.url):new s(n.url,{serverCertificateHashes:[{algorithm:"sha-256",value:Cs(n.certHashBase64)}]});}let r=t,i=s=>{try{r.close();}catch{}throw s},o=r.datagrams;(o?.readable===void 0||o.writable===void 0)&&(Gn=Br,i(new u("connection_failed",`${Br}; the rung is skipped for the rest of this page (a reload probes again)`)));let a=new Promise((s,l)=>{setTimeout(()=>l(new u("timeout","webtransport did not come up in time")),e);});try{await Promise.race([r.ready,a]);let s=r.incomingBidirectionalStreams.getReader(),l=await Promise.race([s.read(),a]);if(s.releaseLock(),l.done||l.value===void 0)return i(new u("connection_failed","the session ended before the control stream arrived"));let d=l.value,m=d.writable.getWriter();await m.write(An(Sn,Kr.encode(zr(n.credential))));let c=new xn,p=d.readable.getReader(),y;for(;y===void 0;){let b=await Promise.race([p.read(),a]);if(b.done||b.value===void 0)return i(new u("connection_failed","the control stream closed before the join was answered"));for(let v of c.push(b.value)){y=Xr(Jr.decode(v.payload));break}}return {link:Ms(r,m,p,c,n.log??(b=>console.warn(b)),Qr(y,n.clock)),welcome:y}}catch(s){return i(s)}}function Ms(n,e,t,r,i,o){let a,s,l=false,d=b=>{if(!l){l=true,m.stop();try{n.close();}catch{}s?.(b);}},m=Dn(o,()=>d(In)),c=(b,v)=>{m.heard(),(b&Pn)===0&&a!==void 0&&((b&Sn)!==0?a(Jr.decode(v),Mn):a(v,Cn(b)));};(async()=>{try{for(;;){let b=await t.read();if(b.done||b.value===void 0)break;for(let v of r.push(b.value))c(v.flags,v.payload);}}catch{}d("stream closed");})();let p,y;try{let b=n.datagrams;if(b?.readable!==void 0&&b.writable!==void 0){let v=b.readable.getReader();y=b.writable.getWriter(),p=b,(async()=>{try{for(;;){let A=await v.read();if(A.done||A.value===void 0)break;let M=A.value;M.byteLength!==0&&c((M[0]??0)|_e|Ye,M.slice(1));}}catch{}})();}else i("[gem] webtransport link has no datagram lane; unreliable sends ride the stream");}catch(b){p=void 0,y=void 0,i(`[gem] webtransport datagram lane unusable (${Mt(b)}); unreliable sends ride the stream`);}return n.closed.then(b=>d(Yr(b?.reason)??"session closed"),()=>d("session failed")),{transport:"webtransport",send(b,v){if(l)return;if(typeof b=="string"){e.write(An(Sn,Kr.encode(b))).catch(()=>d("write failed"));return}let A=En(v);if((A&_e)!==0&&y!==void 0){let M=Tn(A,b),E=p?.maxDatagramSize;if(E===void 0||M.byteLength<=E){y.write(M).catch(()=>{});return}}e.write(An(A,b)).catch(()=>d("write failed"));},onMessage(b){a=b;},onClose(b){s=b;},close(){d("closed");}}}function Ln(n){let e=Math.min(n.timeoutMs??1e4,Math.max(n.credentialExpiresAt-n.clock.now(),1e3)),t=n.webSocket;if(t===void 0){let i=globalThis.WebSocket;if(i===void 0)return Promise.reject(new u("connection_failed","this environment has no WebSocket"));t=new i(n.url);}let r=t;return r.binaryType="arraybuffer",new Promise((i,o)=>{let a=false,s=setTimeout(()=>{a||(a=true,r.close(),o(new u("timeout","websocket did not come up in time")));},e),l=y=>{a||(a=true,clearTimeout(s),r.close(),o(y));},d=()=>{r.send(zr(n.credential));},m=y=>{if(a)return;if(typeof y.data!="string"){l(new u("join_refused","the server sent binary before answering the join"));return}let b;try{b=Xr(y.data);}catch(v){l(v);return}a=true,clearTimeout(s),r.removeEventListener("open",d),r.removeEventListener("message",m),r.removeEventListener("close",c),r.removeEventListener("error",p),i({link:Os(r,Qr(b,n.clock)),welcome:b});},c=()=>{l(new u("connection_failed","the websocket closed before the join was answered"));},p=()=>{l(new u("connection_failed","the websocket failed before the join was answered"));};r.addEventListener("open",d),r.addEventListener("message",m),r.addEventListener("close",c),r.addEventListener("error",p),r.readyState===1&&d();})}function Yr(n){return typeof n=="string"&&n.length>0?n:void 0}function Qr(n,e){return n.heartbeatIntervalMs===void 0?void 0:{clock:e,intervalMs:n.heartbeatIntervalMs}}function Dn(n,e){if(n===void 0)return {heard:()=>{},stop:()=>{}};let{clock:t}=n,r=Math.max(n.intervalMs,Ts)*Is,i=t.now(),o=null,a=()=>{o=null;let s=t.now()-i;if(s>=r){e();return}o=t.setTimeout(a,r-s);};return o=t.setTimeout(a,r),{heard:()=>{i=t.now();},stop:()=>{o!==null&&(t.clearTimeout(o),o=null);}}}function Os(n,e){let t,r,i=false,o=s=>{if(!i){i=true,a.stop();try{n.close();}catch{}r?.(s);}},a=Dn(e,()=>o(In));return n.addEventListener("message",(s=>{if(a.heard(),typeof s.data=="string"){t?.(s.data,Mn);return}let l=new Uint8Array(s.data);if(l.byteLength===0)return;let d=l[0]??0;(d&Pn)===0&&t?.(l.slice(1),Cn(d));})),n.addEventListener("close",(s=>o(Yr(s?.reason)??"socket closed"))),{transport:"websocket",send(s,l){if(!i){if(typeof s=="string"){n.send(s);return}n.send(Tn(En(l),s));}},onMessage(s){t=s;},onClose(s){r=s;},close(){o("closed");}}}function qn(n,e,t,r){let i,o,a=false,s=t,l=c=>{if(!a){a=true,d.stop();try{n.close();}catch{}try{e.close();}catch{}o?.(c);}},d=Dn(r,()=>l(In)),m=(c,p)=>{if(d.heard(),typeof c=="string"){i?.(c,Mn);return}let y=new Uint8Array(c);if(y.byteLength===0)return;let b=y[0]??0;(b&Pn)===0&&i?.(y.slice(1),Cn(b|p));};if(n.addEventListener("message",(c=>m(c.data,0))),n.addEventListener("close",(()=>l("channel closed"))),e.addEventListener("connectionstatechange",(()=>{let c=e.connectionState;(c==="failed"||c==="closed")&&l(`connection ${c}`);})),s!==void 0){let c=s;c.addEventListener("message",(p=>m(p.data,_e|Ye))),c.addEventListener("close",(()=>{s=void 0;}));}return {transport:"webrtc",send(c,p){if(a)return;if(typeof c=="string"){n.send(c);return}let y=En(p),b=Tn(y,c);if((y&_e)!==0&&s!==void 0&&s.readyState==="open"){s.send(b);return}n.send(b);},onMessage(c){i=c;},onClose(c){o=c;},close(){l("closed");}}}var Zr=10240,ei=2048,Gs=new TextEncoder;function ti(n){return Gs.encode(n).length}function se(n,e){let t=i=>{throw new u("invalid_sdp",`the ${e} ${i}`)},r=ti(n);r>=Zr&&t(`is ${r} bytes, over the ${Zr} limit`+(e==="offer"?", which for an offer means an unusually wide candidate list":"")),n.startsWith("v=")||t("does not start with 'v=', so it is not SDP"),n.includes("\0")&&t("contains a null byte"),/\r(?!\n)/.test(n)&&t("contains a bare carriage return");for(let i of n.split(/\r\n|\n/)){let o=ti(i);o>ei&&t(`has a ${o}-byte line, over the ${ei} limit`);}}function Gt(n){for(let e of n.split(/\r\n|\n/)){if(!e.startsWith("m="))continue;let[t,r="0"]=e.slice(2).trim().split(/\s+/);if(!(t!=="audio"&&t!=="video")&&r.split("/")[0]!=="0")return true}return false}var Ls=3e3,Ds=1e4,qs=15e3;async function et(n){let e=n.peerConnection;try{ri(n,"before the handshake started"),$n(n);let t=e.createDataChannel(n.channelLabel??"gem",{ordered:!0});for(let a of n.extraChannels??[]){let s=e.createDataChannel(a.label,{...a.ordered===void 0?{}:{ordered:a.ordered},...a.maxRetransmits===void 0?{}:{maxRetransmits:a.maxRetransmits}});n.onExtraChannel?.(a.label,s);}let r=await e.createOffer();await e.setLocalDescription(r),await Ns(e,n),$n(n);let i=e.localDescription?.sdp??r.sdp;if(!i)throw new u("invalid_sdp","the offer carried no SDP");se(i,"offer"),ni(i,"offer");let o=await Fs(n,i);if($n(n),typeof o?.sdp!="string")throw new u("invalid_sdp","the signaling channel answered without an SDP");return se(o.sdp,"answer"),ni(o.sdp,"answer"),await e.setRemoteDescription({type:"answer",sdp:o.sdp}),await js(t,n),ri(n,"before the channel opened"),t}catch(t){throw e.close(),$s(t)}}function $s(n){return n instanceof u?n:si(n)?Re():new u("connection_failed",ai(n),{cause:n})}function ni(n,e){if(Gt(n))throw new u("media_refused",`the ${e} carries an active audio or video section. This path carries a data channel only; voice belongs to the platform voice service.`)}async function Fs(n,e){let t=n.exchangeTimeoutMs??Ds,r=oi(n,t),i=n.signaling.exchange({matchId:n.matchId,sdp:e,intent:n.intent??"initial"},n.signal).catch(o=>{throw o instanceof u?o:si(o)?Re():new u("signaling_failed",ai(o),{cause:o})});return Us(i,n,r,()=>r.exhausted?Fn("while the offer was in flight"):new u("timeout",`no answer arrived within ${t}ms`))}function Ns(n,e){if(n.iceGatheringState==="complete"||e.signal?.aborted)return Promise.resolve();let{clock:t}=e;return new Promise(r=>{let i=()=>{t.clearTimeout(a),n.removeEventListener("icegatheringstatechange",o),e.signal?.removeEventListener("abort",i),r();},o=()=>{n.iceGatheringState==="complete"&&i();},a=t.setTimeout(i,e.gatherTimeoutMs??Ls);n.addEventListener("icegatheringstatechange",o),e.signal?.addEventListener("abort",i);})}function js(n,e){if(e.signal?.aborted)return Promise.reject(Re());if(n.readyState==="open")return Promise.resolve();if(n.readyState!=="connecting")return Promise.reject(new u("connection_failed",`the data channel was already '${n.readyState}' when the answer was applied`));let{clock:t}=e,r=e.openTimeoutMs??qs,i=oi(e,r);return new Promise((o,a)=>{let s=()=>{t.clearTimeout(p),n.removeEventListener("open",l),n.removeEventListener("close",d),n.removeEventListener("error",d),e.signal?.removeEventListener("abort",m);},l=()=>{s(),o();},d=y=>{s(),a(new u("connection_failed","the data channel failed before it opened",ii(y)===void 0?void 0:{cause:ii(y)}));},m=()=>{s(),a(Re());},c=()=>{s(),a(i.exhausted?Fn("while the channel was still opening"):new u("timeout",`the data channel did not open within ${r}ms`));},p=t.setTimeout(c,i.ms);n.addEventListener("open",l),n.addEventListener("close",d),n.addEventListener("error",d),e.signal?.addEventListener("abort",m);})}function oi(n,e){if(n.credentialExpiresAt===void 0)return {ms:e,exhausted:false};let t=n.credentialExpiresAt-n.clock.now();return t<=e?{ms:Math.max(0,t),exhausted:true}:{ms:e,exhausted:false}}function Us(n,e,t,r){let{clock:i,signal:o}=e;return o?.aborted?Promise.reject(Re()):new Promise((a,s)=>{let l=c=>{i.clearTimeout(m),o?.removeEventListener("abort",d),c();},d=()=>l(()=>s(Re())),m=i.setTimeout(()=>l(()=>s(r())),t.ms);o?.addEventListener("abort",d),n.then(c=>l(()=>a(c)),c=>l(()=>s(c)));})}function ri(n,e){if(n.credentialExpiresAt!==void 0&&!(n.clock.now()<n.credentialExpiresAt))throw Fn(`already ${e}`)}function Fn(n){return new u("credential_expired",`the join credential ran out ${n} \u2014 poll match status again for a fresh one`)}function $n(n){if(n.signal?.aborted)throw Re()}function Re(){return new u("aborted","the caller aborted the connection attempt")}function si(n){return n?.name==="AbortError"}function ii(n){return n?.error}function ai(n){return n instanceof Error?n.message:String(n)}var tt="0.1.0-beta",li="0-beta",Me="web",Ws=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?$/;function Hs(n){let e=Ws.exec(n),t=e?.[1],r=e?.[4];return t===void 0?null:r===void 0?t:`${t}-${r}`}function di(n,e){let t=Bs(n.platform,e),r=n.engineVersion;if(r===void 0)return {...n,platform:t,engineVersion:tt};let i=Hs(r);if(i===null)throw new u("misconfigured",`${e} refused: engineVersion '${r}' is not semver, so no engine line can be derived from it and gem-api answers 400. Omit it \u2014 a browser game has no engine version of its own, and the SDK reports '${tt}' for it.`);if(i!==li)throw new u("misconfigured",`${e} refused: engineVersion '${r}' derives to engine line '${i}', and '${li}' is the only line a web dedicated-server fleet runs. gem-api uses the line \u2014 the semver major plus any prerelease tag \u2014 verbatim as the fleet's label selector, so a match on '${i}' would match nothing and be reported as a capacity shortfall rather than as this. Omit engineVersion: a browser game has no engine version of its own, and the SDK reports '${tt}' for it.`);return {...n,platform:t,engineVersion:r}}function Bs(n,e){if(n===void 0||n===Me)return Me;if(!mn.includes(n))throw new u("misconfigured",`${e} refused: platform '${String(n)}' is not a client platform gem-api recognises \u2014 it knows ${mn.map(t=>`'${t}'`).join(", ")}. Remove the field: the SDK reports '${Me}', which is the only one true of a browser build.`);return console.warn(`[gem] ${e}: clientVersion.platform '${n}' was replaced with '${Me}' and is deprecated \u2014 remove the field. A browser game publishes its bundle in the web package dimension, so a client reporting anything else is version-checked against a dimension it has nothing in, and every blacklist, release and expiry check passes without looking at this build.`),Me}function Vs(n){let{client:e,clock:t,rooms:r}=n,i=n.signaling??It,o=n.log??(f=>console.warn(f)),a=new ve({client:e,gameId:()=>r.gameId,transport:"dedicated"}),s=new Map,l=(f,_)=>{let k=s.get(f)??new Set;k.add(_),s.set(f,k);},d=(f,_)=>{let k=s.get(f);k!==void 0&&(k.delete(_),k.size===0&&s.delete(f));},m=async f=>{if(r.isHost&&!ta(r)&&r.currentRoom?.matchId===void 0){let _=H(f.clusterTargetId);await r.startMatch({..._===void 0?{}:{clusterTargetId:_},...f.retry===void 0?{}:{retry:f.retry},...f.signal===void 0?{}:{signal:f.signal}});}return hi(r.matchConnectionState==="suspended"?await r.reconnectToMatch(Nn(f)):await r.awaitMatch(Nn(f)))},c=async f=>hi(await r.pollMatchStatus(Nn(f))),p=async(f,_)=>{let k=false;try{let R=await m(f),$=r.currentRoom?.transports;if($!==void 0&&!$.includes("webrtc"))throw new u("misconfigured",`the room template offers (${$.join(", ")||"no transports"}), not webrtc, which is all this path dials. Connect through gem.match.connect(), or pass \`transports: []\` here to negotiate across the template's list.`);let O=f.peerConnection??fi();k=!0;let F=await et({peerConnection:O,signaling:_,clock:t,matchId:R.matchId,credentialExpiresAt:R.credentialExpiresAt,...Lt(f,"intent","channelLabel","gatherTimeoutMs","exchangeTimeoutMs","openTimeoutMs","signal")}),j=null;if(f.presentCredential!==!1)try{j=await Xe({channel:F,clock:t,credential:R.joinCredential,credentialExpiresAt:R.credentialExpiresAt,...Lt(f,"onMessage","signal"),...f.welcomeTimeoutMs===void 0?{}:{ceilingMs:f.welcomeTimeoutMs}});}catch(U){throw X(F),X(O),U}return ea(r,R,F,O,_,f.reportDisconnect!==!1,f.reportConnected!==!1,j,{remember:l,forget:d},f.onMatchEnded)}catch(R){throw k&&gi(a,R),X(_),R}},y=async(f,_,k,R,$,O)=>{if(f==="webtransport"){let G=R?.webtransport;if(G===void 0)throw new u("misconfigured","webtransport is not advertised");let Q={credential:k.joinCredential,credentialExpiresAt:k.credentialExpiresAt,clock:t};if(typeof G.named_url=="string"&&G.named_url!==""){let ae=Math.max(Math.floor(O/2),1e3);try{return await Ze({url:G.named_url,timeoutMs:ae,log:o,...Q})}catch(pe){if(Ot()!==null)throw pe;return o(`[gem] webtransport named dial failed (${Mt(pe)}); retrying the pinned dial`),Ze({url:G.url,certHashBase64:G.cert_sha256_b64,timeoutMs:ae,log:o,...Q})}}return Ze({url:G.url,certHashBase64:G.cert_sha256_b64,timeoutMs:O,...Q})}if(f==="websocket"){let G=R?.websocket;if(G===void 0)throw new u("misconfigured","websocket is not advertised");return Ln({url:G.url,credential:k.joinCredential,credentialExpiresAt:k.credentialExpiresAt,clock:t,timeoutMs:O})}let F=_.peerConnection??fi(),j,U=await et({peerConnection:F,signaling:$,clock:t,matchId:k.matchId,credentialExpiresAt:k.credentialExpiresAt,extraChannels:[{label:Tt,ordered:false,maxRetransmits:0}],onExtraChannel:(G,Q)=>{G===Tt&&(j=Q);},...Lt(_,"intent","channelLabel","gatherTimeoutMs","exchangeTimeoutMs","openTimeoutMs","signal"),..._.exchangeTimeoutMs===void 0&&_.redial===true?{exchangeTimeoutMs:Js}:{}}),z;try{z=await Xe({channel:U,clock:t,credential:k.joinCredential,credentialExpiresAt:k.credentialExpiresAt,wire:Et,heartbeat:!0,...Lt(_,"signal"),..._.welcomeTimeoutMs===void 0?{}:{ceilingMs:_.welcomeTimeoutMs}});}catch(G){throw X(U),X(F),G}let T=z.welcome.heartbeatIntervalMs;return {link:qn(U,F,j,T===void 0?void 0:{clock:t,intervalMs:T}),welcome:z.welcome,channel:U,peerConnection:F}},b=(f,_,k,R,$)=>{let O=false,F;R.link.onClose(T=>{!O&&f.reportDisconnect!==false&&vn(r,_.matchId,T),F?.(T);}),f.reportDisconnect!==false&&f.reportConnected!==false&&r.notifyMatchConnected(_.matchId);let j={transport:R.link.transport,send:(T,G)=>R.link.send(T,G),onMessage:T=>R.link.onMessage(T),onClose:T=>{F=T;},close:()=>R.link.close()},U=()=>{O||(O=true,X(R.link),X($));},z=T=>{if(!O){try{f.onMatchEnded?.(T);}catch{}U();}};return l(_.matchId,z),{matchId:_.matchId,transport:k,link:j,joinCredential:_.joinCredential,credentialExpiresAt:_.credentialExpiresAt,welcome:R.welcome,...R.channel===void 0?{}:{channel:R.channel},...R.peerConnection===void 0?{}:{peerConnection:R.peerConnection},close(){d(_.matchId,z),U();}}},v=async(f,_)=>{let k=r.currentRoom?.id,R=r.gameId,$=()=>({advert:Ct(r.currentRoom?.settings,f),template:r.currentRoom?.transports??null});if(k===void 0||R===null)return $();try{let O=await e.get(`/v2/games/${encodeURIComponent(R)}/game-rooms/${encodeURIComponent(k)}`,_);return {advert:Ct(O.settings,f),template:Qe(O.transports)??r.currentRoom?.transports??null}}catch{return $()}},A=(f,_)=>{let k=f??ci;return _.length===0?k:k.filter(R=>_.includes(R))},M=async(f,_,k)=>{try{let R=await m(f),{advert:$,template:O}=await v(R.matchId,f.signal),F=A(O,_),j=F.filter(N=>Ys(N,$)),U=Ot();if(U!==null&&F.includes("webtransport")&&$?.webtransport!==void 0&&o(`[gem] webtransport skipped: ${U} (learned on an earlier dial in this page)`),j.length===0){let N=O===null?`the default order (${ci.join(", ")})`:`the room template's transports (${O.join(", ")||"none"})`;throw new u("misconfigured",`none of ${N}`+(_.length>0?`, narrowed to (${_.join(", ")}),`:"")+" is available here: the server advertises none of them, and only webrtc qualifies without an advertisement.")}let z=f.transportAttemptTimeoutMs??Ks,T=()=>f.deadlineAt===void 0?1/0:f.deadlineAt-t.now(),G=N=>n.signalingReady===void 0||N<=0?Promise.resolve(!0):n.signalingReady(N),Q=[],ae=[];for(let N of j){if(T()<pi){o(`[gem] ${N} rung not tried: the reconnect budget is spent`);break}ae.length>0&&R.credentialExpiresAt-t.now()<mi&&(R=await c(f)),ae.push(N);let tr=!1,nr=!1,pt={attempts:0,waitedMs:0};for(;;){if(N==="webrtc"&&f.redial===!0){let te=Math.min(ui,T());await G(te)||o(`[gem] webrtc rung: the page has not reported its signaling socket up after ${Math.round(te)}ms; offering anyway`);}let Yi=Math.max(Math.min(z,T()),pi);try{return b(f,R,N,await y(N,f,R,$,k,Yi),k)}catch(te){let W=te instanceof u?te:new u("connection_failed",te instanceof Error?te.message:String(te),{cause:te});if(N==="webrtc"&&gi(a,W),(W.reason==="credential_expired"||W.reason==="credential_reused")&&!tr){tr=!0,R=await c(f);continue}if(N==="webrtc"&&W.reason==="signaling_failed"&&W.platformCode===yn&&!nr){nr=!0;let me=Math.min(ui,T());if(o(`[gem] webrtc rung: the page could not put the offer on its socket; waiting up to ${Math.round(me)}ms for the socket before offering again`),await G(me))continue}if(W.reason==="join_rate_limited"){let me=Xs(pt,Math.random,T());if(me!==null){if(pt.attempts+=1,pt.waitedMs+=me,o(`[gem] ${N} rung: the server is rate-limiting this player's joins; waiting ${Math.round(me)}ms before presenting again (retry ${pt.attempts} of ${bi.maxAttempts})`),await At(t,me,f.signal),f.signal?.aborted)throw new u("aborted","the caller aborted while waiting out a join rate limit");R.credentialExpiresAt-t.now()<mi&&(R=await c(f));continue}}if(zs.has(W.reason)||W.reason==="credential_expired"||N==="webrtc"&&yi(W)){o(`[gem] ${N} rung failed (${W.reason}): ${Mt(W.cause??W)} \u2014 `+(ae.length<j.length?`falling through to ${j[ae.length]}`:"no rungs left")),Q.push(W);break}throw o(`[gem] ${N} rung refused the session (${W.reason}): ${W.message}`),W}}}let pe=Q[Q.length-1];throw pe===void 0?new u("timeout",`the reconnect budget ran out before any transport (${j.join(", ")}) could be tried`):new u(pe.reason,`every transport tried (${ae.join(", ")}) failed; the last said: ${pe.message}`,{cause:Q,platformCode:pe.platformCode})}catch(R){throw X(k),R}};return {listClusterTargets:async(f={})=>xt({client:e,...f}),handleMatchEnded(f,_){let k=s.get(f);if(k!==void 0){s.delete(f);for(let R of k)R(_);}},connect:(async(f={})=>{let _=f.signaling??i();return f.transports===void 0?p(f,_):M(f,f.transports,_)})}}var ci=["webtransport","webrtc","websocket"],Ks=1e4,Js=5e3,ui=3e3,pi=1e3,mi=1e4,zs=new Set(["timeout","connection_failed","signaling_unavailable","signaling_failed"]);function yi(n){return n.platformCode!==void 0&&bn.has(n.platformCode)}var bi={initialDelayMs:2e3,factor:2,jitterMs:1e3,maxAttempts:3,budgetMs:15e3};function Xs(n,e=Math.random,t=1/0){let r=bi;if(n.attempts>=r.maxAttempts)return null;let i=Math.min(r.budgetMs-n.waitedMs,t);if(i<r.initialDelayMs)return null;let o=r.initialDelayMs*r.factor**n.attempts;return Math.min(o+e()*r.jitterMs,i)}function Ys(n,e){return n==="webrtc"?e===null||e.webrtc!==false:e===null?false:n==="webtransport"?e.webtransport!==void 0&&On():e.websocket===void 0?false:e.websocket.tls===true||globalThis.isSecureContext!==true||Qs(e.websocket.url)}function Qs(n){if(typeof n!="string")return false;let e;try{e=new URL(n).hostname.toLowerCase();}catch{return false}return Te(e)}function Nn(n){return {...n.timeoutMs===void 0?{}:{timeoutMs:n.timeoutMs},...n.pollIntervalMs===void 0?{}:{pollIntervalMs:n.pollIntervalMs},...n.signal===void 0?{}:{signal:n.signal}}}var Zs=new Set(["timeout","connection_failed","credential_expired","signaling_unavailable","signaling_failed","invalid_sdp","join_refused"]);function gi(n,e){e instanceof u&&(!Zs.has(e.reason)&&!yi(e)||n.report([{type:"connection_failures_total",value:1}]));}function ea(n,e,t,r,i,o,a,s,l,d){let m=false,c=()=>{m||vn(n,e.matchId,void 0);};o&&(t.addEventListener("close",c),a&&n.notifyMatchConnected(e.matchId));let p=()=>{m||(m=true,t.removeEventListener("close",c),s?.detach(),X(t),X(r),X(i));},y=b=>{if(!m){try{d?.(b);}catch{}p();}};return l.remember(e.matchId,y),{matchId:e.matchId,channel:t,peerConnection:r,joinCredential:e.joinCredential,credentialExpiresAt:e.credentialExpiresAt,welcome:s?.welcome??null,close(){l.forget(e.matchId,y),p();}}}function ta(n){let e=n.matchSession.phase;return e!=="none"&&e!=="ended"}function hi(n){if(n.status==="failed")throw new u("allocation_failed",`match ${n.matchId} did not come up: ${n.reason}`);if(n.status==="ended")throw new u("match_ended",`match ${n.matchId} ended (${n.reason}) before this side connected.`);if(n.protocol!==ze)throw new u("unsupported_protocol",`match ${n.matchId} speaks '${n.protocol}', which a browser cannot connect. Only '${ze}' is reachable from a frame; host and port on this response are advisory and must not be dialled.`);let{joinCredential:e,credentialExpiresAt:t}=n;if(e===void 0||t===void 0)throw new u("connection_failed",`match ${n.matchId} is ready but issued no join credential, so the server would refuse the channel once it opened \u2014 poll match status again.`);return {...n,joinCredential:e,credentialExpiresAt:t}}function fi(){let n=globalThis.RTCPeerConnection;if(!n)throw new u("misconfigured","this environment has no RTCPeerConnection. Pass `peerConnection` to connect against a stand-in, which is how the flow is tested outside a browser.");return new n({iceServers:[]})}function X(n){try{n.close();}catch{}}function Lt(n,...e){let t={};for(let r of e)n[r]!==void 0&&(t[r]=n[r]);return t}var Oe=class{constructor(e){this.options=e;this.reporter=new ve({client:e.client,gameId:e.gameId,transport:"p2p"});}options;reporter;async joinCredential(e,t,r){let i=await this.options.client.post(`${this.match(e)}/join-credential`,{fingerprint:t},r);return {credential:Dt(i.credential)??"",hostFingerprint:Dt(i.host_fingerprint)??"",hostEpoch:qt(i.host_epoch)??0,expiresAt:(qt(i.expires_at)??0)*1e3}}async turnLease(e,t,r){let i=await this.options.client.post(`${this.match(e)}/turn-lease`,{cluster_target_id:t},r),o=[];for(let l of Array.isArray(i.ice_servers)?i.ice_servers:[]){let d=l,m=(Array.isArray(d.urls)?d.urls:[]).filter(c=>typeof c=="string"&&c.length>0);m.length!==0&&o.push({urls:m,username:Dt(d.username)??"",credential:Dt(d.credential)??""});}let a=qt(i.expires_at),s=qt(i.ttl_seconds);return {iceServers:o,expiresAt:a!==void 0?a*1e3:Date.now()+(s??0)*1e3}}async kick(e,t,r){await this.options.client.post(`${this.match(e)}/kick`,{player_id:t},r);}async leave(e,t=false){await this.options.client.post(`${this.match(e)}/leave`,{},void 0,{keepalive:t});}async telemetry(e,t){await this.reporter.post(e,t);}match(e){return `/v1/games/${encodeURIComponent(this.options.gameId())}/matches/${encodeURIComponent(e)}`}},na=new Set(["cluster_hidden","invalid_cluster_target"]);async function vi(n,e,t,r){let i=V(t);if(i.length===0)throw new Error("no cluster to lease TURN from");let o;for(let a of i)try{return {...await n.turnLease(e,a,r),clusterTargetId:a}}catch(s){if(!ra(s))throw s;o=s;}throw o}function ra(n){return n instanceof q&&na.has(n.code??"")}function Dt(n){return typeof n=="string"&&n.length>0?n:void 0}function qt(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}var ia=new Set(["timeout","signaling_failed","connection_failed","keys_unavailable"]),w=class extends Error{constructor(t,r,i){super(`${t}: ${r}`,i);this.reason=t;this.name="P2pError",this.retryable=ia.has(t);}reason;retryable};var $t=class{listeners=new Map;on(e,t){let r=this.listeners.get(e);return r||(r=new Set,this.listeners.set(e,r)),r.add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t);}once(e,t){let r=this.on(e,((...i)=>{r(),t(...i);}));return r}emit(e,...t){let r=this.listeners.get(e);if(r)for(let i of [...r])i(...t);}clear(){this.listeners.clear();}};var oa=/^sha-256 ([0-9A-F]{2}:){31}[0-9A-F]{2}$/;function ke(n){let e=null;for(let t of n.split(/\r\n|\n/)){if(!t.startsWith("a=fingerprint:"))continue;let r=nt(t.slice(14).trim());if(r===null||e!==null&&e!==r)return null;e=r;}return e}function nt(n){let[e,...t]=n.trim().split(/\s+/);if(e===void 0||t.length===0)return null;let r=`${e.toLowerCase()} ${t.join("").toUpperCase()}`;return oa.test(r)?r:null}function we(n,e){if(n.length!==e.length)return false;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}var jn=5e3,sa=12e4+jn,aa=1024,la=6e4,da=1e4,_i=64*1024;function Ft(n){let e=new Map,t=-1/0,r=null,i=(()=>{let s;try{s=new URL(`${n.baseUrl.replace(/\/+$/,"")}/oauth2/jwks`);}catch{return {ok:false,reason:"the configured API base is not a valid URL, so the JWKS endpoint cannot be reached. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure."}}return s.username!==""||s.password!==""?{ok:false,reason:"the configured API base carries credentials in the URL, which `fetch` will not send. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure."}:{ok:true,url:s.href,shown:`${s.protocol}//${s.host}`}})(),o=()=>{let s=n.subtle??globalThis.crypto?.subtle;if(!s)throw new w("keys_unavailable","this environment has no WebCrypto, so a join credential cannot be verified. A page served over plain http on a non-loopback origin is the usual cause \u2014 crypto.subtle is only exposed in a secure context.");return s},a=async()=>{if(!i.ok)throw new w("keys_unavailable",i.reason);let s=n.fetch??((p,y)=>globalThis.fetch(p,y)),l;try{l=await s(i.url,{redirect:"error",credentials:"omit",cache:"no-store",referrerPolicy:"no-referrer",signal:AbortSignal.timeout(da)});}catch{throw new w("keys_unavailable",`the JWKS endpoint at ${i.shown} could not be reached \u2014 it timed out, refused a redirect, or the network failed.`)}if(!l.ok)throw new w("keys_unavailable",`the JWKS endpoint answered ${i.shown} with an error`);let d;try{let p=await l.text();if(p.length>_i)throw new w("keys_unavailable",`the JWKS endpoint at ${i.shown} answered with more than ${_i} characters, which is not a key set this platform publishes.`);d=JSON.parse(p);}catch(p){throw p instanceof w?p:new w("keys_unavailable",`the JWKS endpoint at ${i.shown} did not answer with a readable key set.`)}let m=Array.isArray(d.keys)?d.keys:[],c=o();for(let p of m){let y=p;if(y.kty==="RSA"&&!(y.alg!==void 0&&y.alg!=="RS256")&&!(y.use!==void 0&&y.use!=="sig")&&!(typeof y.kid!="string"||y.kid.length===0))try{let b=await c.importKey("jwk",{...p,alg:"RS256",ext:!0},{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},!1,["verify"]);e.set(y.kid,b);}catch{}}};return {async key(s){let l=e.get(s);if(l)return l;if(!i.ok)throw new w("keys_unavailable",i.reason);if(r)return await r,e.get(s)??null;let d=n.now();if(!Number.isFinite(d))throw new w("keys_unavailable","the clock supplied to the SDK returned a non-finite time, so the JWKS throttle cannot be evaluated. This is a configuration fault, not a server failure.");return d<t&&(t=-1/0),d-t<la?null:(t=d,r??=a().finally(()=>{r=null;}),await r,e.get(s)??null)}}}var rt=class{seen=new Map;admit(e,t){for(let[r,i]of this.seen)i<=t&&this.seen.delete(r);if(this.seen.has(e))return false;if(this.seen.size>=aa){let r=this.seen.keys().next().value;r!==void 0&&this.seen.delete(r);}return this.seen.set(e,t+sa),true}};function wi(n){let e=n.replace(/-/g,"+").replace(/_/g,"/"),t=atob(e+"=".repeat((4-e.length%4)%4)),r=new Uint8Array(t.length);for(let i=0;i<t.length;i++)r[i]=t.charCodeAt(i);return r}function Ri(n){try{let e=new TextDecoder().decode(wi(n)),t=JSON.parse(e);return typeof t!="object"||t===null||Array.isArray(t)?null:t}catch{return null}}async function Nt(n,e,t,r,i){let o=T=>{throw new w("peer_unauthenticated",`the peer's join credential is not usable: ${T}`)},a=n.split(".");a.length!==3&&o("it is not a three-part JWT");let[s,l,d]=a,m=Ri(s);m===null&&o("its header does not decode"),m.alg!=="RS256"&&o(`its alg is ${JSON.stringify(m.alg)}, not RS256`),m.typ!=="p2p+jwt"&&o(`its typ is ${JSON.stringify(m.typ)}, not p2p+jwt`);let c=m.kid;(typeof c!="string"||c.length===0)&&o("it names no key");let p=await t.key(c);p===null&&o("it was signed by a key this build cannot resolve");let y=i.subtle??globalThis.crypto?.subtle;y||o("this environment has no WebCrypto");let b=new TextEncoder().encode(`${s}.${l}`),v;try{v=wi(d);}catch{o("its signature does not decode");}await y.verify({name:"RSASSA-PKCS1-v1_5"},p,v,b)||o("its signature does not verify");let M=Ri(l);M===null&&o("its payload does not decode");let E=M,f=T=>typeof E[T]=="string"?E[T]:"",_=T=>typeof E[T]=="number"&&Number.isFinite(E[T])?E[T]:NaN;f("iss")!==i.issuer&&o("it was issued by someone else"),f("aud")!==`${i.issuer}/p2p-match`&&o("its audience is not this platform"),f("match_id")!==e.matchId&&o("it is for another match"),f("game_id")!==e.gameId&&o("it is for another game");let k=_("exp")*1e3,R=_("nbf")*1e3;(!Number.isFinite(k)||e.now>k+jn)&&o("it has expired"),Number.isFinite(R)&&e.now<R-jn&&o("it is not valid yet");let $=f("role");$!=="host"&&$!=="client"&&o("it names no usable role");let O=_("host_epoch");O!==e.hostEpoch&&o("it names a different host epoch");let F=nt(f("fingerprint"));F===null&&o("its fingerprint claim is malformed"),we(F,e.peerFingerprint)||o("it was issued for a different certificate than this connection used");let j=nt(f("host_fingerprint"));j===null&&o("its host fingerprint claim is malformed"),we(j,e.hostFingerprint)||o("it was issued against a different host certificate");let U=f("jti");U.length===0&&o("it carries no jti, so replay cannot be detected"),r.admit(U,e.now)||o("it has already been used");let z=f("sub");return z.length===0&&o("it names no player"),{playerId:z.toLowerCase(),matchId:e.matchId,gameId:e.gameId,hostEpoch:O,role:$,fingerprint:F,hostFingerprint:j,jti:U,expiresAt:k}}var Se=32769;function Un(n){let e=new TextEncoder().encode(n.jwt);if(e.length>8192)throw new Error(`[gem] a join credential of ${e.length} bytes exceeds the wire format's cap`);let t=new ArrayBuffer(3+e.length+4+4),r=new DataView(t);return r.setUint8(0,1),r.setUint16(1,e.length,true),new Uint8Array(t,3,e.length).set(e),r.setUint32(3+e.length,n.protocolVersion,true),r.setUint32(7+e.length,n.assignedPeerId,true),t}function Wn(n){let e=new ArrayBuffer(9),t=new DataView(e);return t.setUint8(0,2),t.setUint32(1,n.protocolVersion,true),t.setUint32(5,n.assignedPeerId,true),e}function Hn(n){let e=new ArrayBuffer(2),t=new DataView(e);return t.setUint8(0,3),t.setUint8(1,n),e}function Bn(n){if(n.byteLength<1)return null;let e=new DataView(n);switch(e.getUint8(0)){case 1:{if(n.byteLength<3)return null;let t=e.getUint16(1,true);return t>8192||n.byteLength<3+t+8?null:{kind:"join",jwt:new TextDecoder().decode(new Uint8Array(n,3,t)),protocolVersion:e.getUint32(3+t,true),assignedPeerId:e.getUint32(7+t,true)}}case 2:return n.byteLength<9?null:{kind:"admit",protocolVersion:e.getUint32(1,true),assignedPeerId:e.getUint32(5,true)};case 3:return n.byteLength<2?null:{kind:"reject",reason:e.getUint8(1)};default:return null}}function ki(n){switch(n){case 1:return "the host did not accept this credential";case 2:return "the host speaks a different protocol version \u2014 an engine peer refuses a browser peer here, deliberately";case 3:return "the match is full";case 4:return "the host is rate-limiting joins";case 129:return "the host verified this credential but it names a different player than the one who signalled \u2014 the host has its own line saying which";case 130:return "the host has this player on the kicked list for the current host epoch";case 131:return "the host could not fetch the platform's signing keys, so it cannot verify anyone yet \u2014 the host's console says why";default:return `the host refused with an unrecognised code (${n})`}}function Si(n){switch(n){case 129:return "not_expected_player";case 130:return "kicked";case 131:return "keys_unavailable";default:return "peer_unauthenticated"}}var xi=3e4,Ge=class{constructor(e,t,r){this.localPlayerId=e;this.hostPlayerId=t,this.epoch=r;}localPlayerId;state="idle";epoch;hostPlayerId;kicked=new Set;startedAt=0;get currentState(){return this.state}get currentEpoch(){return this.epoch}get currentHostPlayerId(){return this.hostPlayerId}get isLocalHost(){return this.hostPlayerId===this.localPlayerId}isKicked(e){return this.kicked.has(e)}stalled(e){return this.state!=="idle"&&e-this.startedAt>3e4}apply(e,t){return !Number.isInteger(e.hostEpoch)||e.hostEpoch<0?{kind:"refused",why:`host epoch ${String(e.hostEpoch)} is not a whole number`}:e.hostEpoch===this.epoch?e.migrationComplete===true&&this.state!=="idle"?(this.state="idle",{kind:"completed"}):{kind:"duplicate"}:e.hostEpoch<this.epoch?{kind:"refused",why:`host epoch ${e.hostEpoch} is behind the current ${this.epoch}`}:e.hostPlayerId.length===0?{kind:"refused",why:"the migration named no new host"}:(this.kicked=new Set(e.kickedPlayerIds??[]),this.epoch=e.hostEpoch,this.hostPlayerId=e.hostPlayerId,this.startedAt=t,this.state=this.isLocalHost?"preparing":"reconnecting",{kind:"accepted",state:this.state})}complete(){this.state="idle";}};var jt={reliable:{negotiated:true,id:0,ordered:true},ordered:{negotiated:true,id:1,ordered:true,maxRetransmits:0},unreliable:{negotiated:true,id:2,ordered:false,maxRetransmits:0}},it=["reliable","ordered","unreliable"];function Ut(n){let e=globalThis.RTCPeerConnection;if(!e)throw new Error("[gem] this environment has no RTCPeerConnection. Pass `peerConnectionFactory` to connect against a stand-in, which is how this flow is tested outside a browser.");return new e({iceServers:n.iceServers,...n.certificates===void 0?{}:{certificates:n.certificates}})}async function Wt(){let n=globalThis.RTCPeerConnection;return n?.generateCertificate?n.generateCertificate({name:"ECDSA",namedCurve:"P-256"}):null}var Pi=1,ga=3e4,Ii=2e3,ha=5e3,Ti=6e4,fa=8,ya=12e4,ba=5e3,va=8e3,Oi=18e5,_a=Oi+6e4,Kn=class{constructor(e=fa){this.perMinute=e;}perMinute;sent=[];delayUntilAllowed(e){return this.prune(e),this.sent.length<this.perMinute?0:Math.max(0,(this.sent[0]??e)+6e4-e)}record(e){this.prune(e),this.sent.push(e);}prune(e){let t=0;for(let r of this.sent)r<=e&&e-r<6e4&&(this.sent[t++]=r);this.sent.length=t;}},Ra=new Kn,wa=8,ka=new Set(["timeout","connection_failed","invalid_sdp","peer_unauthenticated","not_expected_player","keys_unavailable"]),Le=class{constructor(e){this.options=e;this.keys=e.keys??Ft({baseUrl:e.baseUrl,now:()=>e.clock.now()});}options;emitter=new $t;links=new Map;replays=new rt;keys;localPlayerId="";migration=null;lease=null;clusterTargetIds=[];leaseRenewal=null;leaseAskedAt=null;leaseRenewalWarned=false;credential=null;nextPeerId=Pi+1;ended=null;closed=false;ready=null;helloSeen=false;reportedUnreachableEpoch=-1;reportedStallEpoch=-1;counted=false;pendingOffers=new Map;answeredSessions=new Map;credentialGeneration=0;credentialDeadline=0;drainTimers=new Set;get matchId(){return this.options.matchId}get playerId(){return this.localPlayerId}get isHost(){return this.migration?.isLocalHost??false}get hostPlayerId(){return this.migration?.currentHostPlayerId??""}get hostEpoch(){return this.migration?.currentEpoch??0}get peers(){let e=[];for(let t of this.links.values())t.admitted&&e.push(this.expose(t));return e}peer(e){let t=this.links.get(e);return t&&t.admitted?this.expose(t):null}on(e,t){return this.emitter.on(e,t)}off(e,t){this.emitter.off(e,t);}once(e,t){return this.emitter.once(e,t)}async start(e){let t=this.options.connectTimeoutMs??ga;this.credentialDeadline=this.options.clock.now()+t;let r=new Promise((a,s)=>{this.ready={resolve:a,reject:s};});if(this.clusterTargetIds=V([this.options.clusterTargetId,...this.options.clusterTargetIds??[]]),this.clusterTargetIds.length>0)try{await this.takeLease(e),this.mark("turn leased");}catch(a){this.mark("turn lease failed"),this.warn(`no TURN relay could be leased for this match, so peers behind a symmetric NAT will fail to connect: ${st(a)}`);}else this.mark("turn skipped"),this.warn("connecting without a TURN relay because no cluster target was resolved. Host candidates only reach peers on the same network, so peers behind a symmetric NAT will fail to connect. The Arcade page normally resolves one for this player; outside it, pass `clusterTargetId` to connect().");this.options.signaling.join(),this.mark("signaling joined");let i=this.options.clock.setTimeout(()=>{this.fail(new w("timeout",`no usable P2P connection after ${t}ms. The parent relays signaling, so a silent failure here usually means the page is not putting frames on the socket.`));},t),o=()=>this.fail(new w("aborted","the caller aborted while connecting"));e?.aborted?o():e?.addEventListener("abort",o);try{await r;}finally{this.options.clock.clearTimeout(i),e?.removeEventListener("abort",o),this.ready=null;}}async kick(e){if(!this.isHost)throw new w("misconfigured","only the current host of this match can kick a peer");await this.options.api.kick(this.options.matchId,e);}async leave(){try{await this.options.api.leave(this.options.matchId);}catch{}this.close();}close(){if(!this.closed){this.closed=true,this.pendingOffers.clear();for(let e of this.drainTimers)this.options.clock.clearTimeout(e);this.drainTimers.clear();for(let e of [...this.links.values()])this.dropLink(e,"closed",false);this.options.signaling.close(),this.emitter.clear();}}report(e){this.options.api.telemetry(e).catch(()=>{});}notifyCandidatesDropped(e){this.emitter.emit("candidatesDropped",e);}handleSignal(e,t){this.closed||this.route(e,t).catch(r=>this.report_(r));}async route(e,t){switch(e){case "hello":return this.onHello(t);case "offer":return this.onOffer(t);case "answer":return this.onAnswer(t);case "ice":return this.onIce(t);case "peerLeft":return this.onPeerLeft(t);case "migration":return this.onMigration(t);case "matchEnded":return this.onMatchEnded(t);case "error":return this.onSignalingError(t);case "relayDraining":return this.onRelayDraining(t);default:return}}async onHello(e){let t=D(e.playerId),r=D(e.hostPlayerId),i=ot(e.hostEpoch);if(!(t===null||r===null||i===null)&&(this.mark("signaling hello"),this.localPlayerId=t,this.migration===null&&(this.migration=new Ge(t,r,i)),!this.helloSeen)){if(this.helloSeen=true,this.isHost){this.mark("host listening"),this.settle(),await this.drainPendingOffers();return}this.pendingOffers.clear(),await this.offerToHost("initial");}}async drainPendingOffers(){if(this.pendingOffers.size===0)return;let e=[...this.pendingOffers.values()];this.pendingOffers.clear(),this.mark("queued offer drained");for(let t of e)try{await this.onOffer(t);}catch(r){this.report_(r);}}async offerToHost(e){let t=this.hostPlayerId,r=this.openLink(t,Pi),i=await r.connection.createOffer();await r.connection.setLocalDescription(i);let o=r.connection.localDescription?.sdp??i.sdp;if(!o)throw new w("invalid_sdp","the browser produced an offer with no SDP");se(o,"offer"),this.refuseMedia(o,"offer"),this.options.signaling.offer(o,e),this.mark("offer sent"),this.fetchCredential().catch(a=>this.report_(a));}async fetchCredential(){let e=++this.credentialGeneration,r=this.links.get(this.hostPlayerId)?.connection.localDescription?.sdp,i=r===void 0?null:ke(r);if(i===null)throw new w("invalid_sdp","this browser produced an offer with no usable DTLS fingerprint, so the platform cannot bind a credential to it");let o=this.options.credentialPacing??Ra,a=()=>this.closed||e!==this.credentialGeneration,s=ha;for(;;){let l=o.delayUntilAllowed(this.options.clock.now());if(l>0&&!await this.waitForCredential(l,a))return;o.record(this.options.clock.now());try{let d=await this.options.api.joinCredential(this.options.matchId,i);if(a())return;this.credential=d,this.mark("credential ready"),this.maybeAuthenticate();return}catch(d){if(a())return;let m=d.status,c;if(m===409)c=Ii;else if(m===429){let p=d.retryAfterSeconds;typeof p=="number"&&Number.isFinite(p)&&p>=0?c=p*1e3:(c=s,s=Math.min(s*2,Ti)),c=Math.min(Math.max(c,Ii),Ti);}else throw d;if(!await this.waitForCredential(c,a))return}}}async waitForCredential(e,t){return this.options.clock.now()+e>this.credentialDeadline?false:(await this.sleep(e),!t())}async onOffer(e){let t=D(e.fromPlayerId),r=D(e.sdp);if(t===null||r===null)return;if(this.migration===null){if(this.pendingOffers.delete(t),this.pendingOffers.size>=wa){let l=this.pendingOffers.keys().next().value;l!==void 0&&this.pendingOffers.delete(l);}this.pendingOffers.set(t,e);return}if(!this.isHost||this.migration.isKicked(t)===true)return;let i=D(e.sessionId);if(i!==null&&this.answeredSessions.get(t)===i)return;se(r,"offer"),this.refuseMedia(r,"offer");let o=ke(r);if(o===null)throw new w("invalid_sdp",`the offer from ${t} carries no usable fingerprint`);if(this.mark("offer received"),i!==null&&this.answeredSessions.set(t,i),await this.refreshLease(false),this.closed||!this.isHost)return;let a=this.links.get(t);if(D(e.intent)==="ice_restart"&&a?.admitted===true&&a.remoteFingerprint!==null&&we(a.remoteFingerprint,o)){await this.answer(a,r),this.mark("ice restart answered");return}a&&this.dropLink(a,"resignalled",false);let s=this.openLink(t,this.nextPeerId++);s.remoteFingerprint=o,await this.answer(s,r),this.mark("answer sent");}async answer(e,t){await e.connection.setRemoteDescription({type:"offer",sdp:t}),this.flushCandidates(e);let r=await e.connection.createAnswer();await e.connection.setLocalDescription(r);let i=e.connection.localDescription?.sdp??r.sdp;if(!i)throw new w("invalid_sdp","the browser produced an answer with no SDP");se(i,"answer"),this.refuseMedia(i,"answer"),this.options.signaling.answer(e.playerId,i,e.assignedPeerId);}async onAnswer(e){if(this.isHost)return;let t=D(e.fromPlayerId),r=D(e.sdp);if(t===null||r===null)return;let i=this.links.get(this.hostPlayerId);if(!i||t!==this.hostPlayerId)return;se(r,"answer"),this.refuseMedia(r,"answer");let o=ke(r);if(o===null)throw new w("invalid_sdp","the host's answer carries no usable fingerprint");let a=ot(e.assignedPeerId);if(i.restart!==void 0&&a!==null&&a!==i.assignedPeerId){this.warn(`the host answered the ICE restart from a new connection (peer id ${a}, this link is ${i.assignedPeerId}); dropping the link to re-dial`),this.dropLink(i,"restart_mismatch",true);return}this.mark("answer received"),i.remoteFingerprint=o,i.pendingAnswer=r,this.hostVerified(i)&&await this.applyAnswer(i,r);}hostVerified(e){let t=this.credential;return t===null||e.remoteFingerprint===null?false:we(t.hostFingerprint,e.remoteFingerprint)?true:(this.fail(new w("peer_unauthenticated","the host's DTLS certificate is not the one the platform attested for this match. Refusing before the handshake, so no credential is presented.")),this.dropLink(e,"host_fingerprint_mismatch",false),false)}maybeAuthenticate(){if(this.isHost)return;let e=this.links.get(this.hostPlayerId);if(!e||e.remoteFingerprint===null||e.connection.remoteDescription!==null)return;let t=e.pendingAnswer;t!==void 0&&this.hostVerified(e)&&this.applyAnswer(e,t).catch(r=>this.report_(r));}async applyAnswer(e,t){this.mark("host verified"),e.pendingAnswer=void 0,await e.connection.setRemoteDescription({type:"answer",sdp:t}),this.flushCandidates(e),e.restart!==void 0&&(e.restart.answered=true,this.progressRestart(e));}async onIce(e){let t=D(e.fromPlayerId),r=D(e.candidate);if(t===null||r===null)return;let i=this.links.get(t);if(!i)return;let o=D(e.sdpMid),a=ot(e.sdpMLineIndex)??ot(e.sdpMLineIndexAlt),s={candidate:r,...o===null?{}:{sdpMid:o},...a===null?{}:{sdpMLineIndex:a}};if(i.connection.remoteDescription===null){i.pendingCandidates.length<64&&i.pendingCandidates.push(s);return}await i.connection.addIceCandidate(s).catch(()=>{});}onPeerLeft(e){let t=D(e.playerId);if(t===null)return;let r=D(e.reason)??"left";if(t===this.localPlayerId){this.fail(new w("kicked",`this player was removed from the match (${r})`)),this.close();return}let i=this.links.get(t);i&&this.dropLink(i,r,true);}async onMigration(e){let t=D(e.hostPlayerId),r=ot(e.hostEpoch);if(t===null||r===null||this.migration===null)return;let i=this.migration.apply({hostPlayerId:t,hostEpoch:r,...D(e.reason)===null?{}:{reason:D(e.reason)},...Array.isArray(e.kickedPlayerIds)?{kickedPlayerIds:e.kickedPlayerIds.filter(a=>typeof a=="string")}:{},...typeof e.migrationComplete=="boolean"?{migrationComplete:e.migrationComplete}:{}},this.options.clock.now());if(i.kind==="duplicate")return;if(i.kind==="completed"){this.emitter.emit("hostChanged",t,r);return}if(i.kind==="refused"){this.report_(new w("migration_failed",i.why));return}for(let a of [...this.links.values()])this.dropLink(a,"migrating",false);if(this.options.signaling.discardQueued(),this.credential=null,this.emitter.emit("migrating",t,r),i.state==="preparing"){this.options.signaling.migrationAck(r),this.watchMigration();return}this.watchMigration();let o=this.hostEpoch;await this.refreshLease(false),!(this.closed||this.hostEpoch!==o)&&await this.offerToHost("full_reconnect");}watchMigration(){let e=this.hostEpoch;this.options.clock.setTimeout(()=>{this.closed||this.migration===null||this.migration.currentEpoch===e&&this.migration.stalled(this.options.clock.now())&&this.reportedStallEpoch!==e&&(this.reportedStallEpoch=e,this.report_(new w("migration_failed",`host migration to epoch ${e} did not complete. The elected host never acknowledged it, so the platform is still holding the match in migration.`)));},31e3);}onMatchEnded(e){let t=D(e.reason)??"ended";this.ended=t,this.emitter.emit("matchEnded",t),this.ready!==null&&this.fail(new w("match_ended",`the match ended: ${t}`)),this.close();}onSignalingError(e){let t=D(e.code)??"unknown",r=D(e.message)??"the platform refused a signaling message",i=this.isHost?void 0:this.links.get(this.hostPlayerId)?.restart;if(i!==void 0){this.warn(`the platform refused an ICE restart (${t}: ${r})`),i.settle(false);return}this.report_(new w("signaling_failed",`${t}: ${r}`));}async onRelayDraining(e){if(this.migration===null)return;let t=new Set((Array.isArray(e.relayIps)?e.relayIps:[]).filter(c=>typeof c=="string").map(c=>c.toLowerCase())),r=this.isHost?void 0:this.links.get(this.hostPlayerId),i,o=this.options.clock.now(),a=Date.parse(D(e.deadline)??""),s=(Number.isFinite(a)?a:o+Oi)-o,l=this.options.clock.setTimeout(()=>{this.drainTimers.delete(l),this.onDrainDeadline(t,r,i).catch(c=>this.report_(c));},Math.max(0,Math.min(s,_a)));if(this.drainTimers.add(l),await this.refreshLease(true),this.closed||(this.withoutDrainingRelays(t),this.isHost))return;let d=this.links.get(this.hostPlayerId);if(d===void 0||!d.admitted)return this.reportRestart("skipped","announced");let m=await Ei(d.connection,t);if(!(this.closed||this.links.get(d.playerId)!==d)){if(m===false)return this.reportRestart("skipped","announced");await this.restartIceToHost(d,"announced")&&(i=d);}}async onDrainDeadline(e,t,r){if(this.closed||this.isHost)return;let i=this.links.get(this.hostPlayerId);if(i===void 0)return;let o=await Ei(i.connection,e);if(this.closed||this.links.get(i.playerId)!==i)return;(o===true||o===null&&i===t&&i!==r)&&this.dropLink(i,"relay_deadline",true);}withoutDrainingRelays(e){let t=this.lease;if(t===null||e.size===0)return;let r=false,i=[];for(let o of t.iceServers){let a=o.urls.filter(s=>{let l=Sa(s),d=l===null||!e.has(l);return r||=!d,d});a.length>0&&i.push({...o,urls:a});}r&&(this.lease={iceServers:i,expiresAt:t.expiresAt},this.reconfigureLinks());}restartIceToHost(e,t){if(e.restart!==void 0)return e.restart.done;if(e.connection.restartIce===void 0)return this.reportRestart("skipped",t),Promise.resolve(false);let r=()=>{},i=new Promise(s=>{r=s;}),o=this.options.clock.setTimeout(()=>a.settle(false),va),a={done:i,answered:false,settle:s=>{e.restart===a&&(e.restart=void 0,this.options.clock.clearTimeout(o),this.closed||this.reportRestart(s?"success":"failed",t),r(s));}};return e.restart=a,this.offerIceRestart(e).catch(s=>{this.warn(`could not restart ICE on the connection to the host: ${st(s)}`),a.settle(false);}),i}async offerIceRestart(e){let t=e.connection;t.restartIce?.();let r=await t.createOffer();await t.setLocalDescription(r);let i=t.localDescription?.sdp??r.sdp;if(!i)throw new w("invalid_sdp","the browser produced an offer with no SDP");se(i,"offer"),this.refuseMedia(i,"offer"),!(e.restart===void 0||this.links.get(e.playerId)!==e)&&(this.options.signaling.offer(i,"ice_restart"),this.mark("ice restart offered"));}progressRestart(e){let t=e.restart;if(t===void 0)return;let r=e.connection.iceConnectionState;r==="failed"||r==="closed"?t.settle(false):t.answered&&Vn(r)&&t.settle(true);}reportRestart(e,t){this.report([{type:"ice_restart_outcome_total",value:1,labels:{outcome:e,reason:t}}]);}async takeLease(e){this.leaseAskedAt=this.options.clock.now();let t=this.clusterTargetIds,r=await vi(this.options.api,this.options.matchId,t,e);this.lease={iceServers:r.iceServers,expiresAt:r.expiresAt},this.clusterTargetIds=t.slice(Math.max(0,t.indexOf(r.clusterTargetId)));}refreshLease(e){if(this.clusterTargetIds.length===0||this.closed)return Promise.resolve();if(this.leaseRenewal!==null)return this.leaseRenewal;let t=this.options.clock.now();if(this.leaseAskedAt!==null&&t-this.leaseAskedAt<ba)return Promise.resolve();let r=this.lease;if(!e&&r!==null&&r.expiresAt-t>=ya)return Promise.resolve();let i=this.takeLease().then(()=>{this.closed||(this.mark("turn re-leased"),this.reconfigureLinks());},o=>{this.leaseRenewalWarned||(this.leaseRenewalWarned=true,this.warn(`could not renew the TURN lease for this match, so connections keep the relays they were given: ${st(o)}`));}).finally(()=>{this.leaseRenewal=null;});return this.leaseRenewal=i,i}reconfigureLinks(){for(let e of this.links.values()){let t=e.connection;if(t.setConfiguration!==void 0)try{t.setConfiguration({...t.getConfiguration?.(),iceServers:this.iceServers()});}catch{}}}iceServers(){return (this.lease?.iceServers??[]).map(e=>({urls:[...e.urls],username:e.username,credential:e.credential}))}openLink(e,t){let i=(this.options.peerConnectionFactory??Ut)({iceServers:this.iceServers(),...this.options.certificate===void 0?{}:{certificates:[this.options.certificate]}}),o={};for(let p of it){let y=i.createDataChannel(p,jt[p]);y.binaryType="arraybuffer",o[p]=y;}let a={playerId:e,assignedPeerId:t,connection:i,channels:o,remoteFingerprint:null,admitted:false,pendingCandidates:[],detachHandshake:()=>{},detachAll:()=>{}},s=p=>{let y=p.candidate;y?.candidate&&this.options.signaling.candidate(e,y.candidate,y.sdpMid??void 0,y.sdpMLineIndex??void 0);},l=()=>this.onIceState(a),d=()=>this.onConnectionState(a),m=p=>{this.onHandshakeMessage(a,p.data).catch(y=>this.report_(y));},c=()=>{this.onReliableOpen(a).catch(p=>this.report_(p));};return i.addEventListener("icecandidate",s),i.addEventListener("iceconnectionstatechange",l),i.addEventListener("connectionstatechange",d),o.reliable.addEventListener("open",c),o.reliable.addEventListener("message",m),a.detachHandshake=()=>{o.reliable.removeEventListener("open",c),o.reliable.removeEventListener("message",m);},a.detachAll=()=>{a.detachHandshake(),i.removeEventListener("icecandidate",s),i.removeEventListener("iceconnectionstatechange",l),i.removeEventListener("connectionstatechange",d);},this.links.set(e,a),a}flushCandidates(e){let t=e.pendingCandidates;e.pendingCandidates=[];for(let r of t)e.connection.addIceCandidate(r).catch(()=>{});}async onReliableOpen(e){if(this.mark("channel open"),this.isHost)return;let t=this.credential;if(t===null)throw new w("peer_unauthenticated","the channel opened before the platform issued a join credential, so there is nothing to present");if(this.options.clock.now()>=t.expiresAt)throw new w("timeout","the join credential expired before the channel opened \u2014 reconnect for a fresh one");e.channels.reliable.send(Un({jwt:t.credential,protocolVersion:32769,assignedPeerId:e.assignedPeerId}));}async onHandshakeMessage(e,t){if(!(t instanceof ArrayBuffer))return;let r=Bn(t);if(r!==null){if(this.isHost&&r.kind==="join"){await this.admit(e,r.jwt,r.protocolVersion);return}if(!this.isHost&&r.kind==="admit"){e.assignedPeerId=r.assignedPeerId,this.finishAdmission(e),this.migration?.complete();return}!this.isHost&&r.kind==="reject"&&(this.fail(new w(Si(r.reason),ki(r.reason))),this.dropLink(e,"rejected",false));}}async admit(e,t,r){if(r!==32769){this.refuseLink(e,2,"version_mismatch",`it presented protocol version ${r}, not this SDK's ${32769} \u2014 an engine peer cannot join a browser-hosted match`);return}let i=ke(e.connection.localDescription?.sdp??"");if(i===null||e.remoteFingerprint===null){this.refuseLink(e,1,"fingerprint_unavailable","this connection's DTLS fingerprints could not be read from its SDP, so no credential can be checked against it");return}let o;try{o=await Nt(t,{matchId:this.options.matchId,gameId:this.options.gameId,hostEpoch:this.hostEpoch,peerFingerprint:e.remoteFingerprint,hostFingerprint:i,now:this.options.clock.now()},this.keys,this.replays,{issuer:this.options.baseUrl.replace(/\/+$/,"")});}catch(a){let s=a instanceof w&&a.reason==="keys_unavailable";this.refuseLink(e,s?131:1,s?"keys_unavailable":"credential_invalid",st(a)),s&&this.fail(a);return}if(o.playerId!==e.playerId.toLowerCase()){this.refuseLink(e,129,"credential_player_mismatch",`its credential was issued to ${o.playerId}, not to the player who signalled this connection`);return}if(this.migration?.isKicked(o.playerId)===true){this.refuseLink(e,130,"kicked",`it was kicked from this match at host epoch ${this.hostEpoch}`);return}e.channels.reliable.send(Wn({protocolVersion:32769,assignedPeerId:e.assignedPeerId})),this.finishAdmission(e);}refuseLink(e,t,r,i){this.warn(`refused to admit ${e.playerId}: ${i}`);try{e.channels.reliable.send(Hn(t));}catch{}this.dropLink(e,r,false);}finishAdmission(e){this.mark(this.isHost?"peer admitted":"admitted"),e.admitted=true,e.detachHandshake(),e.detachHandshake=()=>{},this.emitter.emit("peerJoined",this.expose(e)),this.settle();}onIceState(e){this.progressRestart(e);let t=e.connection.iceConnectionState;if(!(t!=="failed"&&t!=="disconnected"&&t!=="closed")){if(this.isHost){(t==="failed"||t==="closed")&&this.dropLink(e,t,true);return}if(t==="disconnected"){if(e.recovering===true)return;if(e.admitted&&e.connection.restartIce!==void 0&&this.reportedUnreachableEpoch!==this.hostEpoch){this.recoverHostLink(e).catch(r=>this.report_(r));return}}this.voteHostUnreachable(t);}}async recoverHostLink(e){let t=this.hostEpoch,r=()=>!this.closed&&this.hostEpoch===t&&this.links.get(e.playerId)===e;e.recovering=true;try{if(await this.refreshLease(!0),!r())return;if(Vn(e.connection.iceConnectionState))return this.reportRestart("skipped","disconnected");if(await this.restartIceToHost(e,"disconnected")||!r()||Vn(e.connection.iceConnectionState))return;this.voteHostUnreachable(Ci(e.connection.iceConnectionState));}finally{e.recovering=false;}}voteHostUnreachable(e){if(this.reportedUnreachableEpoch!==this.hostEpoch){this.reportedUnreachableEpoch=this.hostEpoch;try{this.options.signaling.reportHostUnreachable(this.hostEpoch,e);}catch(t){this.report_(t);}}}onConnectionState(e){let t=e.connection.connectionState;t!=="failed"&&t!=="closed"||(e.recovering===true&&this.voteHostUnreachable(Ci(e.connection.iceConnectionState)),this.dropLink(e,t,true));}expose(e){return {playerId:e.playerId,assignedPeerId:e.assignedPeerId,connection:e.connection,channels:e.channels,send:(t,r="reliable")=>e.channels[r].send(t)}}dropLink(e,t,r){this.links.delete(e.playerId),e.restart?.settle(false),e.detachAll();for(let i of it)Mi(e.channels[i]);Mi(e.connection),r&&e.admitted&&this.emitter.emit("peerLeft",e.playerId,t);}settle(){this.ready?.resolve(),this.ready=null;}fail(e){if(this.count(e.reason),this.ready!==null){let t=this.ready;this.ready=null,t.reject(e);return}this.emitter.emit("error",e);}report_(e){this.fail(e instanceof w?e:new w("connection_failed",st(e),{cause:e}));}count(e){this.counted||!ka.has(e)||(this.counted=true,this.report([{type:"connection_failures_total",value:1}]));}refuseMedia(e,t){if(Gt(e))throw new w("media_refused",`the ${t} carries an active audio or video section. This path carries data channels only; voice belongs to the platform voice service, and gem-api age-gates relayed SDP that proposes media.`)}mark(e){this.options.boot?.mark(e);}sleep(e){return new Promise(t=>this.options.clock.setTimeout(t,e))}warn(e){let t=this.options.log;t!==null&&(t??(r=>console.warn(r)))(`[gem] ${e}`);}get endedReason(){return this.ended}};function D(n){return typeof n=="string"&&n.length>0?n:null}function ot(n){return typeof n=="number"&&Number.isInteger(n)?n:null}function st(n){return n instanceof Error?n.message:String(n)}async function Ei(n,e){if(n.getStats===void 0)return null;let t=new Map;try{(await n.getStats()).forEach((s,l)=>{typeof s=="object"&&s!==null&&t.set(l,s);});}catch{return null}let r=[...t.values()],i=r.find(s=>s.type==="transport")?.selectedCandidatePairId,o=(typeof i=="string"?t.get(i):void 0)??r.find(s=>s.type==="candidate-pair"&&s.selected===true)??r.find(s=>s.type==="candidate-pair"&&s.nominated===true&&s.state==="succeeded");if(o===void 0)return null;let a=[o.localCandidateId,o.remoteCandidateId].map(s=>typeof s=="string"?t.get(s):void 0);return a.every(s=>s===void 0)?null:a.some(s=>{let l=s?.address??s?.ip;return typeof l=="string"&&e.has(l.toLowerCase())})}function Sa(n){let e=/^(?:turns?|stuns?):(?:\[([^\]]+)\]|([^:?/]+))/i.exec(n),t=e?.[1]??e?.[2];return t===void 0?null:t.toLowerCase()}function Vn(n){return n==="connected"||n==="completed"}function Ci(n){return n==="failed"||n==="closed"?n:"disconnected"}function Mi(n){try{n.close();}catch{}}var xa=100,Aa=50,De=class{constructor(e){this.options=e;}options;queue=[];drainHandle=null;dropped=0;closed=false;dropListener=null;onDropped(e){this.dropListener=e;}join(){this.assertUsable(),this.options.bridge.signal("join",this.options.matchId,{});}offer(e,t){this.assertUsable(),this.options.bridge.signal("offer",this.options.matchId,{sdp:e,intent:t});}answer(e,t,r){this.assertUsable(),this.options.bridge.signal("answer",this.options.matchId,{peerId:e,sdp:t,assignedPeerId:r});}candidate(e,t,r,i){if(this.assertUsable(),this.queue.length>=(this.options.maxQueuedCandidates??xa)){this.dropped+=1;return}this.queue.push({peerId:e,candidate:t,...r===void 0?{}:{sdpMid:r},...i===void 0?{}:{sdpMLineIndex:i}}),this.schedule();}migrationAck(e){this.assertUsable(),this.options.bridge.signal("migrationAck",this.options.matchId,{hostEpoch:e});}reportHostUnreachable(e,t){this.assertUsable(),this.options.bridge.signal("hostUnreachable",this.options.matchId,{hostEpoch:e,evidence:{iceState:t,lastSeenAt:Math.floor(this.options.clock.now()/1e3)}});}discardQueued(){this.queue.length=0;}close(){this.closed=true,this.discardQueued(),this.drainHandle!==null&&(this.options.clock.clearTimeout(this.drainHandle),this.drainHandle=null);}assertUsable(){if(this.closed)throw new w("signaling_unavailable","this signaling client has been closed");if(!this.options.bridge.parentSupports("p2p"))throw new w("signaling_unavailable","the page embedding this game did not advertise the `p2p` capability, so there is nothing to carry signaling. P2P needs the notification WebSocket, and only the parent can hold one \u2014 see docs/p2p-and-parties.md \xA72.")}schedule(){this.drainHandle!==null||this.closed||(this.drainHandle=this.options.clock.setTimeout(()=>{this.drainHandle=null;let e=this.queue.shift();if(e!==void 0){let{peerId:t,candidate:r,sdpMid:i,sdpMLineIndex:o}=e;this.options.bridge.signal("ice",this.options.matchId,{peerId:t,candidate:r,...i===void 0?{}:{sdpMid:i},...o===void 0?{}:{sdpMLineIndex:o}});}if(this.dropped>0&&this.queue.length===0){let t=this.dropped;this.dropped=0,this.options.onCandidatesDropped?.(t),this.dropListener?.(t);}this.queue.length>0&&this.schedule();},this.options.candidateIntervalMs??Aa));}};var Jn="webrtc_p2p";function Pa(n){let e=new Oe({client:n.client,gameId:n.gameId}),t=new Map;return {handleSignalEvent(r,i,o){t.get(r)?.handleSignal(i,o);},session(r){return t.get(r)??null},async connect(r={}){if(n.bridge===null)throw new w("signaling_unavailable","P2P signaling rides the notification WebSocket, which only the page embedding this game can hold. There is no bridge here \u2014 this is `mock` mode, or a game running outside the Arcade frame. See docs/p2p-and-parties.md \xA72.");let i=H(r.clusterTargetId),o=i===void 0?await Ia(n):[i];n.rooms.isHost&&n.rooms.currentMatch===null&&n.rooms.currentRoom?.matchId===void 0&&await n.rooms.startMatch({...i===void 0?{}:{clusterTargetId:i},...r.signal===void 0?{}:{signal:r.signal}});let a=await n.rooms.awaitMatch({...r.timeoutMs===void 0?{}:{timeoutMs:r.timeoutMs},...r.signal===void 0?{}:{signal:r.signal}});if(a.protocol!==Jn)throw new w("unsupported_protocol",`match ${a.matchId} speaks '${a.protocol}', not '${Jn}'. Use \`dedicatedServer.connect()\` for a dedicated match; the two flows share WebRTC and nothing else.`);let s=new De({bridge:n.bridge,matchId:a.matchId,clock:n.clock}),l=new Le({api:e,clock:n.clock,matchId:a.matchId,gameId:n.gameId(),baseUrl:typeof n.baseUrl=="function"?n.baseUrl():n.baseUrl,signaling:s,...o.length===0?{}:{clusterTargetIds:o},...r.peerConnectionFactory===void 0?{}:{peerConnectionFactory:r.peerConnectionFactory},certificate:r.certificate??await Wt(),...r.timeoutMs===void 0?{}:{connectTimeoutMs:r.timeoutMs},...n.boot===void 0?{}:{boot:n.boot},...n.log===void 0?{}:{log:n.log}});s.onDropped(d=>l.notifyCandidatesDropped(d)),t.set(a.matchId,l),l.on("matchEnded",()=>t.delete(a.matchId));try{await l.start(r.signal);}catch(d){throw t.delete(a.matchId),l.close(),d}return l}}}async function Ia(n){if(n.resolveClusterTargets!==void 0)return V(await n.resolveClusterTargets());let e=H(await n.resolveClusterTarget?.());return e===void 0?[]:[e]}function Gi(n){return typeof n=="string"&&n.trim()!==""}function Li(n,e){return Gi(n)?n.trim():Gi(e)?e:null}function at(n){let e=String(n);if(!/^[A-Za-z0-9_.-]{1,128}$/.test(e)||e==="."||e==="..")throw new TypeError(`${JSON.stringify(e)} is not a usable path segment \u2014 it is interpolated into a URL`);return encodeURIComponent(e)}async function Di(n,e,t){let r=`/v1/games/${at(n.gameId())}/matchmaking/tickets/${at(e)}`;return n.client.delete(r,t?.signal)}async function qi(n,e,t){let r=`/v1/games/${at(n.gameId())}/matchmaking/tickets`;return n.client.post(r,e,t?.signal)}async function $i(n,e,t){let r=`/v1/games/${at(n.gameId())}/matchmaking/tickets/${at(e)}/heartbeat`;return n.client.post(r,void 0,t?.signal)}function lt(n){let e=n??{};if(typeof e.ticket_id!="string"||e.ticket_id.length===0)throw new Error("[gem] a matchmaking-ticket response carried no id");let t=e.heartbeat_interval_seconds;return {id:e.ticket_id,status:typeof e.status=="string"?e.status:"queued",...typeof e.reason=="string"?{reason:e.reason}:{},...typeof e.game_room_id=="string"?{roomId:e.game_room_id}:{},partySize:typeof e.party_size=="number"?e.party_size:1,queuedAt:Fi(e.queued_at),expiresAt:Fi(e.expires_at),heartbeatIntervalSeconds:typeof t=="number"&&t>0?t:0}}function Fi(n){if(typeof n!="string")return 0;let e=Date.parse(n);return Number.isFinite(e)?Math.floor(e/1e3):0}var Ta=3,Ni=1e3,Ht=class{constructor(e){this.options=e;this.snapshot=e.first;let t=new Promise((o,a)=>{this.resolve=o,this.reject=a;});t.catch(()=>{});let r=()=>this.snapshot,i=()=>this.status;this.ticket={get id(){return r().id},get status(){return i()},get partySize(){return r().partySize},get queuedAt(){return r().queuedAt},get expiresAt(){return r().expiresAt},outcome:t,cancel:()=>(this.requestCancel(),t)};}options;ticket;snapshot;status="queued";timer=null;busy=false;again=false;cancelRequested=false;soon=false;misses=0;settled=false;resolve;reject;start(){this.apply(this.snapshot);}poke(){this.settled||(this.soon=true,!this.busy&&(this.clearTimer(),this.tick()));}abandon(e=false){this.settled||(this.options.cancel(this.snapshot.id,e).catch(()=>{}),this.settle({status:"cancelled",reason:"requested"}));}requestCancel(){this.settled||(this.cancelRequested=true,this.trigger());}trigger(){if(this.busy){this.again=true;return}this.clearTimer(),this.tick();}schedule(e=false){if(this.settled)return;this.clearTimer();let t=e?Ni:Math.max(Ni,this.snapshot.heartbeatIntervalSeconds*1e3);this.timer=this.options.clock.setTimeout(()=>{this.timer=null,this.tick();},t);}clearTimer(){this.timer!==null&&(this.options.clock.clearTimeout(this.timer),this.timer=null);}async tick(){if(this.settled||this.busy)return;this.busy=true,this.again=false;let e=null,t=null;try{e=this.cancelRequested?await this.options.cancel(this.snapshot.id):await this.options.heartbeat(this.snapshot.id);}catch(r){t=r;}if(this.busy=false,!this.settled){if(e===null){this.missed(t);return}this.misses=0,await this.apply(e);}}missed(e){if(e instanceof q&&e.reason==="not_found"){this.settle({status:"lost"});return}if(this.misses+=1,this.misses<Ta){this.next();return}this.options.cancel(this.snapshot.id).catch(()=>{});let t=e instanceof q&&e.code==="matchmaking_unavailable";this.settle({status:t?"unavailable":"lost"});}async apply(e){if(this.snapshot=e,e.status==="queued"){this.next();return}if(e.status==="matched"){await this.seat(e);return}e.status==="expired"?this.settle({status:"expired",reason:e.reason??"timed_out"}):e.status==="cancelled"?this.settle({status:"cancelled",reason:e.reason??"requested"}):this.settle({status:"failed",reason:e.reason??e.status});}next(){if(this.again){this.again=false,this.tick();return}let e=this.soon;this.soon=false,this.schedule(e);}async seat(e){if(e.roomId===void 0){this.fail(new Error("[gem] a matched matchmaking ticket named no game room"));return}this.busy=true;try{let t=await this.options.seat(e.roomId);this.busy=!1,this.settle({status:"matched",room:t});}catch(t){this.busy=false,this.fail(t);}}settle(e){this.settled||(this.finish(e.status),this.resolve(e));}fail(e){this.settled||(this.finish("matched"),this.reject(e));}finish(e){this.settled=true,this.status=e,this.clearTimer(),this.options.onSettled();}};function ji(n){let e=typeof n=="object"&&n!==null?n.play_with:void 0;return e==null?null:e==="joined"?"joined":"unavailable"}function Z(n){return typeof n=="string"&&n!==""?n:void 0}var Ea=new Set(["localhost","127.0.0.1","[::1]"]);function qe(n){return Ca(n)}function Ca(n){let e=Z(n);if(e===void 0)return;let t;try{t=new URL(e);}catch{return}if(!(t.username!==""||t.password!=="")&&(t.protocol==="https:"||t.protocol==="http:"&&Ea.has(t.hostname)))return e}var Ma=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,Oa=/^[a-z][a-z0-9_]{0,31}$/;function Ga(n){if(!Array.isArray(n))return;let e=[],t=new Set;for(let r of n.slice(0,8)){let i=r??{},o=Z(i.family),a=Z(i.avatar_3d_id),s=qe(i.model_url),l=qe(i.icon_url);o===void 0||!Oa.test(o)||t.has(o)||a===void 0||!Ma.test(a)||s===void 0||l===void 0||(t.add(o),e.push({family:o,id:a,modelUrl:s,iconUrl:l}));}return e}function La(n){if(!Array.isArray(n))return [];let e=[];for(let t of n){let r=t??{},i=Z(r.item_id),o=qe(r.icon_url);i!==void 0&&o!==void 0&&e.push({itemId:i,iconUrl:o});}return e}function Ui(n){let e=n??{},t=Z(e.player_id),r=Z(e.display_name),i=Z(e.discriminator),o=Z(e.display_tag),a=Z(e.avatar_icon_id),s=qe(e.avatar_icon_url),l=Z(e.avatar_3d_id),d=qe(e.avatar_3d_model_url),m=qe(e.avatar_3d_icon_url),c=Ga(e.avatars_3d);return {...t===void 0?{}:{playerId:t},...r===void 0?{}:{displayName:be(r,"displayName")},...i===void 0?{}:{discriminator:i},...o===void 0?{}:{displayTag:be(o,"displayTag")},...a===void 0?{}:{avatarIconId:a},...s===void 0?{}:{avatarIconUrl:s},...l===void 0?{}:{avatar3dId:l},...d===void 0?{}:{avatar3dModelUrl:d},...m===void 0?{}:{avatar3dIconUrl:m},...c===void 0?{}:{avatars3d:c},badges:La(e.badges)}}function Bt(n){if(typeof n!="object"||n===null)return;let e={};for(let[t,r]of Object.entries(n)){let i=zn(r);i!==void 0&&(e[t]=i);}return e}function Vt(n){if(Array.isArray(n))return n.map(Da).filter(e=>!!e)}function zn(n){if(Xn(n)||Array.isArray(n)&&n.every(Xn))return n}function Xn(n){return typeof n=="string"||typeof n=="boolean"||typeof n=="number"&&Number.isFinite(n)}function Da(n){let e=n??{};if(typeof e.name!="string"||e.name.length===0)return null;let t=zn(e.fixed),r=zn(e.default);return {name:e.name,type:typeof e.type=="string"?e.type:"string",list:e.list===true,required:e.required===true,mutable:e.mutable!==false,...t===void 0?{}:{fixed:t},...r===void 0?{}:{default:r},...$e("description",qa(e.description)),...Array.isArray(e.allowed)&&e.allowed.every(Xn)?{allowed:e.allowed}:{},...$e("min",dt(e.min)),...$e("max",dt(e.max)),...$e("minItems",dt(e.min_items)),...$e("maxItems",dt(e.max_items)),...$e("maxLength",dt(e.max_length))}}function qa(n){return typeof n=="string"&&n.length>0?n:void 0}function dt(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}function $e(n,e){return e===void 0?{}:{[n]:e}}function Y(n){let e=n??{},t=C(e.id)??C(e.game_room_id);if(t===void 0)throw new Error("[gem] a game-room response carried no id");let r=Array.isArray(e.members)?e.members.map(Yn):[];return {id:t,gameId:C(e.game_id)??"",name:C(e.name)??"",status:C(e.status)??"open",hostPlayerId:C(e.host_player_id)??"",maxPlayers:ee(e.max_players)??0,memberCount:ee(e.member_count)??r.length,members:r,isPublic:e.is_public!==false,...J("joinCode",C(e.join_code)),...e.settings===void 0?{}:{settings:e.settings},...J("roomArgs",Bt(e.room_args)),...J("roomArgsTemplate",Vt(e.room_args_template)),gameChannel:C(e.game_channel)??"release",...J("region",C(e.region)),...J("clusterTargetId",C(e.cluster_target_id)),selfHosted:e.self_hosted===true,...J("matchId",C(e.match_id)),version:ee(e.version)??0,...J("templateId",C(e.template_id)),...J("templateSlug",C(e.template_slug)),roomType:C(e.game_room_type)??"Standard",...J("transports",Qe(e.transports)??void 0),createdAt:ee(e.created_at)??0,updatedAt:ee(e.updated_at)??0,...J("startedAt",ee(e.started_at))}}function Yn(n){let e=n??{};return {...Ui(e),playerId:C(e.player_id)??"",isReady:e.is_ready===true,role:C(e.role)??"member",joinedAt:ee(e.joined_at)??0,...J("groupId",C(e.group_id)),...J("platform",C(e.platform))}}function ct(n){let e=n??{};return {id:C(e.id)??C(e.game_room_id)??"",gameId:C(e.game_id)??"",name:C(e.name)??"",status:C(e.status)??"open",hostPlayerId:C(e.host_player_id)??"",maxPlayers:ee(e.max_players)??0,memberCount:ee(e.member_count)??0,isPublic:e.is_public!==false,gameChannel:C(e.game_channel)??"release",roomType:C(e.game_room_type)??"Standard",createdAt:ee(e.created_at)??0}}function C(n){return typeof n=="string"&&n.length>0?n:void 0}function ee(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}function J(n,e){return e===void 0?{}:{[n]:e}}var Fe=class{constructor(e){this.options=e;}options;list(e){let t=new URLSearchParams;e.status&&t.set("status",e.status),e.templateSlug&&t.set("template_slug",e.templateSlug),e.limit!==void 0&&t.set("limit",String(e.limit)),e.cursor&&t.set("cursor",e.cursor);let r=t.size>0?`?${t.toString()}`:"";return this.options.client.get(`${this.baseV1()}${r}`,e.signal).then(i=>({rooms:(i.game_rooms??[]).map(ct),...typeof i.next_cursor=="string"?{nextCursor:i.next_cursor}:{}}))}myRooms(e){return this.options.client.get(`/v1/games/${encodeURIComponent(this.options.gameId())}/players/me/game-rooms`,e).then(t=>({rooms:(t.game_rooms??[]).map(ct)}))}create(e,t,r){return Fa(e.maxPlayers),this.options.client.post(this.baseV2(),{name:e.name,...e.isPublic===void 0?{}:{is_public:e.isPublic},...e.settings===void 0?{}:{settings:e.settings},...e.region===void 0?{}:{region:e.region},...r===void 0?{}:{cluster_target_id:r},...e.templateId===void 0?{}:{template_id:e.templateId},...e.templateSlug===void 0?{}:{template_slug:e.templateSlug},...e.partyId===void 0?{}:{party_id:e.partyId},...this.asksSelfHosted(e.partyId)?{self_hosted:true}:{},...e.roomArgs===void 0?{}:{room_args:e.roomArgs},...ut(t)},e.signal).then(Y)}get(e,t){return this.options.client.get(this.roomV2(e),t).then(Y)}join(e,t,r){return this.options.client.post(`${this.roomV2(e)}/join`,{...t.partyId===void 0?{}:{party_id:t.partyId},...ut(r)},t.signal).then(Y)}joinByCode(e,t,r){return this.options.client.post(`${this.baseV2()}/join`,{join_code:e,...t.partyId===void 0?{}:{party_id:t.partyId},...ut(r)},t.signal).then(Y)}quickJoin(e,t,r=[]){return Qn(this.options,e,t,r).then(i=>i.room)}createTicket(e,t,r=[]){return qi(this.generated(),{templates:e.templateSlugs.map(i=>({template_slug:i})),...r.length===0?{}:{cluster_target_ids:[...r]},...e.partyId===void 0?{}:{party_id:e.partyId},...ut(t)},{...e.signal===void 0?{}:{signal:e.signal}}).then(lt)}heartbeatTicket(e){return $i(this.generated(),e).then(lt)}cancelTicket(e,t=false){return t?this.options.client.delete(`/v1/games/${encodeURIComponent(this.options.gameId())}/matchmaking/tickets/${encodeURIComponent(e)}`,void 0,{keepalive:true}).then(lt):Di(this.generated(),e).then(lt)}leave(e,t,r,i=false){return this.options.client.post(`${this.roomV2(e)}/leave`,{leave_as_group:t},r,{keepalive:i}).then(()=>{})}asksSelfHosted(e){return Hi(this.options,e)}close(e,t){return this.options.client.delete(this.roomV1(e),t).then(()=>{})}update(e,t){return this.options.client.patch(this.roomV2(e),{...t.name===void 0?{}:{name:t.name},...t.maxPlayers===void 0?{}:{max_players:t.maxPlayers},...t.isPublic===void 0?{}:{is_public:t.isPublic},...t.settings===void 0?{}:{settings:t.settings},...t.roomArgs===void 0?{}:{room_args:t.roomArgs}},t.signal).then(Y)}setReady(e,t,r){return this.options.client.post(`${this.roomV2(e)}/ready`,{is_ready:t},r).then(Y)}kick(e,t,r){return this.options.client.post(`${this.roomV1(e)}/kick/${encodeURIComponent(t)}`,{},r).then(()=>this.get(e,r))}transferHost(e,t,r){return this.options.client.post(`${this.roomV2(e)}/transfer/${encodeURIComponent(t)}`,{},r).then(Y)}leaveMatch(e,t){return this.options.client.post(`/v1/games/${encodeURIComponent(this.options.gameId())}/matches/${encodeURIComponent(e)}/leave`,{},void 0,{keepalive:t}).then(()=>{})}generated(){return {client:this.options.client,gameId:this.options.gameId}}baseV2(){return Wi(this.options)}baseV1(){return `/v1/games/${encodeURIComponent(this.options.gameId())}/game-rooms`}roomV2(e){return `${this.baseV2()}/${encodeURIComponent(e)}`}roomV1(e){return `${this.baseV1()}/${encodeURIComponent(e)}`}};function Wi(n){return `/v2/games/${encodeURIComponent(n.gameId())}/game-rooms`}function Hi(n,e){return e===void 0&&n.selfHosted?.()===true}function Qn(n,e,t,r=[],i){let o=Hi(n,e.partyId);return n.client.post(`${Wi(n)}/quick-join`,{...e.templateId===void 0?{}:{template_id:e.templateId},...e.templateSlug===void 0?{}:{template_slug:e.templateSlug},...$a(e.templateSlugs,o),...e.region===void 0?{}:{region:e.region},...r.length===0?{}:{cluster_target_ids:[...r]},...e.name===void 0?{}:{name:e.name},...e.partyId===void 0?{}:{party_id:e.partyId},...o?{self_hosted:true}:{},...i===void 0?{}:{play_with_ticket:i},...ut(t)},e.signal).then(a=>({room:Y(a),playWith:i===void 0?null:ji(a)}))}function $a(n,e){return n===void 0?{}:{templates:(e?n.slice(0,1):n).map(r=>({template_slug:r}))}}function ut(n){return {platform:n.platform,game_version:n.gameVersion,engine_version:n.engineVersion}}function Fa(n){n!==void 0&&console.warn(`[gem] createRoom: maxPlayers (${n}) is deprecated and was not sent. A room's seats come from the room template published in your game config \u2014 name one with templateSlug \u2014 and a room created without a template seats 8. To change a room that already exists, call update({ maxPlayers }) as its host.`);}var Ne=class{listeners=new Map;retainedMatch=null;on(e,t){let r=this.listeners.get(e);if(r||(r=new Set,this.listeners.set(e,r)),r.add(t),e==="matchReady"&&this.retainedMatch!==null){let i=this.retainedMatch;queueMicrotask(()=>{this.listeners.get(e)?.has(t)&&this.retainedMatch===i&&t(i);});}return ()=>this.off(e,t)}hasListeners(e){return (this.listeners.get(e)?.size??0)>0}off(e,t){this.listeners.get(e)?.delete(t);}once(e,t){let r=this.on(e,((...i)=>{r(),t(...i);}));return r}emit(e,...t){e==="matchReady"&&(this.retainedMatch=t[0]);let r=this.listeners.get(e);if(r)for(let i of [...r])i(...t);}get retained(){return this.retainedMatch}retain(e){this.retainedMatch=e;}clearRetained(){this.retainedMatch=null;}clear(){this.listeners.clear(),this.retainedMatch=null;}};var Kt=class{constructor(e){this.options=e;let t=e.first,r=new Promise(s=>{this.resolve=s;}),i=()=>this.status;this.ticket={id:t.ticketId,get status(){return i()},partySize:t.partySize??0,queuedAt:t.queuedAt??0,expiresAt:t.expiresAt??0,outcome:r};let o=t.queuedAt!==void 0&&t.expiresAt!==void 0?(t.expiresAt-t.queuedAt)*1e3:9e5,a=Math.min(Math.max(o,0),9e5)+5e3;this.timer=e.clock.setTimeout(()=>{this.timer=null,this.end({status:"lost"});},a);}options;ticket;status="queued";timer=null;resolve;get id(){return this.ticket.id}end(e){this.status==="queued"&&(this.status=e.status,this.timer!==null&&(this.options.clock.clearTimeout(this.timer),this.timer=null),this.options.onSettled(),this.resolve(e));}};function Na(n,e){return Math.min(e,500*2**Math.max(0,n-1))}var ja=1e3,Ua=3e3,Wa=6e4,Ha=2,Bi=new Set(["game_room_closed","game_room_gone"]),Ba=12e4,Va=5e3,Ka=new Set(["invalid_cluster_target","capacity_unavailable"]),Ja=new Set(["already_in_matching_room","game_room_limit_per_game","game_room_limit_global"]);function zi(n){return new er(n)}var er=class{constructor(e,t={}){this.options=e;this.internals=t;this.autoFollow=e.followMatch!==false,this.apiOptions={client:e.client,gameId:()=>this.requireGameId(),selfHosted:()=>e.localHostAvailable?.()===true},this.api=new Fe(this.apiOptions),this.installUnloadNotifier(),this.installResumeWatch(),this.retuneSeatWatch();}options;internals;emitter=new Ne;api;apiOptions;playWithAnswer=null;room=null;ticketWatch=null;queueing=false;partyWatch=null;heldQueued=null;queuedFromRoomId=null;session={phase:"none"};seenMatches=new Set;refusedMatches=new Set;reconciling=null;reconcileAgain=false;pendingHint=null;settling=null;pollHandle=null;polling=false;pollIntervalMs=null;pinnedIntervalMs=null;lastPushAt=-1/0;inFlight=[];unloadListener=null;visibilityListener=null;seatPollHandle=null;reported=new Set;disposed=false;followAbort=null;followingMatchId=null;autoFollow;reconnect=null;startsInFlight=0;autoStartRetried=false;autoStartRetry=null;async create(e){this.assertNotInRoom("create"),this.assertNotQueued("create");let t=this.clusterTargets(),r=await this.attachParty(e),i=await this.run("create",async()=>{let o=await t,a=s=>this.api.create(r,this.version("create"),s);for(let s=0;;s++)try{return await a(o[s])}catch(l){if(!Zn(l))throw l;if(s+1<o.length)continue;let d=(await this.afterRefusedClusters(l,o)).find(m=>!o.includes(m));if(d===void 0)throw l;return a(d)}});return this.enter(i,"created"),this.askForLocalServer(i),i}async join(e,t={}){this.assertNotInRoom("join"),this.assertNotQueued("join");let r=await this.attachParty(t),i=await this.run("join",()=>this.api.join(e,r,this.version("join")));return this.enter(i,"joined"),this.askForLocalServer(i),i}async joinByCode(e,t={}){this.assertNotInRoom("joinByCode"),this.assertNotQueued("joinByCode");let r=await this.attachParty(t),i=await this.run("joinByCode",()=>this.api.joinByCode(e,r,this.version("joinByCode")));return this.enter(i,"joined"),this.askForLocalServer(i),i}async quickJoin(e){this.assertNotInRoom("quickJoin"),this.assertNotQueued("quickJoin"),Ya(e);let t=this.clusterTargets(),r=await this.attachParty(e),i=s=>this.postQuickJoin(r,s).catch(l=>this.retryPastStaleMembership(l,r,s)),o=await this.run("quickJoin",async()=>{let s=await t;return i(s).catch(async l=>{let d=await this.afterRefusedClusters(l,s);if(d.length===0||Ji(d,s))throw l;return i(d)})}),{room:a}=o;return this.playWithAnswer=o.playWith,this.enter(a,"joined"),this.askForLocalServer(a),a}async queueForMatch(e){this.assertNotQueued("queueForMatch"),Xa(e?.templateSlugs),this.queueing=true;try{let t=this.clusterTargets(),r=await this.attachParty(e),i=await this.run("queueForMatch",async()=>{let a=await t;return this.api.createTicket(r,this.version("queueForMatch"),a).catch(async s=>{let l=await this.afterRefusedClusters(s,a);if(l.length===0||Ji(l,a))throw s;return this.api.createTicket(r,this.version("queueForMatch"),l)})});this.heldQueued?.ticketId===i.id&&(this.heldQueued=null),this.partyWatch?.end({status:"lost"});let o=new Ht({clock:this.options.clock,first:i,heartbeat:a=>this.track(this.api.heartbeatTicket(a)),cancel:(a,s)=>this.track(this.api.cancelTicket(a,s)),seat:a=>this.enterMatchedRoom(a),onSettled:()=>{this.ticketWatch===o&&(this.ticketWatch=null),this.retuneSeatWatch();}});return this.ticketWatch=o,this.retuneSeatWatch(),o.start(),o.ticket}finally{this.queueing=false;let t=this.heldQueued;this.heldQueued=null,t!==null&&this.showPartyTicket(t);}}async enterMatchedRoom(e){let t=await this.run("queueForMatch",()=>this.api.get(e));return this.disposed?t:(this.room!==null&&this.room.id!==t.id&&(this.queuedFromRoomId=this.room.id,this.exit()),this.room===null&&this.enter(t,"joined"),this.room?.id===t.id?this.room:t)}async list(e={}){let t=await this.run("list",()=>this.api.list(e));return this.emitter.emit("listed",t),t}async myRooms(e){let t=await this.run("myRooms",()=>this.api.myRooms(e));return this.emitter.emit("mineListed",t.rooms),t.rooms}async leave(e={}){let t=this.requireRoom("leave"),r=e.keepalive===true,i=()=>this.run("leave",()=>this.api.leave(t.id,e.asGroup!==false,e.signal,r));if(r){let o=i();this.track(this.notifyLeavingMatch({keepalive:true})),await o;}else await this.notifyLeavingMatch(),await i();this.exit(),this.emitter.emit("left");}async leaveAll(e){let t=await this.myRooms(e),r=[];for(let i of t)try{await this.run("leaveAll",()=>this.api.leave(i.id,!0,e)),r.push(i.id),this.room?.id===i.id&&(this.exit(),this.emitter.emit("left"));}catch{}return r}async close(e){let t=this.requireRoom("close");await this.run("close",()=>this.api.close(t.id,e)),this.exit(),this.emitter.emit("closed");}async refresh(e){let t=this.room;if(!t)return null;let r;try{r=await this.track(this.api.get(t.id,e));}catch(i){if(i instanceof q&&i.reason==="not_found"){let o=this.heldMatchId;if(o!==null){let a=await this.askWhyEnded(o,e);this.endMatch(o,a??"game_room_gone",false);}return this.exit(),this.emitter.emit("closed"),null}throw this.reportError("refresh",i),i}return this.apply(r),await this.settleHeldMatch(e),this.room}async settleHeldMatch(e){let t=this.room,r=this.heldMatchId;if(!(t===null||r===null)&&!(t.status==="in_game"&&t.matchId===r)&&!(t.matchId!==void 0&&t.matchId!==r)&&this.settling!==r){this.settling=r;try{let i=await this.askWhyEnded(r,e);i!==null&&this.endMatch(r,i,t.status!=="closed");}finally{this.settling===r&&(this.settling=null);}}}async askWhyEnded(e,t){let r=this.room;if(r===null)return null;let i=this.takeHint(e),o;try{o=await this.track(this.onceGameIdKnown(()=>gn({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:r.id,matchId:e,version:this.version("reconcile"),knownToExist:this.seenMatches.has(e),...t===void 0?{}:{signal:t}})));}catch(a){return a instanceof q&&a.reason==="not_found"?i??null:(this.reportError("reconcile",a),null)}return o===null?null:o.status==="ready"?(this.seenMatches.add(e),null):o.status==="failed"?(this.settleFailed(o.matchId,o.reason),null):o.reason==="unknown"&&i!==null?i:o.reason}takeHint(e){let t=this.pendingHint;if(t===null||t.matchId!==void 0&&t.matchId!==e||(this.pendingHint=null,t.reason===void 0))return null;let r=Ce(t.reason);return r==="unknown"?null:r}async reconcile(e){if(this.disposed)return;if(e!==void 0&&(e.matchId!==void 0||e.reason!==void 0)&&(this.pendingHint=e),this.reconciling!==null)return this.reconcileAgain=true,this.reconciling;let t=this.reconcileOnce().finally(()=>{this.reconciling=null,this.reconcileAgain&&(this.reconcileAgain=false,this.track(this.reconcile().catch(()=>{})));});return this.reconciling=t,this.track(t)}async reconcileOnce(){if(this.room===null){await this.reconcileSeats();return}await this.refresh().catch(()=>null);}async setReady(e,t){let r=this.requireRoom("setReady"),i=await this.run("setReady",()=>this.api.setReady(r.id,e,t));return this.apply(i),i}async kick(e,t){let r=this.requireRoom("kick"),i=await this.run("kick",()=>this.api.kick(r.id,e,t));return this.apply(i),i}async transferHost(e,t){let r=this.requireRoom("transferHost"),i=await this.run("transferHost",()=>this.api.transferHost(r.id,e,t));return this.apply(i),i}async update(e){let t=this.requireRoom("update"),r=await this.run("update",()=>this.api.update(t.id,e));return this.apply(r),r}startMatch(e={}){return this.requestStart(e,true)}async requestStart(e,t){let r=this.requireRoom("startMatch");if(!this.isHost)throw new u("misconfigured",`only the host of room ${r.id} can start a match, and this player is not it. Every other member waits for \`matchAllocationStarted\` instead \u2014 that is the whole point of the room carrying the match.`);this.startsInFlight++;try{return await this.startIn(r,e,t)}finally{this.startsInFlight--;}}async startIn(e,t,r){let i=H(t.clusterTargetId),o=i??(e.clusterTargetId===void 0?(await this.clusterTargets())[0]:void 0),a=o??e.clusterTargetId;await this.options.ensureLocalServer?.(e.id);let s=(m,c)=>kt({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:e.id,...m===void 0?{}:{clusterTargetId:m},...i===void 0?{nextClusterTarget:()=>this.nextCluster(c)}:{},retry:{...t.retry,onWaiting:p=>{this.emitter.emit("matchStartWaiting",p),t.retry?.onWaiting?.(p);}},...t.signal===void 0?{}:{signal:t.signal}}),l=()=>s(o,[a]).catch(async m=>{if(i!==void 0||!Zn(m))throw m;let c=await this.nextCluster([a],true);if(c===void 0)throw m;return s(c,[a,c])}),d=await(r?this.run("startMatch",l):this.track(this.onceGameIdKnown(l)));return this.beginMatch(d.matchId),d}async awaitMatch(e={}){if(this.session.phase==="suspended")throw new u("misconfigured","this match is suspended \u2014 the transport dropped and nobody has decided to reconnect yet. Call reconnectToMatch(), or dedicatedServer.connect(), which does it for you.");let t=this.emitter.retained;return t!==null&&this.session.phase==="ready"?t:this.pollMatchStatus(e)}async pollMatchStatus(e={}){let t=this.requireRoom("pollMatchStatus"),r=await this.matchIdOrWait(e),i=await this.run("pollMatchStatus",()=>this.chase(t.id,r,e));return this.settleOutcome(i)}chase(e,t,r){return St({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:e,matchId:t,version:this.version("pollMatchStatus"),knownToExist:this.seenMatches.has(t),...r.timeoutMs===void 0?{}:{timeoutMs:r.timeoutMs},...r.pollIntervalMs===void 0?{}:{pollIntervalMs:r.pollIntervalMs},...r.signal===void 0?{}:{signal:r.signal}})}settleOutcome(e){if(e.status==="failed")throw this.settleFailed(e.matchId,e.reason),new u("allocation_failed",`match ${e.matchId} did not come up: ${e.reason}`);if(e.status==="ended")throw this.endMatch(e.matchId,e.reason,!Bi.has(e.reason)),this.track(this.reconcile().catch(()=>{})),new u("match_ended",`match ${e.matchId} ended: ${e.reason}`);return this.seenMatches.add(e.matchId),this.emitter.retained?.matchId===e.matchId?this.emitter.retain(e):this.emitter.emit("matchReady",e),this.heldMatchId===e.matchId&&this.session.phase!=="connected"&&(this.session={phase:"ready",matchId:e.matchId}),e}settleFailed(e,t){this.heldMatchId===e&&(this.abandonReconnect(e,t),this.emitter.emit("matchFailed",e,t),this.abortFollow(),this.refusedMatches.add(e),this.session={phase:"none"},this.emitter.clearRetained());}endMatch(e,t,r){this.heldMatchId===e&&(this.abortFollow(),this.session={phase:"ended",matchId:e,reason:t,roomReopened:r},this.emitter.clearRetained(),this.emitter.emit("matchEnded",{matchId:e,reason:t,roomReopened:r}),this.abandonReconnect(e,t));}armReconnect(e,t){let r=this.options.autoReconnect;if(r===void 0||r.attempts<=0||r.budgetMs<=0)return;if(this.finishReconnect(),t==="kicked"){r.abandon(e,t);return}let i={matchId:e,cause:t,attempt:0,deadlineAt:this.options.clock.now()+r.budgetMs,parkedAt:null,timer:null,parked:null,done:false};this.reconnect=i,this.track(this.reconnectStep(i));}park(e,t){e.parkedAt=this.options.clock.now(),e.parked=()=>(e.parkedAt!==null&&(e.deadlineAt+=this.options.clock.now()-e.parkedAt,e.parkedAt=null),t());}reconnecting(e){return !this.disposed&&!e.done&&this.reconnect===e&&this.session.phase==="suspended"&&this.session.matchId===e.matchId}finishReconnect(){let e=this.reconnect;e!==null&&(e.done=true,e.timer!==null&&(this.options.clock.clearTimeout(e.timer),e.timer=null),e.parked=null,this.reconnect=null);}abandonReconnect(e,t){let r=this.reconnect;r===null||r.matchId!==e||(this.finishReconnect(),this.options.autoReconnect?.abandon(e,t));}async reconnectStep(e){if(!this.reconnecting(e))return;if(!Jt()){this.park(e,()=>this.reconnectStep(e));return}let t=this.options.autoReconnect;if(t===void 0||e.attempt===0&&(await this.reconcile().catch(()=>{}),!this.reconnecting(e)))return;let r=e.deadlineAt-this.options.clock.now();if(e.attempt>=t.attempts||r<ja){this.abandonReconnect(e.matchId,e.cause);return}e.attempt+=1,this.emitter.emit("matchReconnecting",{matchId:e.matchId,attempt:e.attempt,attempts:t.attempts,remainingMs:r}),e.timer=this.options.clock.setTimeout(()=>{e.timer=null,this.track(this.reconnectAttempt(e));},Math.min(Na(e.attempt,t.maxDelayMs),r));}async reconnectAttempt(e){if(!this.reconnecting(e))return;if(!Jt()){this.park(e,()=>this.reconnectAttempt(e));return}let t=this.options.autoReconnect,r=this.room;if(t===void 0||r===null)return;let i=null;try{i=await this.track(this.onceGameIdKnown(()=>gn({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:r.id,matchId:e.matchId,version:this.version("reconcile"),knownToExist:this.seenMatches.has(e.matchId)})));}catch(o){this.reportError("reconcile",o);}if(this.reconnecting(e)){if(i?.status==="ended"){this.endMatch(i.matchId,i.reason,!Bi.has(i.reason)),this.track(this.reconcile().catch(()=>{}));return}if(i?.status==="failed"){this.abandonReconnect(e.matchId,i.reason),this.settleFailed(i.matchId,i.reason);return}if(i?.status==="ready"){this.seenMatches.add(e.matchId),this.session={phase:"ready",matchId:e.matchId},this.emitter.retain(i);let o;try{o=await t.redial(e.matchId,e.deadlineAt);}catch(a){this.reportError("reconcile",a),o="failed";}if(e.done||this.reconnect!==e)return;if(o==="closed"){this.finishReconnect();return}if(o==="connected")return;this.session.phase==="ready"&&this.session.matchId===e.matchId&&(this.session={phase:"suspended",matchId:e.matchId,reason:e.cause});}await this.reconnectStep(e);}}notifyMatchConnected(e){if(this.heldMatchId!==e||this.session.phase!=="ready"&&this.session.phase!=="suspended")return;let t=this.session.phase==="suspended";this.session={phase:"connected",matchId:e};let r=this.reconnect;r!==null&&r.matchId===e?(this.finishReconnect(),this.emitter.emit("matchReconnected",{matchId:e,attempt:r.attempt})):t&&this.emitter.emit("matchReconnected",{matchId:e,attempt:0});}async notifyMatchDisconnected(e="link_closed"){if(this.session.phase!=="ready"&&this.session.phase!=="connected")throw new u("misconfigured",`no match is connected, so there is nothing to suspend (state '${this.matchConnectionState}').`);let t=this.session.matchId;this.session={phase:"suspended",matchId:t,reason:e},this.emitter.emit("matchSuspended",{matchId:t,cause:e}),this.armReconnect(t,e);}async reconnectToMatch(e={}){if(this.session.phase!=="suspended")throw new u("misconfigured",`reconnectToMatch expects a suspended match; this one is '${this.matchConnectionState}'. Call notifyMatchDisconnected() when the transport drops.`);let t=this.heldMatchId;if(t===null)throw new u("misconfigured","there is no match to reconnect to.");return this.finishReconnect(),this.session={phase:"starting",matchId:t},this.emitter.clearRetained(),this.pollMatchStatus(e)}async notifyLeavingMatch(e={}){let t=this.heldMatchId;if(t!==null)try{await this.api.leaveMatch(t,e.keepalive??!1);}catch{}}get gameId(){let e=typeof this.options.gameId=="function"?this.options.gameId():this.options.gameId;return e===""?null:e}get currentRoom(){return this.room}get currentTicket(){return this.ticketWatch?.ticket??null}get partyTicket(){return this.partyWatch?.ticket??null}get currentMatch(){return this.emitter.retained}get matchConnectionState(){switch(this.session.phase){case "none":case "ended":return "disconnected";case "ready":case "connected":return "connected";case "suspended":return "suspended";default:return "connecting"}}get matchSession(){return this.session}holdsMatch(){return this.session.phase!=="none"&&this.session.phase!=="ended"}get heldMatchId(){return this.holdsMatch()?this.session.matchId??null:null}get isInRoom(){return this.room!==null}get isHost(){let e=this.playerId();return e!==null&&this.room?.hostPlayerId===e}get members(){return this.room?.members??[]}get playWith(){return this.playWithAnswer}on(e,t){let r=this.emitter.on(e,t);return e==="seated"&&(this.retuneSeatWatch(),this.room===null&&this.track(this.reconcileSeats().catch(()=>{}))),r}off(e,t){this.emitter.off(e,t);}once(e,t){return this.emitter.once(e,t)}handleRoomEvent(e){if(this.ticketWatch!==null&&e.event!=="version_stale"&&e.roomId!==this.room?.id&&this.ticketWatch.poke(),this.room===null)return;if(e.event==="version_stale"){this.emitter.emit("versionUpdateAvailable",e.platform);return}if(e.roomId!==this.room.id||(this.lastPushAt=this.options.clock.now(),this.retune(),e.event==="updated"&&e.version!==void 0&&e.version<=this.room.version))return;let t=e.event==="updated"&&e.reason!==void 0?{...e.matchId===void 0?{}:{matchId:e.matchId},reason:e.reason}:void 0;this.track(this.reconcile(t).catch(()=>{}));}handleAutoStartFailed(e,t){if(this.disposed||this.room?.id!==e)return;let r=this.answerAutoStart(t);if(this.emitter.emit("matchAutoStartFailed",{roomId:e,code:t,handled:r!=="leave"}),r!=="start"||this.startsInFlight>0||this.room?.id!==e)return;this.autoStartRetried=true;let i=new AbortController;this.autoStartRetry=i,this.track(this.requestStart({signal:i.signal},false).then(()=>{},o=>{i.signal.aborted||this.reportError("startMatch",o);}).finally(()=>{this.autoStartRetry===i&&(this.autoStartRetry=null);}));}answerAutoStart(e){return this.options.retryAutoStart===false||!this.isHost||!Ka.has(e)?"leave":this.startsInFlight>0?"in_flight":this.autoStartRetried?"leave":"start"}abortAutoStartRetry(){this.autoStartRetry?.abort(),this.autoStartRetry=null;}handleSeated(e){if(!this.disposed){if(this.ticketWatch!==null){this.ticketWatch.poke();return}this.watchingSeats&&this.room?.id!==e&&(this.reported.has(e)||this.track(this.reportSeat(e).catch(()=>{})));}}handleTicketEvent(e){if(!this.disposed){if(this.ticketWatch!==null){this.ticketWatch.poke();return}if(this.queueing){e.event==="queued"?this.heldQueued=e:this.heldQueued?.ticketId===e.ticketId&&(this.heldQueued=null);return}if(e.event==="queued"){this.showPartyTicket(e);return}if(e.event==="matched"){e.roomId!==void 0&&this.handleSeated(e.roomId),this.partyWatch?.id===e.ticketId&&this.partyWatch.end({status:"matched"});return}this.partyWatch?.id===e.ticketId&&this.partyWatch.end({status:e.event,reason:e.reason??e.event});}}showPartyTicket(e){if(this.disposed||this.partyWatch?.id===e.ticketId)return;this.partyWatch?.end({status:"lost"});let t=new Kt({clock:this.options.clock,first:e,onSettled:()=>{this.partyWatch===t&&(this.partyWatch=null);}});this.partyWatch=t,this.emitter.emit("partyQueued",t.ticket);}async reconcileSeats(){if(this.disposed||!this.watchingSeats||this.ticketWatch!==null)return;let e;try{e=await this.myRooms();}catch{return}let t=new Set;for(let r of e)r.status!=="closed"&&(t.add(r.id),r.id!==this.room?.id&&(this.reported.has(r.id)||await this.reportSeat(r.id).catch(()=>{})));for(let r of [...this.reported])t.has(r)||this.reported.delete(r);}async reportSeat(e){let t=this.playerId(),r=await this.run("handleSeated",()=>this.api.get(e));this.disposed||t===null||!r.members.some(i=>i.playerId===t)||this.room?.id!==r.id&&(this.reported.has(r.id)||(this.reported.add(r.id),this.partyWatch?.end({status:"matched"}),this.emitter.emit("seated",r)));}retuneSeatWatch(){if(!(!this.disposed&&this.watchingSeats&&this.room===null&&this.ticketWatch===null&&this.seatPollMs>0&&Jt())){this.seatPollHandle!==null&&(this.options.clock.clearTimeout(this.seatPollHandle),this.seatPollHandle=null);return}if(this.seatPollHandle!==null)return;let t=()=>{this.seatPollHandle=null,this.track(this.reconcileSeats().catch(()=>{}).then(()=>this.retuneSeatWatch()));};this.seatPollHandle=this.options.clock.setTimeout(t,this.seatPollMs);}installResumeWatch(){let e=globalThis;if(typeof e.addEventListener!="function")return;this.visibilityListener=()=>{if(!this.disposed&&(this.retuneSeatWatch(),!!Jt())){if(this.room!==null){let r=this.reconnect?.parked??null;r!==null&&this.reconnect!==null?(this.reconnect.parked=null,this.track(r())):this.track(this.reconcile().catch(()=>{}));return}if(this.ticketWatch!==null){this.ticketWatch.poke();return}this.options.watchSeats!==false&&this.track(this.reconcileSeats().catch(()=>{}));}};for(let r of Vi)e.addEventListener(r,this.visibilityListener);let t=globalThis.document;typeof t?.addEventListener=="function"&&t.addEventListener("resume",this.visibilityListener);}startPolling(e){this.stopPolling(),!this.disposed&&(this.pinnedIntervalMs=e??null,this.polling=true,this.schedule());}stopPolling(){this.pollHandle!==null&&(this.options.clock.clearTimeout(this.pollHandle),this.pollHandle=null),this.polling=false,this.pollIntervalMs=null;}async settled(){for(let e=0;e<20&&this.inFlight.length>0;e++){let t=this.inFlight;this.inFlight=[],await Promise.allSettled(t);}}dispose(){if(this.disposed=true,this.ticketWatch?.abandon(),this.heldQueued=null,this.partyWatch?.end({status:"lost"}),this.abortAutoStartRetry(),this.finishReconnect(),this.stopPolling(),this.emitter.clear(),this.unloadListener&&(globalThis.removeEventListener?.("pagehide",this.unloadListener),this.unloadListener=null),this.seatPollHandle!==null&&(this.options.clock.clearTimeout(this.seatPollHandle),this.seatPollHandle=null),this.visibilityListener){let e=globalThis;for(let r of Vi)e.removeEventListener?.(r,this.visibilityListener);globalThis.document?.removeEventListener?.("resume",this.visibilityListener),this.visibilityListener=null;}}get watchingSeats(){return this.options.watchSeats!==false&&this.emitter.hasListeners("seated")}get seatPollMs(){return this.options.seatPollIntervalMs??Va}requireGameId(){let e=this.gameId;if(e!==null)return e;throw new u("misconfigured","the game id is not known yet. It comes from the play token, and this manager was given no way to wait for one \u2014 pass gameId, or awaitGameId so a call made before the first token waits for it the way createGem wires it.")}playerId(){return typeof this.options.playerId=="function"?this.options.playerId():this.options.playerId}version(e){let t=this.options.clientVersion,r=Li(this.options.platformGameVersion?.(),t?.gameVersion);if(r===null)throw new u("misconfigured",`${e} has no game version to report \u2014 gem-api requires platform, game_version and engine_version together on every room route, and refuses a request carrying two of the three. The arcade normally supplies it with the play token; an embedder that does not, or a build running outside the arcade, leaves nobody to ask. Pass \`clientVersion: { gameVersion }\` to createGem (or to createRoomManager) as the fallback. It has to name a build you actually published, and a wrong one pins the room to the wrong build \u2014 which is why the platform answers it wherever it can.`);return di({...t,gameVersion:r},e)}async clusterTargets(){try{if(this.options.resolveClusterTargets!==void 0)return V(await this.options.resolveClusterTargets());let e=H(await this.options.resolveClusterTarget?.());return e===void 0?[]:[e]}catch{return []}}async afterRefusedClusters(e,t){return !Zn(e)||t.length===0?[]:V(await this.options.refreshClusterTargets?.(t)??[])}async nextCluster(e,t=false){let r=a=>a.find(s=>!e.includes(s)),i=await this.clusterTargets(),o=r(i);return o!==void 0||!t?o:r(V(await this.options.refreshClusterTargets?.(i)??[]))}async attachParty(e){if(e.partyId!==void 0)return e;let t=this.options.resolveParty;if(t===void 0)return e;let r=await t();return r===void 0?e:{...e,partyId:r}}async retryPastStaleMembership(e,t,r){if(!(e instanceof q)||e.reason!=="conflict"||!Ja.has(e.code??""))throw e;if(this.options.recoverStaleMembership===false)throw new q(e.reason,e.status,`${e.detail} \u2014 quick-join is refused while this player already holds a game room, and \`recoverStaleMembership\` is off, so nothing cleared it. A membership that outlived its page is visible only through myRooms(); leaveAll() is what clears it.`,e.retryAfterSeconds,e.code);let i;try{i=await this.leaveAll(t.signal);}catch{throw new q(e.reason,e.status,`${e.detail} \u2014 quick-join is refused while this player already holds a game room, and the recovery could not list the rooms it would have cleared, so nothing was tried and the refusal stands. That is the listing failing, not a room refusing to be left: retry once the connection is back. myRooms() shows what is held.`,e.retryAfterSeconds,e.code)}if(i.length===0)throw new q(e.reason,e.status,`${e.detail} \u2014 quick-join is refused while this player already holds a game room, and the recovery cleared nothing, so the refusal stands. myRooms() shows what is held. A room this player was seated in as a party member cannot be cleared from here at all \u2014 only the leader of a party may take it out of a room \u2014 so a member joins by adopting that room, myRooms() then join(), rather than quick-joining.`,e.retryAfterSeconds,e.code);return this.postQuickJoin(t,r)}postQuickJoin(e,t){return Qn(this.apiOptions,e,this.version("quickJoin"),t,this.internals.playWithTicket)}assertNotInRoom(e){if(this.room!==null)throw new u("misconfigured",`${e} refused: already in room ${this.room.id}. Leave it first \u2014 this manager tracks one room, and a second would leave its state disagreeing with the server's about which one you are in.`)}assertNotQueued(e){if(!(this.ticketWatch===null&&!this.queueing))throw new u("misconfigured",`${e} refused: this player is waiting on a matchmaking ticket, which will seat them in a room of its own. Cancel it first \u2014 currentTicket.cancel() \u2014 or wait for its outcome.`)}requireRoom(e){if(this.room===null)throw new u("misconfigured",`${e} refused: not in a room.`);return this.room}async run(e,t){try{return await this.track(this.onceGameIdKnown(t))}catch(r){throw this.reportError(e,r),r}}onceGameIdKnown(e){return this.gameId!==null||this.options.awaitGameId===void 0?e():this.options.awaitGameId().then(e)}reportError(e,t){let r=t instanceof q?{operation:e,code:t.status,reason:t.reason,message:t.message,retryable:t.retryable}:{operation:e,code:0,reason:t instanceof u?t.reason:"unavailable",message:t instanceof Error?t.message:String(t),retryable:false};this.emitter.emit("error",r);}askForLocalServer(e){let t=this.options.ensureLocalServer;t===void 0||!e.selfHosted||e.status!=="open"||t(e.id).catch(()=>{});}track(e){return this.inFlight.push(e),e}enter(e,t){this.room=e,this.queuedFromRoomId===e.id&&(this.queuedFromRoomId=null),this.abortFollow(),this.autoStartRetried=false,this.session={phase:"none"},this.seenMatches.clear(),this.refusedMatches.clear(),this.pendingHint=null,this.emitter.clearRetained(),this.emitter.emit(t,e),this.adoptMatch(e),this.options.poll!==false&&this.startPolling(),this.retuneSeatWatch();}exit(){this.room=null,this.abortFollow(),this.abortAutoStartRetry(),this.autoStartRetried=false,this.finishReconnect(),this.session.phase!=="ended"&&(this.session={phase:"none"}),this.seenMatches.clear(),this.refusedMatches.clear(),this.pendingHint=null,this.emitter.clearRetained(),this.stopPolling(),this.retuneSeatWatch();}apply(e){let t=this.room;if(t===null||t.id!==e.id)return;let r=new Map(t.members.map(s=>[s.playerId,s])),i=new Map(e.members.map(s=>[s.playerId,s])),o=this.playerId();for(let s of i.keys())r.has(s)||this.emitter.emit("memberJoined",s);for(let s of r.keys())if(!i.has(s)){if(s===o){this.exit(),this.emitter.emit("kicked");return}this.emitter.emit("memberLeft",s);}for(let[s,l]of i){let d=r.get(s);d&&d.isReady!==l.isReady&&this.emitter.emit("readyChanged",s,l.isReady);}t.hostPlayerId!==e.hostPlayerId&&e.hostPlayerId.length>0&&this.emitter.emit("hostChanged",e.hostPlayerId);let a=za(t,e);this.room=e,this.adoptMatch(e),this.session.phase==="starting"&&this.followMatch(),a&&this.emitter.emit("updated",e);}adoptMatch(e){e.status!=="in_game"||e.matchId===void 0||this.session.matchId!==e.matchId&&(this.refusedMatches.has(e.matchId)||this.beginMatch(e.matchId));}beginMatch(e){let t=this.heldMatchId;t!==null&&t!==e&&this.endMatch(t,"replaced",true),this.session={phase:"starting",matchId:e},this.autoStartRetried=false,this.emitter.clearRetained(),this.emitter.emit("matchAllocationStarted",e),this.followMatch();}followMatch(){if(this.autoFollow===false)return;let e=this.room,t=this.heldMatchId;if(e===null||t===null||this.followAbort!==null&&this.followingMatchId===t)return;this.abortFollow();let r=new AbortController;this.followAbort=r,this.followingMatchId=t,this.track(this.onceGameIdKnown(()=>this.chase(e.id,t,{signal:r.signal})).then(i=>{if(!r.signal.aborted)try{this.settleOutcome(i);}catch{}}).catch(i=>{r.signal.aborted||this.reportError("pollMatchStatus",i);}).finally(()=>{this.followAbort===r&&(this.followAbort=null,this.followingMatchId=null);}));}abortFollow(){this.followAbort?.abort(),this.followAbort=null,this.followingMatchId=null;}matchIdOrWait(e){let t=this.heldMatchId;if(t!==null)return Promise.resolve(t);let r=e.timeoutMs??Ba;return new Promise((i,o)=>{let a=c=>{s(),l(),this.options.clock.clearTimeout(m),e.signal?.removeEventListener("abort",d),c();},s=this.emitter.on("matchAllocationStarted",c=>a(()=>i(c))),l=this.emitter.on("matchFailed",(c,p)=>a(()=>o(new u("allocation_failed",`match ${c} did not come up: ${p}`)))),d=()=>a(()=>o(new u("aborted","the caller aborted while awaiting a match"))),m=this.options.clock.setTimeout(()=>a(()=>o(new u("timeout",`no match was allocated for room ${this.room?.id??"(none)"} within ${r}ms`))),r);e.signal?.aborted?d():e.signal?.addEventListener("abort",d);})}baseInterval(){return this.pushed()?this.pushedInterval():this.options.pollIntervalMs??Ua}pushedInterval(){return this.options.pushedPollIntervalMs??Wa}pushed(){return this.options.clock.now()-this.lastPushAt<Ha*this.pushedInterval()}retune(){!this.polling||this.pinnedIntervalMs!==null||this.baseInterval()!==this.pollIntervalMs&&this.startPolling();}schedule(){this.disposed||!this.polling||this.room===null||(this.pollIntervalMs=this.pinnedIntervalMs??this.baseInterval(),this.pollHandle=this.options.clock.setTimeout(()=>{this.pollHandle=null,this.track(this.reconcile().catch(()=>null).then(()=>{this.schedule();}));},this.pollIntervalMs));}installUnloadNotifier(){if(this.options.notifyOnUnload===false)return;let e=globalThis;typeof e.addEventListener=="function"&&(this.unloadListener=t=>{if(this.ticketWatch?.abandon(true),t?.persisted!==true&&(this.room!==null&&this.track(this.leave({keepalive:true}).catch(()=>{})),this.queuedFromRoomId!==null)){let r=this.queuedFromRoomId;this.queuedFromRoomId=null,this.track(this.api.leave(r,true,void 0,true).catch(()=>{}));}},e.addEventListener("pagehide",this.unloadListener));}};function Jt(){let n=globalThis.document;return n?.visibilityState===void 0||n.visibilityState==="visible"}var Vi=["visibilitychange","focus","pageshow","online"];function za(n,e){return n.version!==e.version||n.status!==e.status||n.matchId!==e.matchId||n.hostPlayerId!==e.hostPlayerId||n.name!==e.name||n.maxPlayers!==e.maxPlayers||n.isPublic!==e.isPublic||Ki(n)!==Ki(e)}function Ki(n){return n.members.map(e=>`${e.playerId}:${e.isReady?1:0}:${e.role}`).sort().join(",")}function Zn(n){return n instanceof q&&n.code==="invalid_cluster_target"}var zt=8;function Xa(n){if(!(Array.isArray(n)&&n.length>=1&&n.length<=zt&&n.every(t=>typeof t=="string"&&t.length>0)))throw new u("misconfigured",`queueForMatch needs templateSlugs: an array naming one to ${zt} room templates by slug, each with matchmaking turned on in your game config \u2014 queueForMatch({ templateSlugs: ['duel'] }).`)}function Ya(n){let e=n?.templateSlugs;if(e===void 0)return;if(!(Array.isArray(e)&&e.length>=1&&e.length<=zt&&e.every(r=>typeof r=="string"&&r.length>0)))throw new u("misconfigured",`quickJoin's templateSlugs is an array naming one to ${zt} room templates by slug \u2014 quickJoin({ templateSlugs: ['duel', 'brawl'] }). A room that has to be created is made from the first.`);if(n?.templateSlug!==void 0||n?.templateId!==void 0)throw new u("misconfigured","quickJoin takes templateSlugs or a single templateSlug / templateId, not both. Put every template this player accepts in templateSlugs, with the one to create from first.")}function Ji(n,e){return n.length===e.length&&n.every((t,r)=>t===e[r])}function Xi(n){let e=0,t=new Map;return {resolve(){if(!n.bridge.parentSupports("parties"))return Promise.resolve(void 0);let r=`party-${String(++e)}`;return new Promise(i=>{let o={},a=l=>{t.delete(r)&&(n.clock.clearTimeout(o.timer),i(l));};t.set(r,a),o.timer=n.clock.setTimeout(()=>a(void 0),n.timeoutMs??2e3);let s=false;try{s=n.bridge.requestParty(r);}catch{}s||a(void 0);})},settle(r,i){t.get(r)?.(i);}}}
|
|
3
|
-
export{
|
|
1
|
+
globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:24.0.0]";var g="gem-web-bridge",ne={min:1,max:1},Zi=65536,ir=8,gt=5e3,or=1024,Zt=1e4,ge=Object.create(null),xe="__malformed",el=["gem.handshake","gem.token","gem.token.denied","gem.player","gem.pause","gem.prefs","gem.cohorts","gem.earnAllowanceChanged","gem.inventoryChanged","gem.viewport","gem.deviceSettings","gem.willTerminate","gem.roomEvent","gem.socketState","gem.seatedEvent","gem.autoStartFailedEvent","gem.ticketEvent","gem.signalEvent","gem.relayDraining","gem.dedicatedEvent","gem.partyResult","gem.clusterTarget","gem.graphicsRoutes","gem.devtools","gem.screenshot","gem.requestContext","gem.localServer","gem.localServer.denied","gem.menuSelect","gem.settingChange","gem.shareResult","gem.error","gem.purchaseResult","gem.purchaseSettled","gem.deviceSettings.result","gem.adResult","gem.momentResult","gem.signInResult","gem.friendChallengeResult","gem.playInviteResult","gem.appLinksResult","gem.openAppResult","gem.openGameResult","gem.similarGamesResult","gem.storeOffersResult","gem.storePurchaseResult","gem.storePurchaseSettled","gem.storeOfferSheetResult"],tl=["gem.handshake.accept","gem.screenshot.denied","gem.gameContext","gem.signal","gem.dedicatedOffer","gem.partyRequest","gem.ready","gem.engaged","gem.progress","gem.requestToken","gem.requestLocalServer","gem.exit","gem.openExternal","gem.requestFullscreen","gem.audioState","gem.rewardCue","gem.rewardPresentation","gem.walletDisplay","gem.controlsDeclare","gem.menu","gem.gemCorner","gem.menuStyle","gem.settingsDeclare","gem.requestShare","gem.requestAvatarPicker","gem.vibrate","gem.frameStats","gem.portraitBorders","gem.error","gem.requestPurchase","gem.requestAvatar3dPurchase","gem.deviceSettings.put","gem.requestAd","gem.signalMoment","gem.requestSignIn","gem.requestFriendChallenge","gem.requestPlayInvite","gem.inviteParams","gem.requestAppLinks","gem.requestOpenApp","gem.requestOpenGame","gem.requestSimilarGames","gem.requestStoreOffers","gem.requestStorePurchase","gem.requestStoreOfferSheet"],en=Object.create(null),eo=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.textScale.","textScale"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"],["gem.share.","shareRemap"]]);function he(n){let e=en[n];if(e!==void 0)return e;for(let[t,r]of eo)if(n.startsWith(t))return r}function ht(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 to(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 no(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function ro(n){return /^[a-z_]+$/.test(n)}function io(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 Yt(n,e,t){if(e>ir)return `nesting deeper than ${ir}`;if(++t.nodes>gt)return `more than ${gt} 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 i=n;if(t.seen.has(i))return "a repeated or cyclic object reference";if(t.seen.add(i),Array.isArray(n)){if(n.length>or)return `an array of ${n.length} entries, over the ${or} limit`;t.bytes+=2;for(let s of n){let l=Yt(s,e+1,t);if(l)return l}return null}let o=Object.getPrototypeOf(n);if(o!==Object.prototype&&o!==null)return "a value that is not a plain object";t.bytes+=2;let a=n;for(let s in a){if(!Object.prototype.hasOwnProperty.call(a,s))continue;if(++t.nodes>gt)return `more than ${gt} values`;if(t.bytes+=s.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let l=Yt(a[s],e+1,t);if(l)return l}return null}function oo(n,e=Zi){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=Yt(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var so={origin:[ht,"not a well-formed origin"],url:[to,"not a safe absolute http(s) URL"],bcp47:[no,"not a well-formed BCP 47 tag"],code:[ro,"not a code of lower-case letters and underscores"],gameRef:[io,"not a game id or web address"]};function sr(n,e,t){for(let r of e){let i=t===""?r[0]:`${t}.${r[0]}`,o=n[r[0]];if(o==null){if(r[1]<"a")continue;return `${i}: required`}let a=Qt(o,r,i);if(a)return a}return null}function Qt(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:Qt(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[i,o]=so[e[3]];if(!i(n))return `${t}: ${o}`}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 i=e[2];return i.includes(n)?null:`${t}: not one of ${i.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:sr(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 i=e[3],o=`${t}[]`;for(let a of n){if(a==null){if(i[1]<"a")continue;return `${o}: required`}let s=Qt(a,i,o);if(s)return s}return null}default:return `${t}: unknown kind ${String(r)}`}}var ar=Object.create(null);function h(n,e,t,r,i){return ge[n]=t,r&&(en[n]=r),ar[n]=o=>{let a=oo(o,i);if(a)return a;if(typeof o!="object"||o===null||Array.isArray(o))return "message: expected object";let s=o;return s.gem!==g?"gem: wrong protocol marker":s.type!==n?`type: expected ${n}`:sr(s,e,"")}}var je=["","s",64],S=["requestId","s",64],Ue=["currencySlug","s",32],We=["matchId","s",64],re=["reason","S",64,"code"],le=["outcome","e",["ok","declined","unavailable"]],nl=h("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,je],["embedder","s",2048,"origin"],["unbidden","B"]],30),rl=h("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,je]],5),il=h("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),ol=h("gem.token.denied",[S,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30),sl=h("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],Ue,["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]],["signInAvailable","B"]],12),al=h("gem.pause",[["paused","b"]],120),ll=h("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"],["textScale","I",100,200]]]],60),dl=h("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts"),cl=h("gem.earnAllowanceChanged",[],30,"earnAllowance"),ul=h("gem.inventoryChanged",[],30,"inventoryChanged"),pl=h("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"),ml=h("gem.deviceSettings",[["settings","J"]],12,"deviceSettings"),gl=h("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10),hl=h("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"),fl=h("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState"),yl=h("gem.seatedEvent",[["roomId","s",64]],60,"partySeating"),bl=h("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure"),vl=h("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"),_l=h("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],We,["data","j"]],1800,"p2p"),Rl=h("gem.relayDraining",[We,["clusterTargetId","s",64],["relayIps","a",16,je],["drainingSince","s",40],["deadline","s",40]],60,"p2pRelayDrain"),wl=h("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],We,["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated"),kl=h("gem.partyResult",[S,["partyId","S",64]],30,"parties"),Sl=h("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,je]],12,"clusterTarget"),xl=h("gem.graphicsRoutes",[["entries","a",32,["","o",[["gpu","s",32],["route","S",32],["host","S",32]]]]],12,"graphicsRoutes"),Al=h("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools"),Pl=h("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot"),Il=h("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot"),Tl=h("gem.requestContext",[],12,"feedbackContext"),El=h("gem.gameContext",[["context","j"]],12,"feedbackContext"),Cl=h("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],We,["data","j"]],1200,"p2p"),Ml=h("gem.dedicatedOffer",[We,["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated"),Ol=h("gem.partyRequest",[S],12,"parties"),Gl=h("gem.ready",[],5),Ll=h("gem.engaged",[],5,"engagement"),Dl=h("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600),ql=h("gem.requestToken",[S,["reason","e",["initial","expiring","rejected"]]],12),$l=h("gem.requestLocalServer",[S,["gameRoomId","s",64]],6,"localHost"),Fl=h("gem.localServer",[S,["serverId","S",128]],30,"localHost"),Nl=h("gem.localServer.denied",[S,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost"),jl=h("gem.exit",[["reason","S",64]],5),Ul=h("gem.openExternal",[["url","s",2048,"url"]],6),Wl=h("gem.requestFullscreen",[["enabled","b"]],30),Hl=h("gem.audioState",[["muted","b"]],120,"audioState"),Bl=h("gem.rewardCue",[Ue,["cueId","S",64],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue"),Vl=h("gem.rewardPresentation",[["mode","e",["arcade","game"]]],12,"rewardPresentation"),Kl=h("gem.walletDisplay",[["visible","b"],["currencies","A",8,je],["score","B"]],60,"walletDisplay"),Jl=h("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls"),zl=h("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu"),Xl=h("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu"),Yl=h("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner"),Ql=h("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle"),Zl=h("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"),ed=h("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings"),td=h("gem.requestShare",[["requestId","S",64],["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share"),nd=h("gem.shareResult",[S,["outcome","e",["accepted","refused"]],re,["key","S",64]],30,"shareResult"),rd=h("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker"),id=h("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration"),od=h("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats"),sd=h("gem.portraitBorders",[["name","S",40]],60,"portraitBorders"),ad=h("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60),ld=h("gem.requestPurchase",[S,["purchaseKind","e",["item","recipe"]],["targetId","s",64],Ue,["quantity","I",1,10],["catalogId","S",64],["origin","E",["sheet","direct_refused"]]],12,"purchases"),dd=h("gem.requestAvatar3dPurchase",[S,["itemId","s",64],Ue],12,"avatar3dPurchases"),cd=h("gem.purchaseResult",[S,le,re,["purchaseId","S",64]],30,"purchases"),ud=h("gem.purchaseSettled",[["purchaseKind","e",["item","recipe"]],["targetId","s",64],["catalogId","S",64],["quantity","i",1,10],["purchaseId","S",64]],30,"purchaseSettled"),pd=h("gem.deviceSettings.put",[S,["settings","j"]],30,"deviceSettings"),md=h("gem.deviceSettings.result",[S,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings"),gd=h("gem.requestAd",[S,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads"),hd=h("gem.adResult",[S,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads"),fd=h("gem.signalMoment",[S,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments"),yd=h("gem.momentResult",[S,["shown","b"]],12,"moments"),bd=h("gem.requestSignIn",[S],6,"signIn"),vd=h("gem.signInResult",[S,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn"),_d=h("gem.requestFriendChallenge",[S,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges"),Rd=h("gem.friendChallengeResult",[S,["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"),wd=h("gem.requestPlayInvite",[S,["params","A",16,["","o",[["key","s",64],["value","s",512]]]]],6,"playInvites"),kd=h("gem.playInviteResult",[S,["shown","b"]],12,"playInvites"),Sd=h("gem.inviteParams",[["params","A",16,["","o",[["key","s",64],["value","s",512]]]]],30,"playInvites"),xd=h("gem.requestAppLinks",[S],12,"appLinks"),Ad=h("gem.appLinksResult",[S,le,re,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks"),Pd=h("gem.requestOpenApp",[S,["target","e",["arcade","game"]]],6,"appLinks"),Id=h("gem.openAppResult",[S,le,re],30,"appLinks"),Td=h("gem.requestOpenGame",[S,["ref","s",40,"gameRef"]],6,"openGame"),Ed=h("gem.openGameResult",[S,le,re],30,"openGame"),Cd=h("gem.requestSimilarGames",[S,["limit","I",1,24]],12,"openGame"),Md=h("gem.similarGamesResult",[S,le,re,["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),Od=h("gem.requestStoreOffers",[S],12,"store"),Gd=h("gem.storeOffersResult",[S,le,re,["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]]],["pass","S",64],["earnMultipliers","A",8,["","o",[Ue,["multiplierBp","i",1e4,1e6]]]]]]]],30,"store",262144),Ld=h("gem.requestStorePurchase",[S,["skuId","s",64]],12,"store"),Dd=h("gem.storePurchaseResult",[S,le,re],30,"store"),qd=h("gem.storePurchaseSettled",[["skuId","s",64]],30,"store"),$d=h("gem.requestStoreOfferSheet",[S,["skuId","s",64]],12,"storeOfferSheet"),Fd=h("gem.storeOfferSheetResult",[S,le,re],30,"storeOfferSheet"),de=Object.freeze(ar);ge.__malformed=60;Object.freeze(ge);Object.freeze(en);var ft=3;function H(n){if(typeof n!="string")return;let e=n.trim();return e.length===0?void 0:e}function V(n){if(!Array.isArray(n))return [];let e=[];for(let t of n){let r=H(t);if(!(r===void 0||e.includes(r))&&(e.push(r),e.length===3))break}return e}var ao=2e3,lo=5e3;function co(n){let e=[],t=false,r=null,i=new Map,o=()=>{if(e.length>0)return Promise.resolve(e);if(t)return Promise.resolve([]);if(!n.bridge.parentSupports("clusterTarget"))return Promise.resolve([]);if(r!==null)return r.promise;let a,s=new Promise(l=>{let d=false,m=null;a=c=>{d||(d=true,r=null,n.clock.clearTimeout(m),l(c));},m=n.clock.setTimeout(()=>{t=true,a([]);},n.timeoutMs??ao);});return r={promise:s,settle:a},s};return {resolveList:o,resolve(){return e.length>0||t||!n.bridge.parentSupports("clusterTarget")?Promise.resolve(e[0]):o().then(a=>a[0])},refresh(a){return !lr(e,a)||!n.bridge.parentSupports("clusterTarget")?Promise.resolve(e):new Promise(s=>{let l=null,d=m=>{i.delete(d)&&(n.clock.clearTimeout(l),s(m));};i.set(d,a),l=n.clock.setTimeout(()=>d(e),n.refreshTimeoutMs??lo);})},accept(a,s){let l=V(s),d=H(a),m=l.length>0?l:d===void 0?[]:[d];if(m.length!==0){e=m,r?.settle(m);for(let[c,p]of [...i])lr(m,p)||c(m);}}}}function lr(n,e){return n.length===e.length&&n.every((t,r)=>t===e[r])}var uo=["angle_gles","native_gl","angle_opengl","angle_other","angle_d3d11","angle_d3d9","angle_metal"],po=Object.freeze(uo.map(n=>Object.freeze({gpu:"powervr_d_series",route:n})));var mo=32,go=32;function dr(n){if(!Array.isArray(n)||n.length>mo)return null;let e=[];for(let t of n){if(t===null||typeof t!="object"||Array.isArray(t))return null;let{gpu:r,route:i,host:o}=t;if(!tn(r)||i!==void 0&&!tn(i)||o!==void 0&&!tn(o))return null;e.push(Object.freeze({gpu:r,...i===void 0?{}:{route:i},...o===void 0?{}:{host:o}}));}return Object.freeze(e)}function tn(n){return typeof n=="string"&&n!==""&&n.length<=go}var fo=/^[a-z0-9][a-z0-9_-]{0,39}$/;function nn(n){return typeof n=="string"&&fo.test(n)}function cr(n){if(n===null||nn(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 ur=Object.freeze(["music","sfx","voice","eye","text","hint","speed","bright","controls","star"]),pr=/^[A-Za-z0-9_-]{1,40}$/;function rn(n){return typeof n=="number"&&Number.isFinite(n)&&Math.abs(n)<=1e6}function mr(n){if(typeof n!="string")return;let e=n.trim();return e.length===0?void 0:e.slice(0,40)}function gr(n,e,t,r){let i=s=>Number.parseFloat(s.toPrecision(12)),o=Math.min(t,Math.max(e,n)),a=i(e+Math.round((o-e)/r)*r);return a>t&&(a=i(a-r)),Math.min(t,Math.max(e,a))}function hr(n,e){switch(n.kind){case "toggle":return typeof e=="boolean"?e:void 0;case "slider":{if(typeof e!="number"||!Number.isFinite(e))return;let t=n.min??0,r=n.max??100;return gr(e,t,r,n.step??1)}case "choice":return typeof e=="string"&&(n.options??[]).some(t=>t.id===e)?e:void 0;default:return}}function fr(n,e=()=>{}){if(!Array.isArray(n))return null;let t=[],r=0;for(let i=0;i<n.length;i+=1){let o=n[i],a=`settings[${i}]`;if(t.length===8){e(`${a} and later were left out: at most 8 settings are shown.`);break}if(typeof o!="object"||o===null){e(`${a} was dropped: a setting is an object.`);continue}let s=o,l=s.id;if(typeof l!="string"||!pr.test(l)){e(`${a} was dropped: its id must be 1 to 40 of A-Z a-z 0-9 _ -.`);continue}let d=`${a} (${JSON.stringify(l)})`;if(t.some(p=>p.id===l)){e(`${d} was dropped: an earlier setting has the same id.`);continue}let m=mr(s.label);if(m===void 0){e(`${d} was dropped: its label is empty.`);continue}let c;switch(s.kind){case "toggle":{if(typeof s.value!="boolean"){e(`${d} was dropped: a toggle's value is true or false.`);continue}c={id:l,label:m,kind:"toggle",value:s.value},s.quick===true&&(r<2?(c.quick=true,r+=1):e(`${d} is not in the quick row: at most 2 toggles are, the first that ask.`));break}case "slider":{let p=s.min??0,y=s.max??100,b=s.step??1;if(!rn(p)||!rn(y)||!rn(b)||p>=y||b<=0){e(`${d} was dropped: a slider needs finite numbers within \xB11000000 with min below max and a step above 0.`);continue}if(typeof s.value!="number"||!Number.isFinite(s.value)){e(`${d} was dropped: a slider's value is a number.`);continue}c={id:l,label:m,kind:"slider",value:gr(s.value,p,y,b),min:p,max:y,step:b};break}case "choice":{let p=[];for(let y of Array.isArray(s.options)?s.options:[]){if(p.length===4)break;if(typeof y!="object"||y===null)continue;let b=y,v=mr(b.label);typeof b.id!="string"||!pr.test(b.id)||v===void 0||p.some(A=>A.id===b.id)||p.push({id:b.id,label:v});}if(p.length<2){e(`${d} was dropped: a choice needs 2 to 4 options, each with an id of 1 to 40 of A-Z a-z 0-9 _ - and a label.`);continue}if(typeof s.value!="string"||!p.some(y=>y.id===s.value)){e(`${d} was dropped: a choice's value is the id of one of its options.`);continue}c={id:l,label:m,kind:"choice",value:s.value,options:p};break}default:e(`${d} was dropped: its kind must be 'toggle', 'slider' or 'choice'.`);continue}s.icon!==void 0&&(ur.includes(s.icon)?c.icon=s.icon:e(`${d} keeps the Arcade's generic icon: ${JSON.stringify(s.icon)} is not one of ${ur.join(", ")}.`)),t.push(c);}return t}var He=16384,yt=24;function x(n){return e=>typeof e=="string"&&e.length>0&&e.length<=n?e:void 0}function ie(n,e){return t=>typeof t=="number"&&Number.isInteger(t)&&t>=n&&t<=e?t:void 0}function Ae(...n){return e=>typeof e=="string"&&n.includes(e)?e:void 0}function yr(n){return typeof n=="boolean"?n:void 0}var L=x(64),Be=ie(0,2147483647);function bt(n){if(!Array.isArray(n))return;let e=[];for(let t of n){if(e.length>=64)break;let r=L(t);typeof r=="string"&&e.push(r);}return e}function P(n,e,t){return {to:n,from:e,read:t,required:true}}function I(n,e,t){return {to:n,from:e,read:t,required:false}}function Pe(n,e){let t={};for(let r of e){let i=r.read(n[r.from]);if(i===void 0){if(r.required)return null;continue}t[r.to]=i;}return t}function Ve(n){if(typeof n!="object"||n===null||Array.isArray(n))return null;let e=n;return typeof e.type!="string"||typeof e.data!="object"||e.data===null||Array.isArray(e.data)?null:{type:e.type,data:e.data}}var br=[P("sdp","sdp",x(10240)),P("signaling_intent","intent",Ae("initial","ice_restart","full_reconnect"))];function vt(n,e,t){if(typeof t!="object"||t===null||Array.isArray(t))return {reason:"offer: data must be an object"};let r=t,i=Pe(r,br);return i===null?{reason:`offer: '${br.find(a=>a.read(r[a.from])===void 0)?.from??"?"}' is missing or out of bounds`}:{frame:{type:"webrtc_offer",match_id:n,game_id:e,...i}}}var on=Object.freeze(Object.assign(Object.create(null),{webrtc_sdp_answer:"answer",webrtc_error:"error",match_ended:"matchEnded"})),yo=new Set(["error"]),bo=Object.freeze(Object.assign(Object.create(null),{answer:[P("sdp","sdp",x(10240)),I("sessionId","session_id",x(64))],error:[I("code","code",x(64)),I("message","message",x(256))],matchEnded:[I("reason","reason",x(64))]}));function _t(n,e,t){let r=Ve(n);if(r===null)return null;let i=on[r.type];if(i===void 0)return null;let o=L(r.data.match_id);if(typeof o!="string"||!(yo.has(i)?t(o):r.data.game_id===e))return null;let s=Pe(r.data,bo[i]??[]);return s===null?null:{event:i,matchId:o,...s}}var vo=n=>{if(typeof n!="object"||n===null||Array.isArray(n))return;let e=n,t=Ae("disconnected","failed","closed")(e.iceState),r=ie(0,4102444800)(e.lastSeenAt);if(!(t===void 0&&r===void 0))return {...t===void 0?{}:{ice_state:t},...r===void 0?{}:{last_seen_timestamp:r}}},_o=Object.freeze(Object.assign(Object.create(null),{join:{type:"p2p_join",gameScoped:true,args:[]},offer:{type:"p2p_offer",gameScoped:false,args:[P("sdp","sdp",x(10240)),P("signaling_intent","intent",Ae("initial","ice_restart","full_reconnect")),I("sdp_hash","sdpHash",x(128))]},answer:{type:"p2p_answer",gameScoped:false,args:[P("peer_id","peerId",L),P("sdp","sdp",x(10240)),P("assigned_peer_id","assignedPeerId",ie(0,4294967295)),I("sdp_hash","sdpHash",x(128))]},ice:{type:"p2p_ice_candidate",gameScoped:false,args:[P("peer_id","peerId",L),P("candidate","candidate",x(512)),I("sdp_mid","sdpMid",x(64)),I("sdp_mline_index","sdpMLineIndex",ie(0,255))]},migrationAck:{type:"p2p_migration_ack",gameScoped:false,args:[P("host_epoch","hostEpoch",Be)]},hostUnreachable:{type:"p2p_report_host_unreachable",gameScoped:false,args:[P("host_epoch","hostEpoch",Be),I("evidence","evidence",vo)]}}));function Rt(n,e,t,r){let i=_o[n];if(i===void 0)return {reason:`unknown signal '${n}'`};if(typeof r!="object"||r===null||Array.isArray(r))return {reason:`${n}: data must be an object`};let o=r,a=Pe(o,i.args);if(a===null){let s=i.args.find(l=>l.required&&l.read(o[l.from])===void 0);return {reason:`${n}: '${s?.from??"?"}' is missing or out of bounds`}}return {frame:{type:i.type,match_id:e,...i.gameScoped?{game_id:t}:{},...a}}}var Ro=Object.freeze(Object.assign(Object.create(null),{p2p_hello:"hello",p2p_sdp_offer:"offer",p2p_sdp_answer:"answer",p2p_ice_candidate:"ice",p2p_peer_joined:"peerJoined",p2p_peer_left:"peerLeft",p2p_host_migration:"migration",p2p_match_ended:"matchEnded",p2p_error:"error",p2p_relay_draining:"relayDraining"})),wo=16,ko=n=>bt(n)?.slice(0,wo),So=new Set(["error","relayDraining"]),xo=Object.freeze(Object.assign(Object.create(null),{hello:[P("playerId","player_id",L),P("hostPlayerId","host_player_id",L),P("hostEpoch","host_epoch",Be),I("members","members",bt)],offer:[P("fromPlayerId","from_player_id",L),P("sdp","sdp",x(10240)),I("sessionId","signaling_session_id",x(128)),I("sdpHash","sdp_hash",x(128)),I("iceUfrag","ice_ufrag",x(64)),I("intent","signaling_intent",Ae("initial","ice_restart","full_reconnect"))],answer:[P("fromPlayerId","from_player_id",L),P("sdp","sdp",x(10240)),I("sessionId","signaling_session_id",x(128)),I("sdpHash","sdp_hash",x(128)),I("iceUfrag","ice_ufrag",x(64)),I("assignedPeerId","assigned_peer_id",ie(0,4294967295))],ice:[P("fromPlayerId","from_player_id",L),P("candidate","candidate",x(512)),I("sdpMid","sdp_mid",x(64)),I("sdpMLineIndex","sdp_m_line_index",ie(0,255)),I("sdpMLineIndexAlt","sdp_mline_index",ie(0,255))],peerJoined:[P("playerId","player_id",L)],peerLeft:[P("playerId","player_id",L),I("reason","reason",x(64))],migration:[P("hostPlayerId","new_host_player_id",L),P("hostEpoch","host_epoch",Be),I("reason","reason",x(64)),I("kickedPlayerIds","kicked_player_ids",bt),I("migrationComplete","migration_complete",yr)],matchEnded:[I("reason","reason",x(64))],error:[I("code","code",x(64)),I("message","message",x(256))],relayDraining:[P("clusterTargetId","cluster_target_id",L),P("relayIps","relay_ips",ko),P("drainingSince","draining_since",x(40)),P("deadline","deadline",x(40))]}));function wt(n,e,t){let r=Ve(n);if(r===null)return null;let i=Ro[r.type];if(i===void 0)return null;let o=L(r.data.match_id);if(typeof o!="string"||!(So.has(i)?t(o):r.data.game_id===e))return null;let s=Pe(r.data,xo[i]??[]);return s===null?null:{event:i,matchId:o,data:s}}var Ao=Object.freeze(Object.assign(Object.create(null),{party_invite_received:"inviteReceived",party_member_joined:"memberJoined",party_member_left:"memberLeft",party_member_kicked:"memberKicked",party_leader_transferred:"leaderTransferred",party_updated:"updated",party_disbanded:"disbanded",party_game_room_joined:"gameRoomJoined",party_game_room_left:"gameRoomLeft"}));function Ke(n){let e=Ve(n);if(e===null)return null;let t=Ao[e.type];if(t===void 0)return null;let r=e.data,i=L(r.party_id);if(typeof i!="string")return null;let o=ie(0,Number.MAX_SAFE_INTEGER)(r.version),a=L(r.game_room_id),s=L(r.game_id),l=L(r.inviter_player_id),d=x(80)(r.inviter_display_tag),m=x(64)(r.party_name);return {event:t,partyId:i,...o===void 0?{}:{version:o},...a===void 0?{}:{gameRoomId:a},...s===void 0?{}:{gameId:s},...l===void 0?{}:{inviterPlayerId:l},...d===void 0?{}:{inviterDisplayTag:d},...m===void 0?{}:{partyName:m}}}var ce={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)},oe=class{constructor(e=ce){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let t=ge[e];if(t===void 0)return false;let r=this.clock.now(),i=this.lastRefill.get(e)??r,o=this.tokens.get(e)??t,a=Math.max(0,r-i),s=Math.min(t,o+a*t/6e4);return s<1?(this.tokens.set(e,s),this.lastRefill.set(e,r),false):(this.tokens.set(e,s-1),this.lastRefill.set(e,r),true)}};function ye(n,e){return n.toLowerCase()===e.toLowerCase()}function Te(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function _r(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:Te(t.hostname)&&Te(r.hostname)}function sn(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 Po=7,an=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,Io=/^[a-z][a-z0-9_]{0,31}$/,To=8,wr=256,Eo=["universal","mobile"],Co={game_room_updated:"updated",game_room_match_started:"match_started",game_room_match_ready:"match_ready",game_room_match_failed:"match_failed",game_room_version_stale:"version_stale",match_ended:"updated",p2p_match_ended:"updated"},Mo=new Set(["match_ended","p2p_match_ended"]),Oo="game_room_auto_start_failed",kr={matchmaking_ticket_queued:"queued",matchmaking_ticket_matched:"matched",matchmaking_ticket_expired:"expired",matchmaking_ticket_cancelled:"cancelled",matchmaking_ticket_failed:"failed"},Go="game_inventory_changed",Je=2e3,Lo=64,Sr=/^[a-z][a-z0-9_]{0,63}$/,ze=64,Do=8,dn=class{constructor(e,t){this.clock=e;this.limit=t;}clock;limit;windowStart=0;used=0;allow(){let e=this.clock.now();return e-this.windowStart>=1e3&&(this.windowStart=e,this.used=0),this.used>=this.limit?false:(this.used+=1,true)}},qo={"gem.requestAppLinks":["onRequestAppLinks","gem.appLinksResult"],"gem.requestOpenApp":["onRequestOpenApp","gem.openAppResult"],"gem.requestOpenGame":["onRequestOpenGame","gem.openGameResult"],"gem.requestSimilarGames":["onRequestSimilarGames","gem.similarGamesResult"],"gem.requestStoreOffers":["onRequestStoreOffers","gem.storeOffersResult"],"gem.requestStorePurchase":["onRequestStorePurchase","gem.storePurchaseResult"],"gem.requestStoreOfferSheet":["onRequestStoreOfferSheet","gem.storeOfferSheetResult"]},$o=64,Fo="error";function No(n){let e=n.toLowerCase().replace(/[^a-z_]/g,"_").slice(0,$o);return e===""?Fo:e}function ue(n){return n===void 0?{}:{reason:No(String(n))}}function Ee(n){if(!(typeof n!="string"||n.length===0||n.length>ze))return n}function xr(n){return typeof n=="number"&&Number.isSafeInteger(n)&&n>=0?n:void 0}function jo(n){return n instanceof Error?n.message:String(n)}function Ar(n,e){if(!n.has(e)){if(n.size>=Do){let t=n.values().next().value;t!==void 0&&n.delete(t);}n.add(e);}}function ln(){return new MessageChannel}var Uo=6e4/(ge["gem.handshake"]??1),Wo=Object.freeze([100,115,130,150,180,200]),Ho=6e4,cn=class{constructor(e){this.options=e;if(!ht(e.frameOrigin))throw new Error(`GemHost: frameOrigin must be a concrete origin, got '${e.frameOrigin}'. '*' and 'null' are refused \u2014 the handshake transfers the MessagePort, so an imprecise targetOrigin gives the session away.`);if(!ht(e.embedderOrigin))throw new Error(`GemHost: embedderOrigin must be a concrete origin, got '${e.embedderOrigin}'.`);this.clock=e.clock??ce,this.inbound=new oe(this.clock),this.outbound=new oe(this.clock),this.socketWindow=new dn(this.clock,24);}options;state="idle";port=null;version=null;timeoutHandle=null;reofferHandle=null;makeChannel=ln;offered=[];offersMade=0;localDenials=0;chainEndsAt=null;gameId=null;grantedChannel=null;inventoryHintAt=null;inventoryHintTimer=null;peerCapabilities=[];lastPrefs=null;prefsCalls=0;knownMatches=new Set;askedMatches=new Set;declaredSettings=[];clock;inbound;outbound;socketWindow;get currentState(){return this.state}get frameCapabilities(){return this.peerCapabilities}frameSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}start(e=ln){if(this.state!=="idle"){this.fail("handshake_duplicate",`cannot start from state '${this.state}'`);return}if(typeof e=="function")this.makeChannel=e;else if(e&&e.port1&&e.port2){let t=e,r=false;this.makeChannel=()=>r?ln():(r=true,t);}else throw new Error("GemHost.start: pass a function returning { port1, port2 } \u2014 omit it entirely to get a fresh MessageChannel per offer, which is what an embedder normally wants.");this.transition("handshaking"),this.offer(),this.scheduleReoffer(),this.timeoutHandle=this.clock.setTimeout(()=>{this.timeoutHandle=null,this.state==="handshaking"&&this.fail("handshake_timeout",`no answer from ${this.options.frameOrigin} to ${this.offersMade} handshake ${this.offersMade===1?"offer":"offers"} in the first ${Zt}ms; the offers continue for as long as this page holds the frame. This page presented embedder origin ${this.options.embedderOrigin}. A frame answers none of them if it carries no bridge at all, if it has not started listening yet, or if that origin is missing from the embedders it will accept \u2014 this side cannot tell which, and does not try.`);},Zt);}offer(){if(!this.outbound.allow("gem.handshake"))return false;let e=this.makeChannel(),t=e.port1;return t.onmessage=r=>this.receive(r.data,t),t.start?.(),this.offered.push({port:t,at:this.clock.now()}),this.offersMade+=1,this.options.frame.postMessage({gem:g,type:"gem.handshake",versions:{min:ne.min,max:ne.max},embedder:this.options.embedderOrigin,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]},...this.options.unbidden===true?{unbidden:true}:{}},this.options.frameOrigin,[e.port2]),true}scheduleReoffer(){this.reofferHandle=this.clock.setTimeout(()=>{this.reofferHandle=null,this.state==="handshaking"&&(this.offer(),this.pruneStaleOffers(),this.scheduleReoffer());},Uo);}pruneStaleOffers(){let e=this.clock.now()-Ho;for(;this.offered.length>0&&(this.offered[0]?.at??e)<e;){let t=this.offered.shift();if(t===void 0)break;t.port.onmessage=null,t.port.close();}}adopt(e){for(let t of this.offered)t.port!==e&&(t.port.onmessage=null,t.port.close());this.offered.length=0,this.port=e;}static checkHandshakeOrigin(e,t,r){return ye(e.origin,t)?e.source!==r?"source is not the mounted frame":null:`origin '${e.origin}' is not the expected frame origin`}sendToken(e){if(!Number.isInteger(e.chainEndsAt)||e.chainEndsAt<0){this.fail("internal","refusing a grant whose chainEndsAt is not a whole number of seconds >= 0");return}let t=this.chainEndsAt??e.chainEndsAt;if(this.chainExpired()){this.fail("token_unavailable","token chain ceiling reached");return}this.send({gem:g,type:"gem.token",token:e.token,expiresAt:e.expiresAt,chainEndsAt:t,generation:e.generation,playerId:e.playerId,gameId:e.gameId,channel:e.channel,...e.gameVersion===void 0?{}:{gameVersion:e.gameVersion},...e.requestId===void 0?{}:{requestId:e.requestId},...e.apiBaseUrl===void 0?{}:{apiBaseUrl:e.apiBaseUrl},...e.allowDevTools===true?{allowDevTools:true}:{},...e.garden===void 0?{}:{garden:e.garden}})&&(this.chainEndsAt=t,this.gameId===null&&(this.gameId=e.gameId,this.grantedChannel=e.channel));}relayRoomEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(typeof t.type!="string")return false;let r=Co[t.type];if(r===void 0||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let i=t.data;if(i.game_id!==this.gameId)return false;let o=Ee(i.game_room_id);if(o===void 0)return false;let a=Mo.has(t.type)?void 0:Ee(i.match_id);a!==void 0&&this.rememberMatch(a);let s=typeof i.version=="number"?i.version:void 0,l=typeof i.reason=="string"?i.reason.slice(0,ze):void 0,d=r==="version_stale"&&Eo.includes(i.platform)?i.platform:void 0;return this.sendRoomEvent({event:r,roomId:o,...a===void 0?{}:{matchId:a},...s===void 0?{}:{version:s},...l===void 0?{}:{reason:l},...d===void 0?{}:{platform:d}})}sendRoomEvent(e){return this.frameSupports("rooms")?this.send({gem:g,type:"gem.roomEvent",event:e.event,roomId:e.roomId,...e.matchId===void 0?{}:{matchId:e.matchId},...e.version===void 0?{}:{version:e.version},...e.reason===void 0?{}:{reason:e.reason},...e.platform===void 0?{}:{platform:e.platform}}):false}sendSocketState(e){return this.frameSupports("socketState")?this.send({gem:g,type:"gem.socketState",connected:e.connected,...e.connected?{}:{reason:e.reason}}):false}sendSeatedEvent(e){return this.frameSupports("partySeating")?this.send({gem:g,type:"gem.seatedEvent",roomId:e}):false}relaySeatedEvent(e){if(this.gameId===null)return false;let t=Ke(e);if(t===null||t.event!=="gameRoomJoined"||t.gameId!==this.gameId)return false;let r=Ee(t.gameRoomId);return r===void 0?false:this.sendSeatedEvent(r)}sendAutoStartFailedEvent(e){return this.frameSupports("autoStartFailure")?this.send({gem:g,type:"gem.autoStartFailedEvent",roomId:e.roomId,code:e.code}):false}relayAutoStartFailedEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(t.type!==Oo||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let r=t.data;if(r.game_id!==this.gameId)return false;let i=Ee(r.game_room_id),o=typeof r.code=="string"&&Sr.test(r.code)?r.code:void 0;return i===void 0||o===void 0?false:this.sendAutoStartFailedEvent({roomId:i,code:o})}sendTicketEvent(e){return this.frameSupports("ticketEvents")?this.send({gem:g,type:"gem.ticketEvent",event:e.event,ticketId:e.ticketId,...e.partySize===void 0?{}:{partySize:e.partySize},...e.queuedAt===void 0?{}:{queuedAt:e.queuedAt},...e.expiresAt===void 0?{}:{expiresAt:e.expiresAt},...e.reason===void 0?{}:{reason:e.reason},...e.roomId===void 0?{}:{roomId:e.roomId}}):false}relayTicketEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(typeof t.type!="string"||!Object.hasOwn(kr,t.type))return false;let r=kr[t.type];if(r===void 0||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let i=t.data;if(i.game_id!==this.gameId)return false;let o=Ee(i.ticket_id);if(o===void 0)return false;if(r==="queued"){let s=typeof i.party_size=="number"&&Number.isInteger(i.party_size)&&i.party_size>=1&&i.party_size<=Lo?i.party_size:void 0,l=xr(i.queued_at),d=xr(i.expires_at);return this.sendTicketEvent({event:r,ticketId:o,...s===void 0?{}:{partySize:s},...l===void 0?{}:{queuedAt:l},...d===void 0?{}:{expiresAt:d}})}if(r==="matched"){let s=Ee(i.game_room_id);return this.sendTicketEvent({event:r,ticketId:o,...s===void 0?{}:{roomId:s}})}let a=typeof i.reason=="string"&&Sr.test(i.reason)?i.reason:void 0;return this.sendTicketEvent({event:r,ticketId:o,...a===void 0?{}:{reason:a}})}relayInventoryEvent(e){if(this.gameId===null||typeof e!="object"||e===null||Array.isArray(e))return false;let t=e;if(t.type!==Go||typeof t.data!="object"||t.data===null||Array.isArray(t.data))return false;let r=t.data;return r.game_id!==this.gameId||r.channel!==this.grantedChannel?false:this.sendInventoryChanged()}sendInventoryChanged(){if(!this.frameSupports("inventoryChanged"))return false;if(this.state!=="connected")return this.flushInventoryHint();if(this.inventoryHintTimer!==null)return true;let e=this.inventoryHintAt===null?1/0:this.clock.now()-this.inventoryHintAt;return e>=Je&&this.flushInventoryHint()||this.armInventoryHint(e>=Je?Je:Je-e),true}flushInventoryHint(){let e=this.send({gem:g,type:"gem.inventoryChanged"});return e&&(this.inventoryHintAt=this.clock.now()),e}armInventoryHint(e){this.inventoryHintTimer=this.clock.setTimeout(()=>{this.inventoryHintTimer=null,this.state==="connected"&&(this.flushInventoryHint()||this.armInventoryHint(Je));},e);}relaySocketEvent(e){let t=[];(this.relayRoomEvent(e)||this.relayAutoStartFailedEvent(e))&&t.push("room"),this.relaySignalEvent(e)&&t.push("signal"),this.relayDedicatedEvent(e)&&t.push("dedicated"),this.relayTicketEvent(e)&&t.push("ticket"),this.relayInventoryEvent(e)&&t.push("inventory");let r=Ke(e);return r!==null&&(this.options.onPartyEvent?.(r),this.relaySeatedEvent(e),t.push("party")),t}relaySignalEvent(e){if(this.gameId===null)return false;let t=wt(e,this.gameId,r=>this.knowsMatch(r));return t===null?false:(t.event==="hello"&&this.rememberMatch(t.matchId),this.sendSignalEvent(t))}sendSignalEvent(e){if(!this.frameSupports("p2p"))return false;if(e.event==="relayDraining"){if(!this.frameSupports("p2pRelayDrain"))return false;let t=e.data;return this.send({gem:g,type:"gem.relayDraining",matchId:e.matchId,clusterTargetId:t.clusterTargetId,relayIps:t.relayIps,drainingSince:t.drainingSince,deadline:t.deadline})}return this.send({gem:g,type:"gem.signalEvent",event:e.event,matchId:e.matchId,data:e.data})}relayDedicatedEvent(e){if(this.gameId===null)return false;let t=_t(e,this.gameId,r=>this.knowsMatch(r));return t===null?false:this.sendDedicatedEvent(t)}sendDedicatedEvent(e){return this.frameSupports("dedicated")?this.send({gem:g,type:"gem.dedicatedEvent",event:e.event,matchId:e.matchId,...e.sdp===void 0?{}:{sdp:e.sdp},...e.sessionId===void 0?{}:{sessionId:e.sessionId},...e.code===void 0?{}:{code:e.code},...e.message===void 0?{}:{message:e.message},...e.reason===void 0?{}:{reason:e.reason}}):false}knowsMatch(e){return this.knownMatches.has(e)||this.askedMatches.has(e)}rememberMatch(e){Ar(this.knownMatches,e);}rememberAsked(e){Ar(this.askedMatches,e);}answerPartyRequest(e,t){return this.send({gem:g,type:"gem.partyResult",requestId:e,...t===void 0?{}:{partyId:t}})}answerPurchase(e,t,r,i){return this.send({gem:g,type:"gem.purchaseResult",requestId:e,outcome:t,...r===void 0||t!=="ok"?{}:{purchaseId:r},...i===void 0||t==="ok"?{}:{reason:i}})}sendPurchaseSettled(e){return !this.options.capabilities?.includes("purchaseSettled")||!this.frameSupports("purchaseSettled")?false:this.send({gem:g,type:"gem.purchaseSettled",purchaseKind:e.purchaseKind,targetId:e.targetId,...e.catalogId===void 0?{}:{catalogId:e.catalogId},quantity:e.quantity,...e.purchaseId===void 0?{}:{purchaseId:e.purchaseId}})}answerDeviceSettingsPut(e,t){return this.send({gem:g,type:"gem.deviceSettings.result",requestId:e,outcome:t})}sendDeviceSettings(e){return this.frameSupports("deviceSettings")?this.send({gem:g,type:"gem.deviceSettings",...e===void 0?{}:{settings:e}}):false}sendMenuSelect(e){return this.frameSupports("gameMenu")?this.send(e==="settings"?{gem:g,type:"gem.menuSelect",entry:"settings"}:{gem:g,type:"gem.menuSelect",entry:"item",id:e.id}):false}sendSettingChange(e,t){if(!this.frameSupports("gameSettings"))return false;let r=this.declaredSettings.find(o=>o.id===e);if(r===void 0)return false;let i=hr(r,t);return i===void 0?false:this.send({gem:g,type:"gem.settingChange",id:e,value:i})}answerAd(e,t){return this.send({gem:g,type:"gem.adResult",requestId:e,outcome:t})}answerMoment(e,t){return this.send({gem:g,type:"gem.momentResult",requestId:e,shown:t})}answerSignIn(e,t){return this.send({gem:g,type:"gem.signInResult",requestId:e,outcome:t})}answerFriendChallenge(e,t,r={}){let i={};if(t==="invalid"){let{code:o,param:a,channel:s,configVersion:l,detail:d}=r;typeof o=="string"&&o.length<=64&&(i.code=o),typeof a=="string"&&a.length<=64&&(i.param=a),typeof s=="string"&&s.length<=16&&(i.channel=s),typeof l=="number"&&Number.isSafeInteger(l)&&l>=0&&(i.configVersion=l),typeof d=="string"&&(i.detail=d.slice(0,300));}else if(t==="rate_limited"){let o=r.retryAfterMs;typeof o=="number"&&Number.isFinite(o)&&(i.retryAfterMs=Math.min(864e5,Math.max(0,Math.round(o))));}return this.send({gem:g,type:"gem.friendChallengeResult",requestId:e,outcome:t,...i})}answerAppLinks(e,t,r={}){return this.send({gem:g,type:"gem.appLinksResult",requestId:e,outcome:t,...ue(r.reason),...r.arcade===void 0?{}:{arcade:{store:r.arcade}},...r.game===void 0?{}:{game:{store:r.game}}})}answerShare(e,t,r={}){return !this.options.capabilities?.includes("shareResult")||!this.frameSupports("shareResult")?false:this.send({gem:g,type:"gem.shareResult",requestId:e,outcome:t,...t==="refused"?ue(r.reason):{},...t==="refused"&&r.key!==void 0?{key:r.key}:{}})}answerOpenApp(e,t,r){return this.send({gem:g,type:"gem.openAppResult",requestId:e,outcome:t,...ue(r)})}answerOpenGame(e,t,r){return this.send({gem:g,type:"gem.openGameResult",requestId:e,outcome:t,...ue(r)})}answerSimilarGames(e,t,r=[],i){return this.send({gem:g,type:"gem.similarGamesResult",requestId:e,outcome:t,...ue(i),games:t==="ok"?[...r]:[]})}answerStoreOffers(e,t,r=[],i){return this.send({gem:g,type:"gem.storeOffersResult",requestId:e,outcome:t,...ue(i),offers:t==="ok"?[...r]:[]})}answerStorePurchase(e,t,r){return this.send({gem:g,type:"gem.storePurchaseResult",requestId:e,outcome:t,...ue(r)})}answerStoreOfferSheet(e,t,r){return this.send({gem:g,type:"gem.storeOfferSheetResult",requestId:e,outcome:t,...ue(r)})}sendStorePurchaseSettled(e){return this.frameSupports("store")?this.send({gem:g,type:"gem.storePurchaseSettled",skuId:e}):false}answerPlayInvite(e,t){return this.send({gem:g,type:"gem.playInviteResult",requestId:e,shown:t})}denyToken(e){this.send({gem:g,type:"gem.token.denied",requestId:e.requestId,retryable:e.retryable,reason:e.reason,correlationId:e.correlationId,...e.retryAfterMs===void 0?{}:{retryAfterMs:e.retryAfterMs}});}grantLocalServer(e){return this.frameSupports("localHost")?this.send({gem:g,type:"gem.localServer",requestId:e.requestId,...e.serverId===void 0?{}:{serverId:e.serverId}}):false}denyLocalServer(e){return this.frameSupports("localHost")?this.send({gem:g,type:"gem.localServer.denied",requestId:e.requestId,retryable:e.retryable,reason:e.reason}):false}sendClusterTarget(e){let t=typeof e=="string"?[e]:e,r=Array.isArray(t)?t.length===0?"[]":t.find(o=>typeof o!="string"||o.trim().length===0||o.length>ze):String(t);if(r!==void 0)return this.fail("internal",`refusing to send '${String(r)}' as a cluster target \u2014 each id must be a non-blank string of at most ${ze} characters, and a list must name at least one. Omit the message entirely when there is no answer: the frame then names no cluster and gem-api chooses.`),false;if(t.length>3)return this.fail("internal",`refusing to send ${t.length} cluster targets \u2014 at most ${3} acceptable clusters cross, best first.`),false;if(!this.frameSupports("clusterTarget"))return false;let[i]=t;return this.send({gem:g,type:"gem.clusterTarget",clusterTargetId:i,...typeof e=="string"?{}:{clusterTargetIds:[...t]}})}sendGraphicsRoutes(e){let t=dr(e);return t===null?(this.fail("internal","refusing to send an unusable graphics route list \u2014 at most 32 entries, each with a `gpu` and optionally a `route` and a `host`, every one a non-empty string of at most 32 characters."),false):this.frameSupports("graphicsRoutes")?this.send({gem:g,type:"gem.graphicsRoutes",entries:t.map(r=>({...r}))}):false}sendPlayer(e){let{displayName:t,displayTag:r}=e.untrusted,i=e.badges?.map(({itemId:m,iconUrl:c})=>({itemId:m,iconUrl:c})),o=e.boost&&{multipliers:e.boost.multipliers.map(({currencyId:m,currencySlug:c,multiplierBp:p})=>({currencyId:m,currencySlug:c,multiplierBp:p})),...e.boost.expiresInDays===void 0?{}:{expiresInDays:Math.min(Po,e.boost.expiresInDays)}},a=e.discriminator!==void 0&&/^[0-9]{1,8}$/.test(e.discriminator)?e.discriminator:void 0,s=e.avatarIconId!==void 0&&an.test(e.avatarIconId)?e.avatarIconId:void 0,l=e.avatar3dId!==void 0&&an.test(e.avatar3dId)?e.avatar3dId:void 0,d;if(e.avatars3d!==void 0){let m=new Set;d=[];for(let{family:c,id:p,modelUrl:y,iconUrl:b}of e.avatars3d){if(d.length===To)break;!an.test(p)||!Io.test(c)||m.has(c)||y.length>wr||b.length>wr||(m.add(c),d.push({family:c,id:p,modelUrl:y,iconUrl:b}));}}this.send({gem:g,type:"gem.player",untrusted:{displayName:t,...r===void 0?{}:{displayTag:r},displayHandle:r??t},...e.playerId===void 0?{}:{playerId:e.playerId},...a===void 0?{}:{discriminator:a},...e.avatarIconUrl===void 0?{}:{avatarIconUrl:e.avatarIconUrl},...s===void 0?{}:{avatarIconId:s},...l===void 0?{}:{avatar3dId:l},...e.avatar3dModelUrl===void 0?{}:{avatar3dModelUrl:e.avatar3dModelUrl},...e.avatar3dIconUrl===void 0?{}:{avatar3dIconUrl:e.avatar3dIconUrl},...d===void 0?{}:{avatars3d:d},...i===void 0?{}:{badges:i},...o===void 0?{}:{boost:o},...typeof e.signInAvailable=="boolean"?{signInAvailable:e.signInAvailable}:{},displayTag:a??"",...e.avatarIconUrl===void 0?{}:{avatarUrl:e.avatarIconUrl}});}chainExpired(){return this.chainEndsAt!==null&&this.clock.now()/1e3>=this.chainEndsAt}localCorrelationId(){return `local-${++this.localDenials}`}sendPause(e){this.send({gem:g,type:"gem.pause",paused:e});}sendDevtools(e){this.send({gem:g,type:"gem.devtools",action:e});}requestScreenshot(e,t){return this.frameSupports("screenshot")?this.send({gem:g,type:"gem.screenshot",maxWidth:t.maxWidth,maxHeight:t.maxHeight},[e]):false}requestContext(){return this.frameSupports("feedbackContext")?this.send({gem:g,type:"gem.requestContext"}):false}sendPrefs(e){this.lastPrefs={...e},this.prefsCalls+=1;let{recommended:t}=e;if(t===void 0||!("textScale"in t)){this.send({gem:g,type:"gem.prefs",...e});return}let{textScale:r,...i}=t,o={gem:g,type:"gem.prefs",...e,recommended:i};if(!(this.frameSupports("textScale")&&typeof r=="number"&&Wo.includes(r))){this.send(o);return}let s={...o,recommended:{...i,textScale:r}};de["gem.prefs"]?.(s)?this.send(o):this.send(s);}sendCohorts(e){return this.frameSupports("cohorts")?this.send({gem:g,type:"gem.cohorts",cohorts:e.map(t=>({experimentId:t.experimentId,variantKey:t.variantKey}))}):false}sendEarnAllowanceChanged(){return this.frameSupports("earnAllowance")?this.send({gem:g,type:"gem.earnAllowanceChanged"}):false}sendViewport(e){if(!this.frameSupports("viewport"))return false;let t=(e.reserved??[]).map(({x:o,y:a,width:s,height:l,control:d})=>({x:o,y:a,width:s,height:l,...d?{control:{x:d.x,y:d.y,width:d.width,height:d.height}}:{}})),r=this.frameSupports("reservedAreas"),i=e.insets.top;if(!r)for(let o of t)o.y===0&&(i=Math.max(i,o.height));return this.send({gem:g,type:"gem.viewport",insets:{top:i,right:e.insets.right,bottom:e.insets.bottom,left:e.insets.left},...r?{reserved:t}:{}})}sendWillTerminate(e,t){this.send({gem:g,type:"gem.willTerminate",reason:e,graceMs:t});}close(){this.timeoutHandle!==null&&(this.clock.clearTimeout(this.timeoutHandle),this.timeoutHandle=null),this.inventoryHintTimer!==null&&(this.clock.clearTimeout(this.inventoryHintTimer),this.inventoryHintTimer=null),this.reofferHandle!==null&&(this.clock.clearTimeout(this.reofferHandle),this.reofferHandle=null);for(let e of this.offered)e.port.onmessage=null,e.port.close();this.offered.length=0,this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="unsupported"&&this.transition("closed");}transition(e){this.state!==e&&(this.state=e,this.options.onStateChange?.(e));}fail(e,t){this.options.onError?.(e,t);}send(e,t){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let r=e.type;if(!this.outbound.allow(r))return this.fail("rate_limited",`parent exceeded its own limit for ${r}`),false;let i=de[r]?.(e);return i?(this.fail("internal",`refusing to send malformed ${r}: ${i}`),false):(t===void 0?this.port.postMessage({...e,v:this.version}):this.port.postMessage({...e,v:this.version},t),true)}receive(e,t){if(this.state==="closed"||this.state==="unsupported")return;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string"){this.refuseMalformed("invalid_message","message has no type");return}let i=de[r];if(!i){this.refuseMalformed("unsupported",`unknown message type ${JSON.stringify(r.slice(0,ze))}`);return}if(r==="gem.controlsDeclare")return;if(!this.inbound.allow(r)){this.fail("rate_limited",`frame exceeded the limit for ${r}`);return}let o=he(r);if(o!==void 0&&!this.options.capabilities?.includes(o)){this.fail("unsupported_capability",`${r} requires the '${o}' capability, which this parent did not advertise`);return}let a=i(e);if(a){this.fail("invalid_message",`${r}: ${a}`);return}let s=e;r!=="gem.handshake.accept"&&!this.versionMatches(s,r)||this.dispatch(r,s,t);}refuseMalformed(e,t){if(!this.inbound.allow(xe)){this.options.onDropped?.(e,t);return}this.fail(e,t);}versionMatches(e,t){return e.v===this.version?true:(this.fail("invalid_message",`${t}: expected v ${String(this.version)}, got ${String(e.v)}`),false)}forwardSignal(e){let t=this.options.onSendSignal;if(t===void 0){this.fail("unsupported","this parent advertised `p2p` but wired no onSendSignal, so signaling has nowhere to go");return}if(this.gameId===null){this.options.onDropped?.("internal","gem.signal before any token was issued");return}if(e.signal!=="join"&&!this.knownMatches.has(e.matchId)){this.fail("unsupported",`gem.signal names match '${e.matchId}', which this player's socket has not mentioned`);return}if(!this.socketWindow.allow()){this.fail("rate_limited","signaling exceeded this parent per-second socket budget");return}let r=Rt(e.signal,e.matchId,this.gameId,e.data);if("reason"in r){this.fail("invalid_message",`gem.signal: ${r.reason}`);return}this.withinFrameSize("gem.signal",r.frame,"Trickle ICE candidates rather than gathering them into the offer.")&&(this.rememberAsked(e.matchId),t(r.frame));}forwardDedicatedOffer(e){let t=this.options.onSendSignal;if(t===void 0){this.fail("unsupported","this parent advertised `dedicated` but wired no onSendSignal, so the offer has nowhere to go");return}if(this.gameId===null){this.options.onDropped?.("internal","gem.dedicatedOffer before any token was issued");return}if(!this.socketWindow.allow()){this.fail("rate_limited","signaling exceeded this parent per-second socket budget");return}let r=vt(e.matchId,this.gameId,{sdp:e.sdp,intent:e.intent});if("reason"in r){this.fail("invalid_message",`gem.dedicatedOffer: ${r.reason}`);return}if(this.withinFrameSize("gem.dedicatedOffer",r.frame,"A complete gathered offer fits; one that does not is carrying media sections this path does not permit, or candidates from ICE servers this path does not use.")){this.rememberAsked(e.matchId);try{t(r.frame);}catch(i){this.sendDedicatedEvent({event:"error",matchId:e.matchId,code:"embedder_relay_unavailable",message:"the page could not put the offer on the player socket"}),this.fail("internal",`gem.dedicatedOffer: the socket refused the send: ${jo(i)}`);}}}withinFrameSize(e,t,r){let i=JSON.stringify(t).length;return i<=16384?true:(this.fail("invalid_message",`${e} builds a ${i}-byte socket frame, over the ${16384}-byte limit gem-api enforces, so the socket would drop it and then fail to establish. ${r}`),false)}dispatch(e,t,r){if(e==="gem.handshake.accept"){if(this.state!=="handshaking"){this.fail("handshake_duplicate","handshake already settled");return}let i=t.v;if(i<ne.min||i>ne.max){this.transition("unsupported"),this.fail("version_mismatch",`frame chose v${i}, outside our supported range`),this.close();return}this.adopt(r),this.version=i,this.peerCapabilities=t.capabilities??[],this.timeoutHandle!==null&&(this.clock.clearTimeout(this.timeoutHandle),this.timeoutHandle=null),this.reofferHandle!==null&&(this.clock.clearTimeout(this.reofferHandle),this.reofferHandle=null),this.transition("connected");return}if(this.state!=="connected"){this.fail("invalid_message",`${e} received before the handshake completed`);return}switch(e){case "gem.ready":this.options.onReady?.();break;case "gem.engaged":this.options.onEngaged?.();break;case "gem.progress":this.options.onProgress?.(t);break;case "gem.requestToken":{let i=t;if(this.chainExpired()){this.denyToken({requestId:i.requestId,retryable:false,reason:"chain_exhausted",correlationId:this.localCorrelationId()});break}this.options.onRequestToken?.(i);break}case "gem.requestLocalServer":{this.options.onRequestLocalServer?.(t);break}case "gem.exit":this.options.onExit?.(t),this.close();break;case "gem.openExternal":this.options.onOpenExternal?.(t);break;case "gem.requestFullscreen":this.options.onRequestFullscreen?.(t);break;case "gem.audioState":{let i=t,o=this.prefsCalls;this.options.onAudioState?.(i),this.prefsCalls===o&&this.lastPrefs!==null&&this.lastPrefs.muted!==i.muted&&this.sendPrefs({...this.lastPrefs,muted:i.muted});break}case "gem.rewardCue":this.options.onRewardCue?.(t);break;case "gem.walletDisplay":this.options.onWalletDisplay?.(t);break;case "gem.menu":this.options.onMenu?.(t);break;case "gem.gemCorner":this.options.onGemCorner?.(t);break;case "gem.rewardPresentation":this.options.onRewardPresentation?.(t);break;case "gem.menuStyle":this.options.onMenuStyle?.(t);break;case "gem.inviteParams":this.options.onInviteParams?.(t);break;case "gem.settingsDeclare":{let i=fr(t.settings);this.declaredSettings=i??[],this.options.onSettingsDeclare?.({gem:g,type:"gem.settingsDeclare",settings:this.declaredSettings.map(o=>({...o}))});break}case "gem.frameStats":this.options.onFrameStats?.(t);break;case "gem.portraitBorders":{let{name:i}=t;i==null?this.options.onPortraitBorders?.(null):nn(i)&&this.options.onPortraitBorders?.(i);break}case "gem.screenshot.denied":this.options.onScreenshotDenied?.(t);break;case "gem.gameContext":this.options.onGameContext?.(t);break;case "gem.signal":this.forwardSignal(t);break;case "gem.dedicatedOffer":this.forwardDedicatedOffer(t);break;case "gem.partyRequest":{let i=t;if(this.options.onPartyRequest===void 0){this.answerPartyRequest(i.requestId);break}this.options.onPartyRequest(i);break}case "gem.requestPurchase":{let i=t;if(!this.frameSupports("purchases")){this.fail("unsupported_capability","refusing gem.requestPurchase: the frame did not advertise 'purchases', so it cannot receive gem.purchaseResult. Nothing was prompted and nothing was spent.");break}if(this.options.onRequestPurchase===void 0){this.answerPurchase(i.requestId,"unavailable");break}this.options.onRequestPurchase(i);break}case "gem.requestAvatar3dPurchase":{let i=t;if(!this.frameSupports("avatar3dPurchases")||!this.frameSupports("purchases")){this.fail("unsupported_capability","refusing gem.requestAvatar3dPurchase: the frame did not advertise 'avatar3dPurchases' and 'purchases', so it cannot receive gem.purchaseResult. Nothing was prompted and nothing was spent.");break}if(this.options.onRequestAvatar3dPurchase===void 0){this.answerPurchase(i.requestId,"unavailable");break}this.options.onRequestAvatar3dPurchase(i);break}case "gem.deviceSettings.put":{let i=t;if(!this.frameSupports("deviceSettings")){this.fail("unsupported_capability","refusing gem.deviceSettings.put: the frame did not advertise 'deviceSettings', so it cannot receive gem.deviceSettings.result. Nothing was stored.");break}if(this.options.onDeviceSettingsPut===void 0){this.answerDeviceSettingsPut(i.requestId,"unavailable");break}this.options.onDeviceSettingsPut(i);break}case "gem.requestAd":{let i=t;if(!this.frameSupports("ads")){this.fail("unsupported_capability","refusing gem.requestAd: the frame did not advertise 'ads', so it cannot receive gem.adResult. Nothing was rendered.");break}if(this.options.onRequestAd===void 0){this.answerAd(i.requestId,"no_ad");break}this.options.onRequestAd(i);break}case "gem.signalMoment":{let i=t;if(!this.frameSupports("moments")){this.fail("unsupported_capability","refusing gem.signalMoment: the frame did not advertise 'moments', so it cannot receive gem.momentResult. Nothing was rendered.");break}if(this.options.onSignalMoment===void 0){this.answerMoment(i.requestId,false);break}this.options.onSignalMoment(i);break}case "gem.requestSignIn":{let i=t;if(!this.frameSupports("signIn")){this.fail("unsupported_capability","refusing gem.requestSignIn: the frame did not advertise 'signIn', so it cannot receive gem.signInResult. Nothing was rendered.");break}if(this.options.onRequestSignIn===void 0){this.answerSignIn(i.requestId,"unavailable");break}this.options.onRequestSignIn(i);break}case "gem.requestFriendChallenge":{let i=t;if(!this.frameSupports("friendChallenges")){this.fail("unsupported_capability","refusing gem.requestFriendChallenge: the frame did not advertise 'friendChallenges', so it cannot receive gem.friendChallengeResult. Nothing was previewed, rendered or sent.");break}if(this.options.onRequestFriendChallenge===void 0){this.answerFriendChallenge(i.requestId,"unavailable");break}this.options.onRequestFriendChallenge(i);break}case "gem.requestAppLinks":case "gem.requestOpenApp":case "gem.requestOpenGame":case "gem.requestSimilarGames":case "gem.requestStoreOffers":case "gem.requestStorePurchase":case "gem.requestStoreOfferSheet":{let i=he(e);if(!this.frameSupports(i)){this.fail("unsupported_capability",`refusing ${e}: the frame did not advertise '${i}', so it cannot receive the answer. Nothing was rendered.`);break}let[o,a]=qo[e],s=this.options[o];if(s===void 0){this.send({gem:g,type:a,requestId:t.requestId,outcome:"unavailable",...a==="gem.similarGamesResult"?{games:[]}:{},...a==="gem.storeOffersResult"?{offers:[]}:{}});break}s.call(this.options,t);break}case "gem.requestPlayInvite":{let i=t;if(!this.frameSupports("playInvites")){this.fail("unsupported_capability","refusing gem.requestPlayInvite: the frame did not advertise 'playInvites', so it cannot receive gem.playInviteResult. Nothing was rendered.");break}if(this.options.onRequestPlayInvite===void 0){this.answerPlayInvite(i.requestId,false);break}this.options.onRequestPlayInvite(i);break}case "gem.requestShare":if(!this.frameSupports("share")){this.fail("unsupported_capability","refusing gem.requestShare: the frame did not advertise 'share'. Nothing was shared.");break}this.options.onRequestShare?.(t);break;case "gem.requestAvatarPicker":if(!this.frameSupports("avatarPicker")){this.fail("unsupported_capability","refusing gem.requestAvatarPicker: the frame did not advertise 'avatarPicker'. Nothing was opened.");break}{let i=t.tab;this.options.onRequestAvatarPicker?.(i==="2d"||i==="3d"?{tab:i}:{});}break;case "gem.vibrate":{if(!this.frameSupports("vibration")){this.fail("unsupported_capability","refusing gem.vibrate: the frame did not advertise 'vibration'. Nothing was played.");break}let i=t,o=i.pattern.reduce((a,s)=>a+s,0);if(o>1e4){this.fail("invalid_message",`refusing gem.vibrate: the pattern totals ${o} ms, over ${1e4}. Nothing was played.`);break}this.options.onVibrate?.(i);break}case "gem.error":this.fail(t.code,t.message);break;default:this.fail("unsupported",`no parent handler for '${e}'`);}}};var Tr="[gem: use gem.text.toPlain()]",Pr=new Set,Ir;function Bo(n){try{if(Pr.has(n))return;let e=globalThis.location;if(Ir??=!!e?.port&&Te(e.hostname??""),!Ir)return;Pr.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Tr}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
2
|
+
${new Error().stack}`);}catch{}}var B=class{#e;#t;constructor(e,t="player text"){this.#e=e,this.#t=t,Object.freeze(this);}toString(){return Bo(this.#t),Tr}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function be(n,e){return new B(n,e)}var Vo={toPlain(n){return B.unwrap(n)},compare(n,e,t){let r=n instanceof B?B.unwrap(n):n,i=e instanceof B?B.unwrap(e):e;if(typeof Intl>"u"||typeof Intl.Collator!="function")return r<i?-1:r>i?1:0;let o;try{o=new Intl.Collator(t);}catch{o=new Intl.Collator;}return o.compare(r,i)},equals(n,e){let t=n instanceof B?B.unwrap(n):n,r=e instanceof B?B.unwrap(e):e;return t.normalize("NFC")===r.normalize("NFC")}};function Er(n){return typeof n=="string"&&/^[\w-]{1,64}$/.test(n)}function Cr(n){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function Ko(n,e){if(n!==true)return {visible:false};let t=[],r=false;for(let i of Array.isArray(e)?e:[])i==="score"?r=true:typeof i=="string"&&i.length>0&&i.length<=64&&!t.includes(i)&&t.push(i);return !r&&t.length===0?{visible:true}:{visible:true,currencies:t.slice(0,8),score:r}}function Jo(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:i}=t;typeof r!="string"||r.length===0||r.length>32||typeof i!="string"||i.trim().length===0||e.some(o=>o.id===r)||e.push({id:r,label:i.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function Mr(n,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function zo(n){let e=n.untrusted,t=be(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,i=r?be(r,"gem.player.displayTag"):void 0,o=n.discriminator||n.displayTag,a=o&&/^[0-9]{1,8}$/.test(o)?o:void 0,s=n.avatarIconUrl||n.avatarUrl,l=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,d=n.avatarIconId,m=n.avatar3dId,c={displayName:t,displayHandle:i??t,badges:(n.badges??[]).map(v=>({itemId:v.itemId,iconUrl:v.iconUrl}))};n.playerId&&(c.playerId=n.playerId),a&&(c.discriminator=a),i&&(c.displayTag=i),s&&(c.avatarIconUrl=c.avatarUrl=s),d&&l.test(d)&&(c.avatarIconId=d),m&&l.test(m)&&(c.avatar3dId=m),n.avatar3dModelUrl&&(c.avatar3dModelUrl=n.avatar3dModelUrl),n.avatar3dIconUrl&&(c.avatar3dIconUrl=n.avatar3dIconUrl),n.avatars3d&&(c.avatars3d=n.avatars3d.filter(v=>l.test(v.id)&&/^[a-z][a-z0-9_]*$/.test(v.family)).filter((v,A,M)=>M.findIndex(E=>E.family==v.family)==A).map(({family:v,id:A,modelUrl:M,iconUrl:E})=>({family:v,id:A,modelUrl:M,iconUrl:E})));let p=n.boost,y=(p?.multipliers??[]).map(v=>({currencyId:v.currencyId,currencySlug:v.currencySlug,multiplier:v.multiplierBp/1e4})),b=y.some(v=>v.multiplier>1);return c.boost=b&&p?.expiresInDays?{active:b,multipliers:y,expiresInDays:p.expiresInDays}:{active:b,multipliers:y},c}var Xo=1e4,Yo=["appLinks","openGame","store","storeOfferSheet"],Qo=["token","pause","menuSelect","settingChange","prefs","cohorts","viewport","willTerminate","localServer","roomEvent","seatedEvent","autoStartFailedEvent","ticketEvent","socketState","signalEvent","relayDraining","dedicatedEvent","clusterTarget","graphicsRoutes","partyResult","purchaseResult","purchaseSettled","inventoryChanged","shareResult","deviceSettings","adResult","momentResult","signInResult","friendChallengeResult","playInviteResult"],Zo=new WeakMap;var un=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??ce,this.inbound=new oe(this.clock),this.outbound=new oe(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(s=>ye(s,e.origin))||_r(e.origin,this.options.selfOrigin)||(Zo.get(this)??[]).some(s=>ye(s,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=de["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let i=e.data;if(!ye(i.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${i.embedder}' but arrived from '${e.origin}'`),false;let o=e.ports?.[0];if(!o)return this.refuse(e,"handshake transferred no MessagePort"),false;let a=sn(ne,i.versions);return a===null?(o.onmessage=null,o.postMessage({gem:g,type:"gem.error",code:"version_mismatch",message:`frame supports v${ne.min}..v${ne.max}`}),o.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=o,this.version=a,o.onmessage=s=>this.receive(s.data,s.ports??[]),o.start?.(),this.peerCapabilities=i.capabilities??[],this.unbidden=i.unbidden===true,o.postMessage({gem:g,type:"gem.handshake.accept",v:a,...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:g,type:"gem.ready"});}settled(e=Xo){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(t=>{let r=o=>{this.clock.clearTimeout(i),t(o);},i=this.clock.setTimeout(()=>{let o=this.settleWaiters.indexOf(r);o>=0&&this.settleWaiters.splice(o,1),t(this.state);},e);this.settleWaiters.push(r);})}progress(e,t){this.send({gem:g,type:"gem.progress",loaded:e,total:t});}requestToken(e,t){this.send({gem:g,type:"gem.requestToken",requestId:e,reason:t});}requestLocalServer(e,t){return this.parentSupports("localHost")?(this.send({gem:g,type:"gem.requestLocalServer",requestId:e,gameRoomId:t}),true):false}exit(e){this.send(e===void 0?{gem:g,type:"gem.exit"}:{gem:g,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:g,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:g,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:g,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:g,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let t={gem:g,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return Er(e.cueId)&&(t.cueId=e.cueId),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:Cr(e.origin.x),y:Cr(e.origin.y)}),this.send(t),true}showPortraitBorders(e){let t=cr(e);return t===void 0||!this.gated("portraitBorders")?false:this.send(t===null?{gem:g,type:"gem.portraitBorders"}:{gem:g,type:"gem.portraitBorders",name:t})}setWalletVisible(e,t){return this.gated("walletDisplay")?this.send({gem:g,type:"gem.walletDisplay",...Ko(e,t?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:g,type:"gem.menu",...Jo(e)}):false}declareSettings(e){return this.gated("gameSettings")?this.send({gem:g,type:"gem.settingsDeclare",settings:e}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:g,type:"gem.gemCorner",corner:e}):false}setRewardPresentation(e){return this.gated("rewardPresentation")?this.send({gem:g,type:"gem.rewardPresentation",mode:e}):false}setMenuStyle(e){return this.gated("menuStyle")?this.send(e===null?{gem:g,type:"gem.menuStyle"}:{gem:g,type:"gem.menuStyle",ground:e.ground,accent:e.accent}):false}requestShare(e,t){return this.gated("share")?this.send({gem:g,type:"gem.requestShare",...t===void 0?{}:{requestId:t},params:e.map(({key:r,value:i})=>({key:r,value:i}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:g,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:g,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:g,type:"gem.frameStats",fps:Mr(e.fps,1e3),frameTimeP95Ms:Mr(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:g,type:"gem.gameContext",context:e}),true)}signal(e,t,r){this.send({gem:g,type:"gem.signal",signal:e,matchId:t,data:r});}dedicatedOffer(e,t,r){this.send({gem:g,type:"gem.dedicatedOffer",matchId:e,sdp:t,intent:r});}requestParty(e){return this.send({gem:g,type:"gem.partyRequest",requestId:e})}requestPurchase(e,t,r,i,o,a,s){return this.send({gem:g,type:"gem.requestPurchase",requestId:e,purchaseKind:t,targetId:r,currencySlug:i,...o===void 0?{}:{quantity:o},...a===void 0?{}:{catalogId:a},...s===void 0?{}:{origin:s}})}requestAvatar3dPurchase(e,t,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:g,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:t,currencySlug:r}):false}putDeviceSettings(e,t){return this.send({gem:g,type:"gem.deviceSettings.put",requestId:e,settings:t})}requestAd(e,t,r){return this.send({gem:g,type:"gem.requestAd",requestId:e,placement:t,format:r})}signalMoment(e,t,r,i){return this.send({gem:g,type:"gem.signalMoment",requestId:e,kind:t,...r===void 0?{}:{placement:r},...i===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:g,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,t,r,i){return this.send({gem:g,type:"gem.requestFriendChallenge",requestId:e,playerId:t,template:r,...i===void 0?{}:{params:i}})}requestArcade(e){return this.gated(he(e.type))&&this.send({gem:g,...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(xe)){this.options.onDropped?.(e,t);return}this.fail(e,t);}dropUnknownType(e){this.inbound.allow(xe)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,t){e.ports?.[0]?.close?.(),this.inbound.allow(xe)&&this.options.onRefused?.(t);}fail(e,t){this.options.onError?.(e,t),this.state==="connected"&&this.port&&this.send({gem:g,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=de[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||Or(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 i=de[r];if(!i)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let o=he(r);if(o!==void 0&&!this.options.capabilities?.includes(o))return this.fail("unsupported_capability",`${r} requires the '${o}' capability, which this frame did not advertise`),false;let a=i(e);if(a)return this.fail("invalid_message",`${r}: ${a}`),false;let s=e;if(s.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.devtools":this.options.onDevtools?.(s.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let l=t[0];if(!l){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let d={maxWidth:s.maxWidth,maxHeight:s.maxHeight};if(!this.options.onScreenshot){l.close(),this.screenshotDenied("not_wired",false);break}return Or(t.slice(1)),this.options.onScreenshot(d,l,(m,c)=>this.screenshotDenied(m,c)),true}case "gem.token.denied":this.options.onTokenDenied?.(s);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(s);break;case "gem.player":{let l=s;this.options.onPlayer?.(zo(l),l.signInAvailable);break}case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(s);break;case "gem.error":this.options.onError?.(s.code,s.message);break;default:if(Qo.includes(r.slice(4))){this.options[`on${r[4].toUpperCase()}${r.slice(5)}`]?.(s);break}if(Yo.includes(he(r))){this.options.onArcadeAnswer?.(s);break}this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,t){this.send({gem:g,type:"gem.screenshot.denied",reason:e,retryable:t});}};function Or(n){for(let e of n)try{e?.close?.();}catch{}}var es=16384;function ts(n){if(typeof n!="object"||n===null||Array.isArray(n))return null;let e;try{e=JSON.stringify(n);}catch{return null}return typeof e!="string"?null:ns(e)}function ns(n){let e=0;for(let t=0;t<n.length;t+=1){let r=n.charCodeAt(t);if(r<128)e+=1;else if(r<2048)e+=2;else if(r>=55296&&r<=56319){let i=n.charCodeAt(t+1);i>=56320&&i<=57343?(e+=4,t+=1):e+=3;}else e+=3;}return e}var Lr=["p2p","dedicated","unknown"],kt=["connection_setup_duration_seconds","reconnect_latency_seconds","datachannel_rtt_seconds","p2p_join_to_play_latency_seconds","ice_restart_outcome_total","connection_failures_total","transport_permanent_failures_total","transport_quality_score","host_migration_outcome_total","turn_relay_usage_ratio"];var ve=class{constructor(e){this.options=e;}options;async post(e,t){let r=this.options.gameId();if(r===null||r==="")return;let i=rs(e,this.options.transport,(this.options.now??Date.now)());i.length!==0&&await this.options.client.post(`/v1/games/${encodeURIComponent(r)}/telemetry/p2p`,{events:i},t);}report(e){this.post(e).catch(()=>{});}};function rs(n,e,t){let r=Math.floor(t/1e3);return n.filter(i=>kt.includes(i.type)).slice(0,100).map(i=>{let o=os(i.transport??e);return {event_type:i.type,timestamp:r,...o===void 0?{}:{transport:o},data:{...is(i.labels),...i.value===void 0?{}:{value:i.value}}}})}function is(n){let e={};if(typeof n!="object"||n===null)return e;let t=0;for(let[r,i]of Object.entries(n)){if(t>=4)break;r==="value"||!Gr(r)||!Gr(i)||(e[r]=i,t+=1);}return e}function Gr(n){return typeof n=="string"&&n.length>0&&n.length<=32}function os(n){if(n!==void 0)return Lr.includes(n)?n:"unknown"}function pn(n){let e=atob(n),t=new Uint8Array(e.length);for(let r=0;r<e.length;r+=1)t[r]=e.charCodeAt(r);return t}var q=class extends Error{constructor(t,r,i,o,a){super(`${t} (${r}): ${i}`);this.reason=t;this.status=r;this.detail=i;this.retryAfterSeconds=o;this.code=a;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}};var as=new Set(["timeout","credential_expired","credential_reused","join_rate_limited","signaling_failed","connection_failed","no_server_ready","start_rate_limited"]),u=class extends Error{constructor(t,r,i){super(`${t}: ${r}`,i);this.reason=t;this.name="DedicatedServerError",this.retryable=as.has(t),this.platformCode=i?.platformCode;}reason;retryable;platformCode};var Dr=["explicit","stale_cleanup","game_room_closed","replaced","migration_failed","host_left","insufficient_players","all_left","expired","migration_rate_exceeded","game_room_gone","failed","idle","reclaimed"],ls=new Set(Dr);function Ce(n){return typeof n=="string"&&ls.has(n)?n:"unknown"}var Xe="webrtc_dedicated",gn=["universal","mobile","web"];var ds="p2p_unavailable",cs="capacity_unavailable",us=2,qr=3e4,$r=1e3,Fr=2e4;async function St(n){let e=H(n.clusterTargetId),t=n.nextClusterTarget,r=0,i=n.clock??ce,o=n.retry??{},a=o.budgetMs??qr,s=o.maxDelayMs??Fr,l=o.initialDelayMs??$r,d=i.now(),m=d+a,c=0;for(;;){c++;try{let p=await fn(n.signal,"while allocating",()=>n.client.post(`${Ur(n)}/start_match`,e===void 0?{}:{cluster_target_id:e},n.signal));return {matchId:p.match_id,status:p.status}}catch(p){if(!ps(p)||a<=0)throw p;if(p.status===503&&p.code===ds)throw new u("p2p_unavailable",`room ${n.roomId} starts a peer-to-peer match, which allocates no server \u2014 so this refusal is not capacity and waiting cannot help: ${p.message}`,{cause:p});if(r=p.code===cs?r+1:0,t!==void 0&&r>=us){let M=t;t=void 0;let E=H(await M());E!==void 0&&(e=E,r=0);}let y=Math.min(s,l*2**(c-1)),b=Math.min(s,(p.retryAfterSeconds??0)*1e3),A=Math.max(y/2,b)+Math.random()*(y/2);if(i.now()+A>m)throw ms(n,p,c,i.now()-d,a);if(o.onWaiting?.({attempt:c,elapsedMs:i.now()-d,budgetMs:a,nextAttemptInMs:A,status:p.status,message:p.message}),await Pt(i,A,n.signal),n.signal?.aborted)throw mn("while waiting to allocate")}}}function ps(n){return n instanceof q?n.status===503?true:n.status===429&&n.code!=="too_many_concurrent_matches":false}function ms(n,e,t,r,i){let o=`${t} attempt${t===1?"":"s"} over ${r}ms of a ${i}ms budget`;return e.status===429?new u("start_rate_limited",`the platform was still limiting match starts for room ${n.roomId} after ${o}: ${e.message}`,{cause:e}):new u("no_server_ready",`the platform had no server ready for room ${n.roomId} after ${o}: ${e.message}`,{cause:e})}var gs=12e4,hs=1e3;async function xt(n){let{clock:e,matchId:t}=n,r=n.pollIntervalMs??hs,i=n.timeoutMs??gs,o=e.now()+i,a=Nr(n),s={seen:n.knownToExist===true};for(;;){let l=await jr(n,a,s);if(l.outcome!==null)return l.outcome;if(e.now()+r>o)throw new u("timeout",`match ${t} was still ${l.status} after ${i}ms`);await Pt(e,r,n.signal);}}async function hn(n){return (await jr(n,Nr(n),{seen:n.knownToExist===true})).outcome}function Nr(n){let e=new URLSearchParams({platform:n.version.platform,game_version:n.version.gameVersion});return `${Ur(n)}/matches/${encodeURIComponent(n.matchId)}/status?${e}`}async function jr(n,e,t){let{matchId:r}=n,i=n.clock.now(),o;try{o=await fn(n.signal,"while polling match status",()=>n.client.get(e,n.signal));}catch(a){if(a instanceof q&&a.reason==="not_found")return {status:"not_found",outcome:t.seen?{status:"ended",matchId:r,reason:"unknown"}:{status:"failed",matchId:r,reason:"not_found"}};throw a}if(t.seen=true,o.status==="ready")return {status:"ready",outcome:fs(r,o,i)};if(o.status==="failed"){let a=o.ended_reason;return typeof a=="string"?{status:"failed",outcome:{status:"ended",matchId:r,reason:Ce(a)}}:{status:"failed",outcome:{status:"failed",matchId:r,reason:o.failure_reason??"unknown"}}}return {status:o.status,outcome:null}}async function At(n){let e=await fn(n.signal,"while listing cluster targets",()=>n.client.get("/v1/ping/targets",n.signal)),t=[];for(let r of e.targets??[]){let i=r?.cluster_target_id;typeof i!="string"||i.length===0||t.push({id:i,pressure:typeof r.pressure=="string"?r.pressure:"unknown"});}return t}async function fn(n,e,t){if(n?.aborted)throw mn(e);try{return await t()}catch(r){throw n?.aborted?mn(e,r):r}}function mn(n,e){return new u("aborted",`the caller aborted ${n}`,e===void 0?void 0:{cause:e})}function fs(n,e,t){let r=e.join_credential,i=e.expires_in_seconds,o=typeof r=="string"&&typeof i=="number";return {status:"ready",matchId:n,protocol:e.protocol??"unknown",...o?{joinCredential:r,credentialExpiresAt:t+i*1e3}:{},...e.host===void 0?{}:{host:e.host},...e.port===void 0?{}:{port:e.port}}}function Ur(n){return `/v1/games/${encodeURIComponent(n.gameId)}/game-rooms/${encodeURIComponent(n.roomId)}`}function Pt(n,e,t){return t?.aborted?Promise.resolve():new Promise(r=>{let i=()=>{n.clearTimeout(o),t?.removeEventListener("abort",i),r();},o=n.setTimeout(i,e);t?.addEventListener("abort",i);})}var ys=new Set(["invalid_sdp","age_restricted","match_not_found","not_match_member","account_banned"]),bn="embedder_relay_unavailable",bs="embedder_no_relay",vs=new Set([bn]),vn=new Set([bs]),It=class{constructor(e){this.options=e;}options;pending=new Map;channel(){return new yn(this)}handleEvent(e){let t=this.pending.get(e.matchId);if(t!==void 0){if(e.event==="matchEnded"){t.refuse(new u("match_ended",`the match ended while the offer was in flight (${e.reason??"no reason given"}), so no server is left to answer it`));return}if(e.event==="answer"){if(typeof e.sdp!="string"||e.sdp.length===0){t.refuse(new u("signaling_failed","the parent relayed an answer with no SDP in it"));return}t.settle({sdp:e.sdp});return}t.refuse(_s(e.code,e.message));}}register(e,t){this.pending.get(e)?.refuse(new u("signaling_failed","a newer offer for this match superseded the one in flight")),this.pending.set(e,t);}release(e,t){this.pending.get(e)===t&&this.pending.delete(e);}transport(){let e=this.options.bridge;if(e===null)throw new u("signaling_unavailable","this game is not running inside a page that holds the player's notification socket, so there is nothing to carry an offer. Dedicated signaling needs that socket and only the page can hold one \u2014 see docs/dedicated-server-flow.md \xA72.");if(!e.parentSupports("dedicated"))throw new u("signaling_unavailable","the page embedding this game did not advertise the `dedicated` capability, so it will not relay signaling for a dedicated server. See docs/dedicated-server-flow.md \xA72.");return e}};function _s(n,e){let t=e??"the offer was refused",r=n===void 0?t:`${t} (${n})`;return new u(Rs(n),r,{platformCode:n})}function Rs(n){return n===void 0||vs.has(n)?"signaling_failed":vn.has(n)?"offer_refused":ys.has(n)?n==="invalid_sdp"?"invalid_sdp":"offer_refused":"signaling_failed"}var yn=class{constructor(e){this.hub=e;}hub;closed=false;teardown=null;exchange(e,t){if(this.closed)return Promise.reject(new u("signaling_unavailable","this signaling channel is closed"));let r;try{r=this.hub.transport();}catch(i){return Promise.reject(i)}return t?.aborted?Promise.reject(new u("aborted","the caller aborted before the offer was sent")):new Promise((i,o)=>{let a=()=>{s(),o(new u("aborted","the caller aborted the signaling exchange"));},s=()=>{t?.removeEventListener("abort",a),this.hub.release(e.matchId,l),this.teardown===s&&(this.teardown=null);},l={settle:d=>{s(),i(d);},refuse:d=>{s(),o(d);}};this.teardown=s,t?.addEventListener("abort",a),this.hub.register(e.matchId,l);try{r.dedicatedOffer(e.matchId,e.sdp,e.intent);}catch(d){l.refuse(d instanceof u?d:new u("signaling_failed",d instanceof Error?d.message:String(d),{cause:d}));}})}close(){this.closed=true,this.teardown?.(),this.teardown=null;}};var Wr="match_ended:",Hr="server_shutdown:",Br="kicked:",ws=123;function ks(n){if(typeof n!="string"||n.length===0||n.length>ws)return {kind:"other"};if(n.startsWith(Wr)){let e=Ce(n.slice(Wr.length));return e==="unknown"?{kind:"other"}:{kind:"match_ended",reason:e}}return n.startsWith(Hr)?{kind:"server_shutdown",cause:n.slice(Hr.length)}:n.startsWith(Br)?{kind:"kicked",detail:n.slice(Br.length)}:{kind:"other"}}function _n(n,e,t){let r=ks(t),i=r.kind==="kicked"?"kicked":"link_closed";n.notifyMatchDisconnected(i).catch(()=>{}),r.kind==="match_ended"&&n.reconcile({matchId:e,reason:r.reason}).catch(()=>{});}var Ss="join",xs="welcome",As="rejected",Rn=25e3,Ps=1e3,wn=1;function kn(n){return typeof n=="number"&&Number.isFinite(n)&&n>0?n:void 0}var Is={credential_expired:"credential_expired",credential_reused:"credential_reused",credential_timeout:"timeout",join_rate_limited:"join_rate_limited",expected_join:"join_refused",invalid_credential:"join_refused",credential_not_for_this_channel:"join_refused"};function Sn(n){let e=typeof n=="string"?n:"unknown";return new u(Is[e]??"join_refused",`the server runtime rejected the join credential: ${e}`)}function Ye(n){let{channel:e,clock:t}=n;return new Promise((r,i)=>{let o=false,a=Math.min(n.credentialExpiresAt-t.now(),n.ceilingMs??Rn),s=t.setTimeout(()=>v(new u("timeout","the server runtime sent no welcome before the join credential expired. The channel was open, so this is the runtime not answering rather than a connection that never came up.")),Math.max(a,Ps)),l=A=>{let M=A.data;if(typeof M!="string"){c(M);return}let E;try{E=JSON.parse(M);}catch{c(M);return}if(o){c(M);return}if(E.t===xs){let f=kn(E.heartbeat_interval);b({...typeof E.player_id=="string"?{playerId:E.player_id}:{},...typeof E.match_id=="string"?{matchId:E.match_id}:{},...f===void 0?{}:{heartbeatIntervalMs:f}});return}if(E.t===As){v(Sn(E.reason));return}c(M);},d=()=>{v(new u("connection_failed","the channel closed before the join/welcome exchange completed, so there is nothing left to present the credential on."));},m=()=>{v(new u("aborted","the caller aborted during the join exchange"));},c=A=>{o&&n.onMessage?.(A);},p=()=>{t.clearTimeout(s),e.removeEventListener("close",d),n.signal?.removeEventListener("abort",m);},y=()=>{e.removeEventListener("message",l);},b=A=>{o=true,p(),n.onMessage===void 0&&y(),r({welcome:A,detach:y});},v=A=>{o||(o=true,p(),y(),i(A));};if(e.addEventListener("message",l),e.addEventListener("close",d),n.signal?.aborted){m();return}n.signal?.addEventListener("abort",m);try{e.send(JSON.stringify({t:Ss,credential:n.credential,...n.wire===void 0?{}:{wire:n.wire},...n.heartbeat===!0?{heartbeat:wn}:{}}));}catch(A){v(new u("connection_failed",`the join frame could not be sent on the open channel: ${String(A)}`,{cause:A}));}})}function Tt(){return {exchange(){return Promise.reject(new u("signaling_unavailable","no signaling channel is wired. The exchange rides the notification WebSocket, and POST /v1/ws/ticket accepts only account credentials \u2014 a game frame holds a game-scoped token and cannot mint one \u2014 so it has to be relayed by the page embedding this game. Run inside a parent that advertises `dedicated`, or pass your own `signaling` implementation. See docs/dedicated-server-flow.md \xA72."))},close(){}}}var _e=1,Qe=2,xn=4,In=8,Ts=2,Es=1e3,Tn="heartbeat timeout",Et="gem-uu",Ct=2,Kr="gem_transports";function Ze(n){if(!Array.isArray(n))return null;let e=[];for(let t of n){if(typeof t!="string")continue;let r=t.toLowerCase();!Cs(r)||e.includes(r)||e.push(r);}return e}function Cs(n){return n==="webtransport"||n==="webrtc"||n==="websocket"}function Mt(n,e){if(typeof n!="object"||n===null)return null;let t=n[Kr];if(typeof t!="object"||t===null)return null;let r=t;return typeof r.v!="number"||r.v<1||typeof r.host!="string"||typeof r.port!="number"||e!==void 0&&r.match_id!==e?null:r}function Ot(n){return n instanceof Error?n.message===""?n.name:`${n.name}: ${n.message}`:String(n)}var Jr=new TextEncoder,zr=new TextDecoder;function En(n,e){let t=new Uint8Array(e.byteLength+1);return t[0]=n,t.set(e,1),t}function Cn(n){let e=0;return n?.reliable===false&&(e|=_e),n?.ordered===false&&(e|=Qe),e}function Mn(n){return {reliable:(n&_e)===0,ordered:(n&Qe)===0}}var On={reliable:true,ordered:true};function Xr(n){return JSON.stringify({t:"join",credential:n,wire:Ct,heartbeat:wn})}function Yr(n){let e;try{e=JSON.parse(n);}catch{throw new u("join_refused","the server answered the join with something unreadable")}if(e.t==="welcome"){let t=kn(e.heartbeat_interval);return {...e.player_id===void 0?{}:{playerId:e.player_id},...e.match_id===void 0?{}:{matchId:e.match_id},...t===void 0?{}:{heartbeatIntervalMs:t}}}throw e.t==="rejected"?Sn(e.reason):new u("join_refused","the server answered the join out of protocol")}function Gn(){return typeof globalThis.WebTransport=="function"&&Ln===null}function Gt(){return Ln}var Ln=null;var Vr="webtransport has no datagram lane; refusing the rung \u2014 server-to-client unreliable traffic would be silently lost";function Ms(n){let e=pn(n);if(e.byteLength!==32)throw new u("misconfigured",`the advertised certificate hash decodes to ${e.byteLength} bytes; a SHA-256 is 32`);return e}var An=class{buffered=new Uint8Array(0);push(e){let t=new Uint8Array(this.buffered.byteLength+e.byteLength);t.set(this.buffered,0),t.set(e,this.buffered.byteLength),this.buffered=t;let r=[];for(;!(this.buffered.byteLength<4);){let o=new DataView(this.buffered.buffer,this.buffered.byteOffset,4).getUint32(0,true);if(o===0||o>1<<20)throw new u("connection_failed",`a control-stream record claims ${o} bytes`);if(this.buffered.byteLength<4+o)break;let a=this.buffered.slice(4,4+o);this.buffered=this.buffered.slice(4+o),r.push({flags:a[0]??0,payload:a.slice(1)});}return r}};function Pn(n,e){let t=new Uint8Array(5+e.byteLength);return new DataView(t.buffer).setUint32(0,e.byteLength+1,true),t[4]=n,t.set(e,5),t}async function et(n){let e=Math.min(n.timeoutMs??1e4,Math.max(n.credentialExpiresAt-n.clock.now(),1e3)),t=n.webTransport;if(t===void 0){let s=globalThis.WebTransport;if(s===void 0)throw new u("connection_failed","this environment has no WebTransport");t=n.certHashBase64===void 0?new s(n.url):new s(n.url,{serverCertificateHashes:[{algorithm:"sha-256",value:Ms(n.certHashBase64)}]});}let r=t,i=s=>{try{r.close();}catch{}throw s},o=r.datagrams;(o?.readable===void 0||o.writable===void 0)&&(Ln=Vr,i(new u("connection_failed",`${Vr}; the rung is skipped for the rest of this page (a reload probes again)`)));let a=new Promise((s,l)=>{setTimeout(()=>l(new u("timeout","webtransport did not come up in time")),e);});try{await Promise.race([r.ready,a]);let s=r.incomingBidirectionalStreams.getReader(),l=await Promise.race([s.read(),a]);if(s.releaseLock(),l.done||l.value===void 0)return i(new u("connection_failed","the session ended before the control stream arrived"));let d=l.value,m=d.writable.getWriter();await m.write(Pn(xn,Jr.encode(Xr(n.credential))));let c=new An,p=d.readable.getReader(),y;for(;y===void 0;){let b=await Promise.race([p.read(),a]);if(b.done||b.value===void 0)return i(new u("connection_failed","the control stream closed before the join was answered"));for(let v of c.push(b.value)){y=Yr(zr.decode(v.payload));break}}return {link:Os(r,m,p,c,n.log??(b=>console.warn(b)),Zr(y,n.clock)),welcome:y}}catch(s){return i(s)}}function Os(n,e,t,r,i,o){let a,s,l=false,d=b=>{if(!l){l=true,m.stop();try{n.close();}catch{}s?.(b);}},m=qn(o,()=>d(Tn)),c=(b,v)=>{m.heard(),(b&In)===0&&a!==void 0&&((b&xn)!==0?a(zr.decode(v),On):a(v,Mn(b)));};(async()=>{try{for(;;){let b=await t.read();if(b.done||b.value===void 0)break;for(let v of r.push(b.value))c(v.flags,v.payload);}}catch{}d("stream closed");})();let p,y;try{let b=n.datagrams;if(b?.readable!==void 0&&b.writable!==void 0){let v=b.readable.getReader();y=b.writable.getWriter(),p=b,(async()=>{try{for(;;){let A=await v.read();if(A.done||A.value===void 0)break;let M=A.value;M.byteLength!==0&&c((M[0]??0)|_e|Qe,M.slice(1));}}catch{}})();}else i("[gem] webtransport link has no datagram lane; unreliable sends ride the stream");}catch(b){p=void 0,y=void 0,i(`[gem] webtransport datagram lane unusable (${Ot(b)}); unreliable sends ride the stream`);}return n.closed.then(b=>d(Qr(b?.reason)??"session closed"),()=>d("session failed")),{transport:"webtransport",send(b,v){if(l)return;if(typeof b=="string"){e.write(Pn(xn,Jr.encode(b))).catch(()=>d("write failed"));return}let A=Cn(v);if((A&_e)!==0&&y!==void 0){let M=En(A,b),E=p?.maxDatagramSize;if(E===void 0||M.byteLength<=E){y.write(M).catch(()=>{});return}}e.write(Pn(A,b)).catch(()=>d("write failed"));},onMessage(b){a=b;},onClose(b){s=b;},close(){d("closed");}}}function Dn(n){let e=Math.min(n.timeoutMs??1e4,Math.max(n.credentialExpiresAt-n.clock.now(),1e3)),t=n.webSocket;if(t===void 0){let i=globalThis.WebSocket;if(i===void 0)return Promise.reject(new u("connection_failed","this environment has no WebSocket"));t=new i(n.url);}let r=t;return r.binaryType="arraybuffer",new Promise((i,o)=>{let a=false,s=setTimeout(()=>{a||(a=true,r.close(),o(new u("timeout","websocket did not come up in time")));},e),l=y=>{a||(a=true,clearTimeout(s),r.close(),o(y));},d=()=>{r.send(Xr(n.credential));},m=y=>{if(a)return;if(typeof y.data!="string"){l(new u("join_refused","the server sent binary before answering the join"));return}let b;try{b=Yr(y.data);}catch(v){l(v);return}a=true,clearTimeout(s),r.removeEventListener("open",d),r.removeEventListener("message",m),r.removeEventListener("close",c),r.removeEventListener("error",p),i({link:Gs(r,Zr(b,n.clock)),welcome:b});},c=()=>{l(new u("connection_failed","the websocket closed before the join was answered"));},p=()=>{l(new u("connection_failed","the websocket failed before the join was answered"));};r.addEventListener("open",d),r.addEventListener("message",m),r.addEventListener("close",c),r.addEventListener("error",p),r.readyState===1&&d();})}function Qr(n){return typeof n=="string"&&n.length>0?n:void 0}function Zr(n,e){return n.heartbeatIntervalMs===void 0?void 0:{clock:e,intervalMs:n.heartbeatIntervalMs}}function qn(n,e){if(n===void 0)return {heard:()=>{},stop:()=>{}};let{clock:t}=n,r=Math.max(n.intervalMs,Es)*Ts,i=t.now(),o=null,a=()=>{o=null;let s=t.now()-i;if(s>=r){e();return}o=t.setTimeout(a,r-s);};return o=t.setTimeout(a,r),{heard:()=>{i=t.now();},stop:()=>{o!==null&&(t.clearTimeout(o),o=null);}}}function Gs(n,e){let t,r,i=false,o=s=>{if(!i){i=true,a.stop();try{n.close();}catch{}r?.(s);}},a=qn(e,()=>o(Tn));return n.addEventListener("message",(s=>{if(a.heard(),typeof s.data=="string"){t?.(s.data,On);return}let l=new Uint8Array(s.data);if(l.byteLength===0)return;let d=l[0]??0;(d&In)===0&&t?.(l.slice(1),Mn(d));})),n.addEventListener("close",(s=>o(Qr(s?.reason)??"socket closed"))),{transport:"websocket",send(s,l){if(!i){if(typeof s=="string"){n.send(s);return}n.send(En(Cn(l),s));}},onMessage(s){t=s;},onClose(s){r=s;},close(){o("closed");}}}function $n(n,e,t,r){let i,o,a=false,s=t,l=c=>{if(!a){a=true,d.stop();try{n.close();}catch{}try{e.close();}catch{}o?.(c);}},d=qn(r,()=>l(Tn)),m=(c,p)=>{if(d.heard(),typeof c=="string"){i?.(c,On);return}let y=new Uint8Array(c);if(y.byteLength===0)return;let b=y[0]??0;(b&In)===0&&i?.(y.slice(1),Mn(b|p));};if(n.addEventListener("message",(c=>m(c.data,0))),n.addEventListener("close",(()=>l("channel closed"))),e.addEventListener("connectionstatechange",(()=>{let c=e.connectionState;(c==="failed"||c==="closed")&&l(`connection ${c}`);})),s!==void 0){let c=s;c.addEventListener("message",(p=>m(p.data,_e|Qe))),c.addEventListener("close",(()=>{s=void 0;}));}return {transport:"webrtc",send(c,p){if(a)return;if(typeof c=="string"){n.send(c);return}let y=Cn(p),b=En(y,c);if((y&_e)!==0&&s!==void 0&&s.readyState==="open"){s.send(b);return}n.send(b);},onMessage(c){i=c;},onClose(c){o=c;},close(){l("closed");}}}var ei=10240,ti=2048,Ls=new TextEncoder;function ni(n){return Ls.encode(n).length}function se(n,e){let t=i=>{throw new u("invalid_sdp",`the ${e} ${i}`)},r=ni(n);r>=ei&&t(`is ${r} bytes, over the ${ei} limit`+(e==="offer"?", which for an offer means an unusually wide candidate list":"")),n.startsWith("v=")||t("does not start with 'v=', so it is not SDP"),n.includes("\0")&&t("contains a null byte"),/\r(?!\n)/.test(n)&&t("contains a bare carriage return");for(let i of n.split(/\r\n|\n/)){let o=ni(i);o>ti&&t(`has a ${o}-byte line, over the ${ti} limit`);}}function Lt(n){for(let e of n.split(/\r\n|\n/)){if(!e.startsWith("m="))continue;let[t,r="0"]=e.slice(2).trim().split(/\s+/);if(!(t!=="audio"&&t!=="video")&&r.split("/")[0]!=="0")return true}return false}var Ds=3e3,qs=1e4,$s=15e3;async function tt(n){let e=n.peerConnection;try{ii(n,"before the handshake started"),Fn(n);let t=e.createDataChannel(n.channelLabel??"gem",{ordered:!0});for(let a of n.extraChannels??[]){let s=e.createDataChannel(a.label,{...a.ordered===void 0?{}:{ordered:a.ordered},...a.maxRetransmits===void 0?{}:{maxRetransmits:a.maxRetransmits}});n.onExtraChannel?.(a.label,s);}let r=await e.createOffer();await e.setLocalDescription(r),await js(e,n),Fn(n);let i=e.localDescription?.sdp??r.sdp;if(!i)throw new u("invalid_sdp","the offer carried no SDP");se(i,"offer"),ri(i,"offer");let o=await Ns(n,i);if(Fn(n),typeof o?.sdp!="string")throw new u("invalid_sdp","the signaling channel answered without an SDP");return se(o.sdp,"answer"),ri(o.sdp,"answer"),await e.setRemoteDescription({type:"answer",sdp:o.sdp}),await Us(t,n),ii(n,"before the channel opened"),t}catch(t){throw e.close(),Fs(t)}}function Fs(n){return n instanceof u?n:ai(n)?Re():new u("connection_failed",li(n),{cause:n})}function ri(n,e){if(Lt(n))throw new u("media_refused",`the ${e} carries an active audio or video section. This path carries a data channel only; voice belongs to the platform voice service.`)}async function Ns(n,e){let t=n.exchangeTimeoutMs??qs,r=si(n,t),i=n.signaling.exchange({matchId:n.matchId,sdp:e,intent:n.intent??"initial"},n.signal).catch(o=>{throw o instanceof u?o:ai(o)?Re():new u("signaling_failed",li(o),{cause:o})});return Ws(i,n,r,()=>r.exhausted?Nn("while the offer was in flight"):new u("timeout",`no answer arrived within ${t}ms`))}function js(n,e){if(n.iceGatheringState==="complete"||e.signal?.aborted)return Promise.resolve();let{clock:t}=e;return new Promise(r=>{let i=()=>{t.clearTimeout(a),n.removeEventListener("icegatheringstatechange",o),e.signal?.removeEventListener("abort",i),r();},o=()=>{n.iceGatheringState==="complete"&&i();},a=t.setTimeout(i,e.gatherTimeoutMs??Ds);n.addEventListener("icegatheringstatechange",o),e.signal?.addEventListener("abort",i);})}function Us(n,e){if(e.signal?.aborted)return Promise.reject(Re());if(n.readyState==="open")return Promise.resolve();if(n.readyState!=="connecting")return Promise.reject(new u("connection_failed",`the data channel was already '${n.readyState}' when the answer was applied`));let{clock:t}=e,r=e.openTimeoutMs??$s,i=si(e,r);return new Promise((o,a)=>{let s=()=>{t.clearTimeout(p),n.removeEventListener("open",l),n.removeEventListener("close",d),n.removeEventListener("error",d),e.signal?.removeEventListener("abort",m);},l=()=>{s(),o();},d=y=>{s(),a(new u("connection_failed","the data channel failed before it opened",oi(y)===void 0?void 0:{cause:oi(y)}));},m=()=>{s(),a(Re());},c=()=>{s(),a(i.exhausted?Nn("while the channel was still opening"):new u("timeout",`the data channel did not open within ${r}ms`));},p=t.setTimeout(c,i.ms);n.addEventListener("open",l),n.addEventListener("close",d),n.addEventListener("error",d),e.signal?.addEventListener("abort",m);})}function si(n,e){if(n.credentialExpiresAt===void 0)return {ms:e,exhausted:false};let t=n.credentialExpiresAt-n.clock.now();return t<=e?{ms:Math.max(0,t),exhausted:true}:{ms:e,exhausted:false}}function Ws(n,e,t,r){let{clock:i,signal:o}=e;return o?.aborted?Promise.reject(Re()):new Promise((a,s)=>{let l=c=>{i.clearTimeout(m),o?.removeEventListener("abort",d),c();},d=()=>l(()=>s(Re())),m=i.setTimeout(()=>l(()=>s(r())),t.ms);o?.addEventListener("abort",d),n.then(c=>l(()=>a(c)),c=>l(()=>s(c)));})}function ii(n,e){if(n.credentialExpiresAt!==void 0&&!(n.clock.now()<n.credentialExpiresAt))throw Nn(`already ${e}`)}function Nn(n){return new u("credential_expired",`the join credential ran out ${n} \u2014 poll match status again for a fresh one`)}function Fn(n){if(n.signal?.aborted)throw Re()}function Re(){return new u("aborted","the caller aborted the connection attempt")}function ai(n){return n?.name==="AbortError"}function oi(n){return n?.error}function li(n){return n instanceof Error?n.message:String(n)}var nt="0.1.0-beta",di="0-beta",Me="web",Hs=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?$/;function Bs(n){let e=Hs.exec(n),t=e?.[1],r=e?.[4];return t===void 0?null:r===void 0?t:`${t}-${r}`}function ci(n,e){let t=Vs(n.platform,e),r=n.engineVersion;if(r===void 0)return {...n,platform:t,engineVersion:nt};let i=Bs(r);if(i===null)throw new u("misconfigured",`${e} refused: engineVersion '${r}' is not semver, so no engine line can be derived from it and gem-api answers 400. Omit it \u2014 a browser game has no engine version of its own, and the SDK reports '${nt}' for it.`);if(i!==di)throw new u("misconfigured",`${e} refused: engineVersion '${r}' derives to engine line '${i}', and '${di}' is the only line a web dedicated-server fleet runs. gem-api uses the line \u2014 the semver major plus any prerelease tag \u2014 verbatim as the fleet's label selector, so a match on '${i}' would match nothing and be reported as a capacity shortfall rather than as this. Omit engineVersion: a browser game has no engine version of its own, and the SDK reports '${nt}' for it.`);return {...n,platform:t,engineVersion:r}}function Vs(n,e){if(n===void 0||n===Me)return Me;if(!gn.includes(n))throw new u("misconfigured",`${e} refused: platform '${String(n)}' is not a client platform gem-api recognises \u2014 it knows ${gn.map(t=>`'${t}'`).join(", ")}. Remove the field: the SDK reports '${Me}', which is the only one true of a browser build.`);return console.warn(`[gem] ${e}: clientVersion.platform '${n}' was replaced with '${Me}' and is deprecated \u2014 remove the field. A browser game publishes its bundle in the web package dimension, so a client reporting anything else is version-checked against a dimension it has nothing in, and every blacklist, release and expiry check passes without looking at this build.`),Me}function Ks(n){let{client:e,clock:t,rooms:r}=n,i=n.signaling??Tt,o=n.log??(f=>console.warn(f)),a=new ve({client:e,gameId:()=>r.gameId,transport:"dedicated"}),s=new Map,l=(f,_)=>{let k=s.get(f)??new Set;k.add(_),s.set(f,k);},d=(f,_)=>{let k=s.get(f);k!==void 0&&(k.delete(_),k.size===0&&s.delete(f));},m=async f=>{if(r.isHost&&!na(r)&&r.currentRoom?.matchId===void 0){let _=H(f.clusterTargetId);await r.startMatch({..._===void 0?{}:{clusterTargetId:_},...f.retry===void 0?{}:{retry:f.retry},...f.signal===void 0?{}:{signal:f.signal}});}return fi(r.matchConnectionState==="suspended"?await r.reconnectToMatch(jn(f)):await r.awaitMatch(jn(f)))},c=async f=>fi(await r.pollMatchStatus(jn(f))),p=async(f,_)=>{let k=false;try{let R=await m(f),$=r.currentRoom?.transports;if($!==void 0&&!$.includes("webrtc"))throw new u("misconfigured",`the room template offers (${$.join(", ")||"no transports"}), not webrtc, which is all this path dials. Connect through gem.match.connect(), or pass \`transports: []\` here to negotiate across the template's list.`);let O=f.peerConnection??yi();k=!0;let F=await tt({peerConnection:O,signaling:_,clock:t,matchId:R.matchId,credentialExpiresAt:R.credentialExpiresAt,...Dt(f,"intent","channelLabel","gatherTimeoutMs","exchangeTimeoutMs","openTimeoutMs","signal")}),j=null;if(f.presentCredential!==!1)try{j=await Ye({channel:F,clock:t,credential:R.joinCredential,credentialExpiresAt:R.credentialExpiresAt,...Dt(f,"onMessage","signal"),...f.welcomeTimeoutMs===void 0?{}:{ceilingMs:f.welcomeTimeoutMs}});}catch(U){throw X(F),X(O),U}return ta(r,R,F,O,_,f.reportDisconnect!==!1,f.reportConnected!==!1,j,{remember:l,forget:d},f.onMatchEnded)}catch(R){throw k&&hi(a,R),X(_),R}},y=async(f,_,k,R,$,O)=>{if(f==="webtransport"){let G=R?.webtransport;if(G===void 0)throw new u("misconfigured","webtransport is not advertised");let Q={credential:k.joinCredential,credentialExpiresAt:k.credentialExpiresAt,clock:t};if(typeof G.named_url=="string"&&G.named_url!==""){let ae=Math.max(Math.floor(O/2),1e3);try{return await et({url:G.named_url,timeoutMs:ae,log:o,...Q})}catch(pe){if(Gt()!==null)throw pe;return o(`[gem] webtransport named dial failed (${Ot(pe)}); retrying the pinned dial`),et({url:G.url,certHashBase64:G.cert_sha256_b64,timeoutMs:ae,log:o,...Q})}}return et({url:G.url,certHashBase64:G.cert_sha256_b64,timeoutMs:O,...Q})}if(f==="websocket"){let G=R?.websocket;if(G===void 0)throw new u("misconfigured","websocket is not advertised");return Dn({url:G.url,credential:k.joinCredential,credentialExpiresAt:k.credentialExpiresAt,clock:t,timeoutMs:O})}let F=_.peerConnection??yi(),j,U=await tt({peerConnection:F,signaling:$,clock:t,matchId:k.matchId,credentialExpiresAt:k.credentialExpiresAt,extraChannels:[{label:Et,ordered:false,maxRetransmits:0}],onExtraChannel:(G,Q)=>{G===Et&&(j=Q);},...Dt(_,"intent","channelLabel","gatherTimeoutMs","exchangeTimeoutMs","openTimeoutMs","signal"),..._.exchangeTimeoutMs===void 0&&_.redial===true?{exchangeTimeoutMs:zs}:{}}),z;try{z=await Ye({channel:U,clock:t,credential:k.joinCredential,credentialExpiresAt:k.credentialExpiresAt,wire:Ct,heartbeat:!0,...Dt(_,"signal"),..._.welcomeTimeoutMs===void 0?{}:{ceilingMs:_.welcomeTimeoutMs}});}catch(G){throw X(U),X(F),G}let T=z.welcome.heartbeatIntervalMs;return {link:$n(U,F,j,T===void 0?void 0:{clock:t,intervalMs:T}),welcome:z.welcome,channel:U,peerConnection:F}},b=(f,_,k,R,$)=>{let O=false,F;R.link.onClose(T=>{!O&&f.reportDisconnect!==false&&_n(r,_.matchId,T),F?.(T);}),f.reportDisconnect!==false&&f.reportConnected!==false&&r.notifyMatchConnected(_.matchId);let j={transport:R.link.transport,send:(T,G)=>R.link.send(T,G),onMessage:T=>R.link.onMessage(T),onClose:T=>{F=T;},close:()=>R.link.close()},U=()=>{O||(O=true,X(R.link),X($));},z=T=>{if(!O){try{f.onMatchEnded?.(T);}catch{}U();}};return l(_.matchId,z),{matchId:_.matchId,transport:k,link:j,joinCredential:_.joinCredential,credentialExpiresAt:_.credentialExpiresAt,welcome:R.welcome,...R.channel===void 0?{}:{channel:R.channel},...R.peerConnection===void 0?{}:{peerConnection:R.peerConnection},close(){d(_.matchId,z),U();}}},v=async(f,_)=>{let k=r.currentRoom?.id,R=r.gameId,$=()=>({advert:Mt(r.currentRoom?.settings,f),template:r.currentRoom?.transports??null});if(k===void 0||R===null)return $();try{let O=await e.get(`/v2/games/${encodeURIComponent(R)}/game-rooms/${encodeURIComponent(k)}`,_);return {advert:Mt(O.settings,f),template:Ze(O.transports)??r.currentRoom?.transports??null}}catch{return $()}},A=(f,_)=>{let k=f??ui;return _.length===0?k:k.filter(R=>_.includes(R))},M=async(f,_,k)=>{try{let R=await m(f),{advert:$,template:O}=await v(R.matchId,f.signal),F=A(O,_),j=F.filter(N=>Qs(N,$)),U=Gt();if(U!==null&&F.includes("webtransport")&&$?.webtransport!==void 0&&o(`[gem] webtransport skipped: ${U} (learned on an earlier dial in this page)`),j.length===0){let N=O===null?`the default order (${ui.join(", ")})`:`the room template's transports (${O.join(", ")||"none"})`;throw new u("misconfigured",`none of ${N}`+(_.length>0?`, narrowed to (${_.join(", ")}),`:"")+" is available here: the server advertises none of them, and only webrtc qualifies without an advertisement.")}let z=f.transportAttemptTimeoutMs??Js,T=()=>f.deadlineAt===void 0?1/0:f.deadlineAt-t.now(),G=N=>n.signalingReady===void 0||N<=0?Promise.resolve(!0):n.signalingReady(N),Q=[],ae=[];for(let N of j){if(T()<mi){o(`[gem] ${N} rung not tried: the reconnect budget is spent`);break}ae.length>0&&R.credentialExpiresAt-t.now()<gi&&(R=await c(f)),ae.push(N);let nr=!1,rr=!1,mt={attempts:0,waitedMs:0};for(;;){if(N==="webrtc"&&f.redial===!0){let te=Math.min(pi,T());await G(te)||o(`[gem] webrtc rung: the page has not reported its signaling socket up after ${Math.round(te)}ms; offering anyway`);}let Qi=Math.max(Math.min(z,T()),mi);try{return b(f,R,N,await y(N,f,R,$,k,Qi),k)}catch(te){let W=te instanceof u?te:new u("connection_failed",te instanceof Error?te.message:String(te),{cause:te});if(N==="webrtc"&&hi(a,W),(W.reason==="credential_expired"||W.reason==="credential_reused")&&!nr){nr=!0,R=await c(f);continue}if(N==="webrtc"&&W.reason==="signaling_failed"&&W.platformCode===bn&&!rr){rr=!0;let me=Math.min(pi,T());if(o(`[gem] webrtc rung: the page could not put the offer on its socket; waiting up to ${Math.round(me)}ms for the socket before offering again`),await G(me))continue}if(W.reason==="join_rate_limited"){let me=Ys(mt,Math.random,T());if(me!==null){if(mt.attempts+=1,mt.waitedMs+=me,o(`[gem] ${N} rung: the server is rate-limiting this player's joins; waiting ${Math.round(me)}ms before presenting again (retry ${mt.attempts} of ${vi.maxAttempts})`),await Pt(t,me,f.signal),f.signal?.aborted)throw new u("aborted","the caller aborted while waiting out a join rate limit");R.credentialExpiresAt-t.now()<gi&&(R=await c(f));continue}}if(Xs.has(W.reason)||W.reason==="credential_expired"||N==="webrtc"&&bi(W)){o(`[gem] ${N} rung failed (${W.reason}): ${Ot(W.cause??W)} \u2014 `+(ae.length<j.length?`falling through to ${j[ae.length]}`:"no rungs left")),Q.push(W);break}throw o(`[gem] ${N} rung refused the session (${W.reason}): ${W.message}`),W}}}let pe=Q[Q.length-1];throw pe===void 0?new u("timeout",`the reconnect budget ran out before any transport (${j.join(", ")}) could be tried`):new u(pe.reason,`every transport tried (${ae.join(", ")}) failed; the last said: ${pe.message}`,{cause:Q,platformCode:pe.platformCode})}catch(R){throw X(k),R}};return {listClusterTargets:async(f={})=>At({client:e,...f}),handleMatchEnded(f,_){let k=s.get(f);if(k!==void 0){s.delete(f);for(let R of k)R(_);}},connect:(async(f={})=>{let _=f.signaling??i();return f.transports===void 0?p(f,_):M(f,f.transports,_)})}}var ui=["webtransport","webrtc","websocket"],Js=1e4,zs=5e3,pi=3e3,mi=1e3,gi=1e4,Xs=new Set(["timeout","connection_failed","signaling_unavailable","signaling_failed"]);function bi(n){return n.platformCode!==void 0&&vn.has(n.platformCode)}var vi={initialDelayMs:2e3,factor:2,jitterMs:1e3,maxAttempts:3,budgetMs:15e3};function Ys(n,e=Math.random,t=1/0){let r=vi;if(n.attempts>=r.maxAttempts)return null;let i=Math.min(r.budgetMs-n.waitedMs,t);if(i<r.initialDelayMs)return null;let o=r.initialDelayMs*r.factor**n.attempts;return Math.min(o+e()*r.jitterMs,i)}function Qs(n,e){return n==="webrtc"?e===null||e.webrtc!==false:e===null?false:n==="webtransport"?e.webtransport!==void 0&&Gn():e.websocket===void 0?false:e.websocket.tls===true||globalThis.isSecureContext!==true||Zs(e.websocket.url)}function Zs(n){if(typeof n!="string")return false;let e;try{e=new URL(n).hostname.toLowerCase();}catch{return false}return Te(e)}function jn(n){return {...n.timeoutMs===void 0?{}:{timeoutMs:n.timeoutMs},...n.pollIntervalMs===void 0?{}:{pollIntervalMs:n.pollIntervalMs},...n.signal===void 0?{}:{signal:n.signal}}}var ea=new Set(["timeout","connection_failed","credential_expired","signaling_unavailable","signaling_failed","invalid_sdp","join_refused"]);function hi(n,e){e instanceof u&&(!ea.has(e.reason)&&!bi(e)||n.report([{type:"connection_failures_total",value:1}]));}function ta(n,e,t,r,i,o,a,s,l,d){let m=false,c=()=>{m||_n(n,e.matchId,void 0);};o&&(t.addEventListener("close",c),a&&n.notifyMatchConnected(e.matchId));let p=()=>{m||(m=true,t.removeEventListener("close",c),s?.detach(),X(t),X(r),X(i));},y=b=>{if(!m){try{d?.(b);}catch{}p();}};return l.remember(e.matchId,y),{matchId:e.matchId,channel:t,peerConnection:r,joinCredential:e.joinCredential,credentialExpiresAt:e.credentialExpiresAt,welcome:s?.welcome??null,close(){l.forget(e.matchId,y),p();}}}function na(n){let e=n.matchSession.phase;return e!=="none"&&e!=="ended"}function fi(n){if(n.status==="failed")throw new u("allocation_failed",`match ${n.matchId} did not come up: ${n.reason}`);if(n.status==="ended")throw new u("match_ended",`match ${n.matchId} ended (${n.reason}) before this side connected.`);if(n.protocol!==Xe)throw new u("unsupported_protocol",`match ${n.matchId} speaks '${n.protocol}', which a browser cannot connect. Only '${Xe}' is reachable from a frame; host and port on this response are advisory and must not be dialled.`);let{joinCredential:e,credentialExpiresAt:t}=n;if(e===void 0||t===void 0)throw new u("connection_failed",`match ${n.matchId} is ready but issued no join credential, so the server would refuse the channel once it opened \u2014 poll match status again.`);return {...n,joinCredential:e,credentialExpiresAt:t}}function yi(){let n=globalThis.RTCPeerConnection;if(!n)throw new u("misconfigured","this environment has no RTCPeerConnection. Pass `peerConnection` to connect against a stand-in, which is how the flow is tested outside a browser.");return new n({iceServers:[]})}function X(n){try{n.close();}catch{}}function Dt(n,...e){let t={};for(let r of e)n[r]!==void 0&&(t[r]=n[r]);return t}var Oe=class{constructor(e){this.options=e;this.reporter=new ve({client:e.client,gameId:e.gameId,transport:"p2p"});}options;reporter;async joinCredential(e,t,r){let i=await this.options.client.post(`${this.match(e)}/join-credential`,{fingerprint:t},r);return {credential:qt(i.credential)??"",hostFingerprint:qt(i.host_fingerprint)??"",hostEpoch:$t(i.host_epoch)??0,expiresAt:($t(i.expires_at)??0)*1e3}}async turnLease(e,t,r){let i=await this.options.client.post(`${this.match(e)}/turn-lease`,{cluster_target_id:t},r),o=[];for(let l of Array.isArray(i.ice_servers)?i.ice_servers:[]){let d=l,m=(Array.isArray(d.urls)?d.urls:[]).filter(c=>typeof c=="string"&&c.length>0);m.length!==0&&o.push({urls:m,username:qt(d.username)??"",credential:qt(d.credential)??""});}let a=$t(i.expires_at),s=$t(i.ttl_seconds);return {iceServers:o,expiresAt:a!==void 0?a*1e3:Date.now()+(s??0)*1e3}}async kick(e,t,r){await this.options.client.post(`${this.match(e)}/kick`,{player_id:t},r);}async leave(e,t=false){await this.options.client.post(`${this.match(e)}/leave`,{},void 0,{keepalive:t});}async telemetry(e,t){await this.reporter.post(e,t);}match(e){return `/v1/games/${encodeURIComponent(this.options.gameId())}/matches/${encodeURIComponent(e)}`}},ra=new Set(["cluster_hidden","invalid_cluster_target"]);async function _i(n,e,t,r){let i=V(t);if(i.length===0)throw new Error("no cluster to lease TURN from");let o;for(let a of i)try{return {...await n.turnLease(e,a,r),clusterTargetId:a}}catch(s){if(!ia(s))throw s;o=s;}throw o}function ia(n){return n instanceof q&&ra.has(n.code??"")}function qt(n){return typeof n=="string"&&n.length>0?n:void 0}function $t(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}var oa=new Set(["timeout","signaling_failed","connection_failed","keys_unavailable"]),w=class extends Error{constructor(t,r,i){super(`${t}: ${r}`,i);this.reason=t;this.name="P2pError",this.retryable=oa.has(t);}reason;retryable};var Ft=class{listeners=new Map;on(e,t){let r=this.listeners.get(e);return r||(r=new Set,this.listeners.set(e,r)),r.add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t);}once(e,t){let r=this.on(e,((...i)=>{r(),t(...i);}));return r}emit(e,...t){let r=this.listeners.get(e);if(r)for(let i of [...r])i(...t);}clear(){this.listeners.clear();}};var sa=/^sha-256 ([0-9A-F]{2}:){31}[0-9A-F]{2}$/;function ke(n){let e=null;for(let t of n.split(/\r\n|\n/)){if(!t.startsWith("a=fingerprint:"))continue;let r=rt(t.slice(14).trim());if(r===null||e!==null&&e!==r)return null;e=r;}return e}function rt(n){let[e,...t]=n.trim().split(/\s+/);if(e===void 0||t.length===0)return null;let r=`${e.toLowerCase()} ${t.join("").toUpperCase()}`;return sa.test(r)?r:null}function we(n,e){if(n.length!==e.length)return false;let t=0;for(let r=0;r<n.length;r++)t|=n.charCodeAt(r)^e.charCodeAt(r);return t===0}var Un=5e3,aa=12e4+Un,la=1024,da=6e4,ca=1e4,Ri=64*1024;function Nt(n){let e=new Map,t=-1/0,r=null,i=(()=>{let s;try{s=new URL(`${n.baseUrl.replace(/\/+$/,"")}/oauth2/jwks`);}catch{return {ok:false,reason:"the configured API base is not a valid URL, so the JWKS endpoint cannot be reached. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure."}}return s.username!==""||s.password!==""?{ok:false,reason:"the configured API base carries credentials in the URL, which `fetch` will not send. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure."}:{ok:true,url:s.href,shown:`${s.protocol}//${s.host}`}})(),o=()=>{let s=n.subtle??globalThis.crypto?.subtle;if(!s)throw new w("keys_unavailable","this environment has no WebCrypto, so a join credential cannot be verified. A page served over plain http on a non-loopback origin is the usual cause \u2014 crypto.subtle is only exposed in a secure context.");return s},a=async()=>{if(!i.ok)throw new w("keys_unavailable",i.reason);let s=n.fetch??((p,y)=>globalThis.fetch(p,y)),l;try{l=await s(i.url,{redirect:"error",credentials:"omit",cache:"no-store",referrerPolicy:"no-referrer",signal:AbortSignal.timeout(ca)});}catch{throw new w("keys_unavailable",`the JWKS endpoint at ${i.shown} could not be reached \u2014 it timed out, refused a redirect, or the network failed.`)}if(!l.ok)throw new w("keys_unavailable",`the JWKS endpoint answered ${i.shown} with an error`);let d;try{let p=await l.text();if(p.length>Ri)throw new w("keys_unavailable",`the JWKS endpoint at ${i.shown} answered with more than ${Ri} characters, which is not a key set this platform publishes.`);d=JSON.parse(p);}catch(p){throw p instanceof w?p:new w("keys_unavailable",`the JWKS endpoint at ${i.shown} did not answer with a readable key set.`)}let m=Array.isArray(d.keys)?d.keys:[],c=o();for(let p of m){let y=p;if(y.kty==="RSA"&&!(y.alg!==void 0&&y.alg!=="RS256")&&!(y.use!==void 0&&y.use!=="sig")&&!(typeof y.kid!="string"||y.kid.length===0))try{let b=await c.importKey("jwk",{...p,alg:"RS256",ext:!0},{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},!1,["verify"]);e.set(y.kid,b);}catch{}}};return {async key(s){let l=e.get(s);if(l)return l;if(!i.ok)throw new w("keys_unavailable",i.reason);if(r)return await r,e.get(s)??null;let d=n.now();if(!Number.isFinite(d))throw new w("keys_unavailable","the clock supplied to the SDK returned a non-finite time, so the JWKS throttle cannot be evaluated. This is a configuration fault, not a server failure.");return d<t&&(t=-1/0),d-t<da?null:(t=d,r??=a().finally(()=>{r=null;}),await r,e.get(s)??null)}}}var it=class{seen=new Map;admit(e,t){for(let[r,i]of this.seen)i<=t&&this.seen.delete(r);if(this.seen.has(e))return false;if(this.seen.size>=la){let r=this.seen.keys().next().value;r!==void 0&&this.seen.delete(r);}return this.seen.set(e,t+aa),true}};function ki(n){let e=n.replace(/-/g,"+").replace(/_/g,"/"),t=atob(e+"=".repeat((4-e.length%4)%4)),r=new Uint8Array(t.length);for(let i=0;i<t.length;i++)r[i]=t.charCodeAt(i);return r}function wi(n){try{let e=new TextDecoder().decode(ki(n)),t=JSON.parse(e);return typeof t!="object"||t===null||Array.isArray(t)?null:t}catch{return null}}async function jt(n,e,t,r,i){let o=T=>{throw new w("peer_unauthenticated",`the peer's join credential is not usable: ${T}`)},a=n.split(".");a.length!==3&&o("it is not a three-part JWT");let[s,l,d]=a,m=wi(s);m===null&&o("its header does not decode"),m.alg!=="RS256"&&o(`its alg is ${JSON.stringify(m.alg)}, not RS256`),m.typ!=="p2p+jwt"&&o(`its typ is ${JSON.stringify(m.typ)}, not p2p+jwt`);let c=m.kid;(typeof c!="string"||c.length===0)&&o("it names no key");let p=await t.key(c);p===null&&o("it was signed by a key this build cannot resolve");let y=i.subtle??globalThis.crypto?.subtle;y||o("this environment has no WebCrypto");let b=new TextEncoder().encode(`${s}.${l}`),v;try{v=ki(d);}catch{o("its signature does not decode");}await y.verify({name:"RSASSA-PKCS1-v1_5"},p,v,b)||o("its signature does not verify");let M=wi(l);M===null&&o("its payload does not decode");let E=M,f=T=>typeof E[T]=="string"?E[T]:"",_=T=>typeof E[T]=="number"&&Number.isFinite(E[T])?E[T]:NaN;f("iss")!==i.issuer&&o("it was issued by someone else"),f("aud")!==`${i.issuer}/p2p-match`&&o("its audience is not this platform"),f("match_id")!==e.matchId&&o("it is for another match"),f("game_id")!==e.gameId&&o("it is for another game");let k=_("exp")*1e3,R=_("nbf")*1e3;(!Number.isFinite(k)||e.now>k+Un)&&o("it has expired"),Number.isFinite(R)&&e.now<R-Un&&o("it is not valid yet");let $=f("role");$!=="host"&&$!=="client"&&o("it names no usable role");let O=_("host_epoch");O!==e.hostEpoch&&o("it names a different host epoch");let F=rt(f("fingerprint"));F===null&&o("its fingerprint claim is malformed"),we(F,e.peerFingerprint)||o("it was issued for a different certificate than this connection used");let j=rt(f("host_fingerprint"));j===null&&o("its host fingerprint claim is malformed"),we(j,e.hostFingerprint)||o("it was issued against a different host certificate");let U=f("jti");U.length===0&&o("it carries no jti, so replay cannot be detected"),r.admit(U,e.now)||o("it has already been used");let z=f("sub");return z.length===0&&o("it names no player"),{playerId:z.toLowerCase(),matchId:e.matchId,gameId:e.gameId,hostEpoch:O,role:$,fingerprint:F,hostFingerprint:j,jti:U,expiresAt:k}}var Se=32769;function Wn(n){let e=new TextEncoder().encode(n.jwt);if(e.length>8192)throw new Error(`[gem] a join credential of ${e.length} bytes exceeds the wire format's cap`);let t=new ArrayBuffer(3+e.length+4+4),r=new DataView(t);return r.setUint8(0,1),r.setUint16(1,e.length,true),new Uint8Array(t,3,e.length).set(e),r.setUint32(3+e.length,n.protocolVersion,true),r.setUint32(7+e.length,n.assignedPeerId,true),t}function Hn(n){let e=new ArrayBuffer(9),t=new DataView(e);return t.setUint8(0,2),t.setUint32(1,n.protocolVersion,true),t.setUint32(5,n.assignedPeerId,true),e}function Bn(n){let e=new ArrayBuffer(2),t=new DataView(e);return t.setUint8(0,3),t.setUint8(1,n),e}function Vn(n){if(n.byteLength<1)return null;let e=new DataView(n);switch(e.getUint8(0)){case 1:{if(n.byteLength<3)return null;let t=e.getUint16(1,true);return t>8192||n.byteLength<3+t+8?null:{kind:"join",jwt:new TextDecoder().decode(new Uint8Array(n,3,t)),protocolVersion:e.getUint32(3+t,true),assignedPeerId:e.getUint32(7+t,true)}}case 2:return n.byteLength<9?null:{kind:"admit",protocolVersion:e.getUint32(1,true),assignedPeerId:e.getUint32(5,true)};case 3:return n.byteLength<2?null:{kind:"reject",reason:e.getUint8(1)};default:return null}}function Si(n){switch(n){case 1:return "the host did not accept this credential";case 2:return "the host speaks a different protocol version \u2014 an engine peer refuses a browser peer here, deliberately";case 3:return "the match is full";case 4:return "the host is rate-limiting joins";case 129:return "the host verified this credential but it names a different player than the one who signalled \u2014 the host has its own line saying which";case 130:return "the host has this player on the kicked list for the current host epoch";case 131:return "the host could not fetch the platform's signing keys, so it cannot verify anyone yet \u2014 the host's console says why";default:return `the host refused with an unrecognised code (${n})`}}function xi(n){switch(n){case 129:return "not_expected_player";case 130:return "kicked";case 131:return "keys_unavailable";default:return "peer_unauthenticated"}}var Ai=3e4,Ge=class{constructor(e,t,r){this.localPlayerId=e;this.hostPlayerId=t,this.epoch=r;}localPlayerId;state="idle";epoch;hostPlayerId;kicked=new Set;startedAt=0;get currentState(){return this.state}get currentEpoch(){return this.epoch}get currentHostPlayerId(){return this.hostPlayerId}get isLocalHost(){return this.hostPlayerId===this.localPlayerId}isKicked(e){return this.kicked.has(e)}stalled(e){return this.state!=="idle"&&e-this.startedAt>3e4}apply(e,t){return !Number.isInteger(e.hostEpoch)||e.hostEpoch<0?{kind:"refused",why:`host epoch ${String(e.hostEpoch)} is not a whole number`}:e.hostEpoch===this.epoch?e.migrationComplete===true&&this.state!=="idle"?(this.state="idle",{kind:"completed"}):{kind:"duplicate"}:e.hostEpoch<this.epoch?{kind:"refused",why:`host epoch ${e.hostEpoch} is behind the current ${this.epoch}`}:e.hostPlayerId.length===0?{kind:"refused",why:"the migration named no new host"}:(this.kicked=new Set(e.kickedPlayerIds??[]),this.epoch=e.hostEpoch,this.hostPlayerId=e.hostPlayerId,this.startedAt=t,this.state=this.isLocalHost?"preparing":"reconnecting",{kind:"accepted",state:this.state})}complete(){this.state="idle";}};var Ut={reliable:{negotiated:true,id:0,ordered:true},ordered:{negotiated:true,id:1,ordered:true,maxRetransmits:0},unreliable:{negotiated:true,id:2,ordered:false,maxRetransmits:0}},ot=["reliable","ordered","unreliable"];function Wt(n){let e=globalThis.RTCPeerConnection;if(!e)throw new Error("[gem] this environment has no RTCPeerConnection. Pass `peerConnectionFactory` to connect against a stand-in, which is how this flow is tested outside a browser.");return new e({iceServers:n.iceServers,...n.certificates===void 0?{}:{certificates:n.certificates}})}async function Ht(){let n=globalThis.RTCPeerConnection;return n?.generateCertificate?n.generateCertificate({name:"ECDSA",namedCurve:"P-256"}):null}var Ii=1,ha=3e4,Ti=2e3,fa=5e3,Ei=6e4,ya=8,ba=12e4,va=5e3,_a=8e3,Gi=18e5,Ra=Gi+6e4,Jn=class{constructor(e=ya){this.perMinute=e;}perMinute;sent=[];delayUntilAllowed(e){return this.prune(e),this.sent.length<this.perMinute?0:Math.max(0,(this.sent[0]??e)+6e4-e)}record(e){this.prune(e),this.sent.push(e);}prune(e){let t=0;for(let r of this.sent)r<=e&&e-r<6e4&&(this.sent[t++]=r);this.sent.length=t;}},wa=new Jn,ka=8,Sa=new Set(["timeout","connection_failed","invalid_sdp","peer_unauthenticated","not_expected_player","keys_unavailable"]),Le=class{constructor(e){this.options=e;this.keys=e.keys??Nt({baseUrl:e.baseUrl,now:()=>e.clock.now()});}options;emitter=new Ft;links=new Map;replays=new it;keys;localPlayerId="";migration=null;lease=null;clusterTargetIds=[];leaseRenewal=null;leaseAskedAt=null;leaseRenewalWarned=false;credential=null;nextPeerId=Ii+1;ended=null;closed=false;ready=null;helloSeen=false;reportedUnreachableEpoch=-1;reportedStallEpoch=-1;counted=false;pendingOffers=new Map;answeredSessions=new Map;credentialGeneration=0;credentialDeadline=0;drainTimers=new Set;get matchId(){return this.options.matchId}get playerId(){return this.localPlayerId}get isHost(){return this.migration?.isLocalHost??false}get hostPlayerId(){return this.migration?.currentHostPlayerId??""}get hostEpoch(){return this.migration?.currentEpoch??0}get peers(){let e=[];for(let t of this.links.values())t.admitted&&e.push(this.expose(t));return e}peer(e){let t=this.links.get(e);return t&&t.admitted?this.expose(t):null}on(e,t){return this.emitter.on(e,t)}off(e,t){this.emitter.off(e,t);}once(e,t){return this.emitter.once(e,t)}async start(e){let t=this.options.connectTimeoutMs??ha;this.credentialDeadline=this.options.clock.now()+t;let r=new Promise((a,s)=>{this.ready={resolve:a,reject:s};});if(this.clusterTargetIds=V([this.options.clusterTargetId,...this.options.clusterTargetIds??[]]),this.clusterTargetIds.length>0)try{await this.takeLease(e),this.mark("turn leased");}catch(a){this.mark("turn lease failed"),this.warn(`no TURN relay could be leased for this match, so peers behind a symmetric NAT will fail to connect: ${at(a)}`);}else this.mark("turn skipped"),this.warn("connecting without a TURN relay because no cluster target was resolved. Host candidates only reach peers on the same network, so peers behind a symmetric NAT will fail to connect. The Arcade page normally resolves one for this player; outside it, pass `clusterTargetId` to connect().");this.options.signaling.join(),this.mark("signaling joined");let i=this.options.clock.setTimeout(()=>{this.fail(new w("timeout",`no usable P2P connection after ${t}ms. The parent relays signaling, so a silent failure here usually means the page is not putting frames on the socket.`));},t),o=()=>this.fail(new w("aborted","the caller aborted while connecting"));e?.aborted?o():e?.addEventListener("abort",o);try{await r;}finally{this.options.clock.clearTimeout(i),e?.removeEventListener("abort",o),this.ready=null;}}async kick(e){if(!this.isHost)throw new w("misconfigured","only the current host of this match can kick a peer");await this.options.api.kick(this.options.matchId,e);}async leave(){try{await this.options.api.leave(this.options.matchId);}catch{}this.close();}close(){if(!this.closed){this.closed=true,this.pendingOffers.clear();for(let e of this.drainTimers)this.options.clock.clearTimeout(e);this.drainTimers.clear();for(let e of [...this.links.values()])this.dropLink(e,"closed",false);this.options.signaling.close(),this.emitter.clear();}}report(e){this.options.api.telemetry(e).catch(()=>{});}notifyCandidatesDropped(e){this.emitter.emit("candidatesDropped",e);}handleSignal(e,t){this.closed||this.route(e,t).catch(r=>this.report_(r));}async route(e,t){switch(e){case "hello":return this.onHello(t);case "offer":return this.onOffer(t);case "answer":return this.onAnswer(t);case "ice":return this.onIce(t);case "peerLeft":return this.onPeerLeft(t);case "migration":return this.onMigration(t);case "matchEnded":return this.onMatchEnded(t);case "error":return this.onSignalingError(t);case "relayDraining":return this.onRelayDraining(t);default:return}}async onHello(e){let t=D(e.playerId),r=D(e.hostPlayerId),i=st(e.hostEpoch);if(!(t===null||r===null||i===null)&&(this.mark("signaling hello"),this.localPlayerId=t,this.migration===null&&(this.migration=new Ge(t,r,i)),!this.helloSeen)){if(this.helloSeen=true,this.isHost){this.mark("host listening"),this.settle(),await this.drainPendingOffers();return}this.pendingOffers.clear(),await this.offerToHost("initial");}}async drainPendingOffers(){if(this.pendingOffers.size===0)return;let e=[...this.pendingOffers.values()];this.pendingOffers.clear(),this.mark("queued offer drained");for(let t of e)try{await this.onOffer(t);}catch(r){this.report_(r);}}async offerToHost(e){let t=this.hostPlayerId,r=this.openLink(t,Ii),i=await r.connection.createOffer();await r.connection.setLocalDescription(i);let o=r.connection.localDescription?.sdp??i.sdp;if(!o)throw new w("invalid_sdp","the browser produced an offer with no SDP");se(o,"offer"),this.refuseMedia(o,"offer"),this.options.signaling.offer(o,e),this.mark("offer sent"),this.fetchCredential().catch(a=>this.report_(a));}async fetchCredential(){let e=++this.credentialGeneration,r=this.links.get(this.hostPlayerId)?.connection.localDescription?.sdp,i=r===void 0?null:ke(r);if(i===null)throw new w("invalid_sdp","this browser produced an offer with no usable DTLS fingerprint, so the platform cannot bind a credential to it");let o=this.options.credentialPacing??wa,a=()=>this.closed||e!==this.credentialGeneration,s=fa;for(;;){let l=o.delayUntilAllowed(this.options.clock.now());if(l>0&&!await this.waitForCredential(l,a))return;o.record(this.options.clock.now());try{let d=await this.options.api.joinCredential(this.options.matchId,i);if(a())return;this.credential=d,this.mark("credential ready"),this.maybeAuthenticate();return}catch(d){if(a())return;let m=d.status,c;if(m===409)c=Ti;else if(m===429){let p=d.retryAfterSeconds;typeof p=="number"&&Number.isFinite(p)&&p>=0?c=p*1e3:(c=s,s=Math.min(s*2,Ei)),c=Math.min(Math.max(c,Ti),Ei);}else throw d;if(!await this.waitForCredential(c,a))return}}}async waitForCredential(e,t){return this.options.clock.now()+e>this.credentialDeadline?false:(await this.sleep(e),!t())}async onOffer(e){let t=D(e.fromPlayerId),r=D(e.sdp);if(t===null||r===null)return;if(this.migration===null){if(this.pendingOffers.delete(t),this.pendingOffers.size>=ka){let l=this.pendingOffers.keys().next().value;l!==void 0&&this.pendingOffers.delete(l);}this.pendingOffers.set(t,e);return}if(!this.isHost||this.migration.isKicked(t)===true)return;let i=D(e.sessionId);if(i!==null&&this.answeredSessions.get(t)===i)return;se(r,"offer"),this.refuseMedia(r,"offer");let o=ke(r);if(o===null)throw new w("invalid_sdp",`the offer from ${t} carries no usable fingerprint`);if(this.mark("offer received"),i!==null&&this.answeredSessions.set(t,i),await this.refreshLease(false),this.closed||!this.isHost)return;let a=this.links.get(t);if(D(e.intent)==="ice_restart"&&a?.admitted===true&&a.remoteFingerprint!==null&&we(a.remoteFingerprint,o)){await this.answer(a,r),this.mark("ice restart answered");return}a&&this.dropLink(a,"resignalled",false);let s=this.openLink(t,this.nextPeerId++);s.remoteFingerprint=o,await this.answer(s,r),this.mark("answer sent");}async answer(e,t){await e.connection.setRemoteDescription({type:"offer",sdp:t}),this.flushCandidates(e);let r=await e.connection.createAnswer();await e.connection.setLocalDescription(r);let i=e.connection.localDescription?.sdp??r.sdp;if(!i)throw new w("invalid_sdp","the browser produced an answer with no SDP");se(i,"answer"),this.refuseMedia(i,"answer"),this.options.signaling.answer(e.playerId,i,e.assignedPeerId);}async onAnswer(e){if(this.isHost)return;let t=D(e.fromPlayerId),r=D(e.sdp);if(t===null||r===null)return;let i=this.links.get(this.hostPlayerId);if(!i||t!==this.hostPlayerId)return;se(r,"answer"),this.refuseMedia(r,"answer");let o=ke(r);if(o===null)throw new w("invalid_sdp","the host's answer carries no usable fingerprint");let a=st(e.assignedPeerId);if(i.restart!==void 0&&a!==null&&a!==i.assignedPeerId){this.warn(`the host answered the ICE restart from a new connection (peer id ${a}, this link is ${i.assignedPeerId}); dropping the link to re-dial`),this.dropLink(i,"restart_mismatch",true);return}this.mark("answer received"),i.remoteFingerprint=o,i.pendingAnswer=r,this.hostVerified(i)&&await this.applyAnswer(i,r);}hostVerified(e){let t=this.credential;return t===null||e.remoteFingerprint===null?false:we(t.hostFingerprint,e.remoteFingerprint)?true:(this.fail(new w("peer_unauthenticated","the host's DTLS certificate is not the one the platform attested for this match. Refusing before the handshake, so no credential is presented.")),this.dropLink(e,"host_fingerprint_mismatch",false),false)}maybeAuthenticate(){if(this.isHost)return;let e=this.links.get(this.hostPlayerId);if(!e||e.remoteFingerprint===null||e.connection.remoteDescription!==null)return;let t=e.pendingAnswer;t!==void 0&&this.hostVerified(e)&&this.applyAnswer(e,t).catch(r=>this.report_(r));}async applyAnswer(e,t){this.mark("host verified"),e.pendingAnswer=void 0,await e.connection.setRemoteDescription({type:"answer",sdp:t}),this.flushCandidates(e),e.restart!==void 0&&(e.restart.answered=true,this.progressRestart(e));}async onIce(e){let t=D(e.fromPlayerId),r=D(e.candidate);if(t===null||r===null)return;let i=this.links.get(t);if(!i)return;let o=D(e.sdpMid),a=st(e.sdpMLineIndex)??st(e.sdpMLineIndexAlt),s={candidate:r,...o===null?{}:{sdpMid:o},...a===null?{}:{sdpMLineIndex:a}};if(i.connection.remoteDescription===null){i.pendingCandidates.length<64&&i.pendingCandidates.push(s);return}await i.connection.addIceCandidate(s).catch(()=>{});}onPeerLeft(e){let t=D(e.playerId);if(t===null)return;let r=D(e.reason)??"left";if(t===this.localPlayerId){this.fail(new w("kicked",`this player was removed from the match (${r})`)),this.close();return}let i=this.links.get(t);i&&this.dropLink(i,r,true);}async onMigration(e){let t=D(e.hostPlayerId),r=st(e.hostEpoch);if(t===null||r===null||this.migration===null)return;let i=this.migration.apply({hostPlayerId:t,hostEpoch:r,...D(e.reason)===null?{}:{reason:D(e.reason)},...Array.isArray(e.kickedPlayerIds)?{kickedPlayerIds:e.kickedPlayerIds.filter(a=>typeof a=="string")}:{},...typeof e.migrationComplete=="boolean"?{migrationComplete:e.migrationComplete}:{}},this.options.clock.now());if(i.kind==="duplicate")return;if(i.kind==="completed"){this.emitter.emit("hostChanged",t,r);return}if(i.kind==="refused"){this.report_(new w("migration_failed",i.why));return}for(let a of [...this.links.values()])this.dropLink(a,"migrating",false);if(this.options.signaling.discardQueued(),this.credential=null,this.emitter.emit("migrating",t,r),i.state==="preparing"){this.options.signaling.migrationAck(r),this.watchMigration();return}this.watchMigration();let o=this.hostEpoch;await this.refreshLease(false),!(this.closed||this.hostEpoch!==o)&&await this.offerToHost("full_reconnect");}watchMigration(){let e=this.hostEpoch;this.options.clock.setTimeout(()=>{this.closed||this.migration===null||this.migration.currentEpoch===e&&this.migration.stalled(this.options.clock.now())&&this.reportedStallEpoch!==e&&(this.reportedStallEpoch=e,this.report_(new w("migration_failed",`host migration to epoch ${e} did not complete. The elected host never acknowledged it, so the platform is still holding the match in migration.`)));},31e3);}onMatchEnded(e){let t=D(e.reason)??"ended";this.ended=t,this.emitter.emit("matchEnded",t),this.ready!==null&&this.fail(new w("match_ended",`the match ended: ${t}`)),this.close();}onSignalingError(e){let t=D(e.code)??"unknown",r=D(e.message)??"the platform refused a signaling message",i=this.isHost?void 0:this.links.get(this.hostPlayerId)?.restart;if(i!==void 0){this.warn(`the platform refused an ICE restart (${t}: ${r})`),i.settle(false);return}this.report_(new w("signaling_failed",`${t}: ${r}`));}async onRelayDraining(e){if(this.migration===null)return;let t=new Set((Array.isArray(e.relayIps)?e.relayIps:[]).filter(c=>typeof c=="string").map(c=>c.toLowerCase())),r=this.isHost?void 0:this.links.get(this.hostPlayerId),i,o=this.options.clock.now(),a=Date.parse(D(e.deadline)??""),s=(Number.isFinite(a)?a:o+Gi)-o,l=this.options.clock.setTimeout(()=>{this.drainTimers.delete(l),this.onDrainDeadline(t,r,i).catch(c=>this.report_(c));},Math.max(0,Math.min(s,Ra)));if(this.drainTimers.add(l),await this.refreshLease(true),this.closed||(this.withoutDrainingRelays(t),this.isHost))return;let d=this.links.get(this.hostPlayerId);if(d===void 0||!d.admitted)return this.reportRestart("skipped","announced");let m=await Ci(d.connection,t);if(!(this.closed||this.links.get(d.playerId)!==d)){if(m===false)return this.reportRestart("skipped","announced");await this.restartIceToHost(d,"announced")&&(i=d);}}async onDrainDeadline(e,t,r){if(this.closed||this.isHost)return;let i=this.links.get(this.hostPlayerId);if(i===void 0)return;let o=await Ci(i.connection,e);if(this.closed||this.links.get(i.playerId)!==i)return;(o===true||o===null&&i===t&&i!==r)&&this.dropLink(i,"relay_deadline",true);}withoutDrainingRelays(e){let t=this.lease;if(t===null||e.size===0)return;let r=false,i=[];for(let o of t.iceServers){let a=o.urls.filter(s=>{let l=xa(s),d=l===null||!e.has(l);return r||=!d,d});a.length>0&&i.push({...o,urls:a});}r&&(this.lease={iceServers:i,expiresAt:t.expiresAt},this.reconfigureLinks());}restartIceToHost(e,t){if(e.restart!==void 0)return e.restart.done;if(e.connection.restartIce===void 0)return this.reportRestart("skipped",t),Promise.resolve(false);let r=()=>{},i=new Promise(s=>{r=s;}),o=this.options.clock.setTimeout(()=>a.settle(false),_a),a={done:i,answered:false,settle:s=>{e.restart===a&&(e.restart=void 0,this.options.clock.clearTimeout(o),this.closed||this.reportRestart(s?"success":"failed",t),r(s));}};return e.restart=a,this.offerIceRestart(e).catch(s=>{this.warn(`could not restart ICE on the connection to the host: ${at(s)}`),a.settle(false);}),i}async offerIceRestart(e){let t=e.connection;t.restartIce?.();let r=await t.createOffer();await t.setLocalDescription(r);let i=t.localDescription?.sdp??r.sdp;if(!i)throw new w("invalid_sdp","the browser produced an offer with no SDP");se(i,"offer"),this.refuseMedia(i,"offer"),!(e.restart===void 0||this.links.get(e.playerId)!==e)&&(this.options.signaling.offer(i,"ice_restart"),this.mark("ice restart offered"));}progressRestart(e){let t=e.restart;if(t===void 0)return;let r=e.connection.iceConnectionState;r==="failed"||r==="closed"?t.settle(false):t.answered&&Kn(r)&&t.settle(true);}reportRestart(e,t){this.report([{type:"ice_restart_outcome_total",value:1,labels:{outcome:e,reason:t}}]);}async takeLease(e){this.leaseAskedAt=this.options.clock.now();let t=this.clusterTargetIds,r=await _i(this.options.api,this.options.matchId,t,e);this.lease={iceServers:r.iceServers,expiresAt:r.expiresAt},this.clusterTargetIds=t.slice(Math.max(0,t.indexOf(r.clusterTargetId)));}refreshLease(e){if(this.clusterTargetIds.length===0||this.closed)return Promise.resolve();if(this.leaseRenewal!==null)return this.leaseRenewal;let t=this.options.clock.now();if(this.leaseAskedAt!==null&&t-this.leaseAskedAt<va)return Promise.resolve();let r=this.lease;if(!e&&r!==null&&r.expiresAt-t>=ba)return Promise.resolve();let i=this.takeLease().then(()=>{this.closed||(this.mark("turn re-leased"),this.reconfigureLinks());},o=>{this.leaseRenewalWarned||(this.leaseRenewalWarned=true,this.warn(`could not renew the TURN lease for this match, so connections keep the relays they were given: ${at(o)}`));}).finally(()=>{this.leaseRenewal=null;});return this.leaseRenewal=i,i}reconfigureLinks(){for(let e of this.links.values()){let t=e.connection;if(t.setConfiguration!==void 0)try{t.setConfiguration({...t.getConfiguration?.(),iceServers:this.iceServers()});}catch{}}}iceServers(){return (this.lease?.iceServers??[]).map(e=>({urls:[...e.urls],username:e.username,credential:e.credential}))}openLink(e,t){let i=(this.options.peerConnectionFactory??Wt)({iceServers:this.iceServers(),...this.options.certificate===void 0?{}:{certificates:[this.options.certificate]}}),o={};for(let p of ot){let y=i.createDataChannel(p,Ut[p]);y.binaryType="arraybuffer",o[p]=y;}let a={playerId:e,assignedPeerId:t,connection:i,channels:o,remoteFingerprint:null,admitted:false,pendingCandidates:[],detachHandshake:()=>{},detachAll:()=>{}},s=p=>{let y=p.candidate;y?.candidate&&this.options.signaling.candidate(e,y.candidate,y.sdpMid??void 0,y.sdpMLineIndex??void 0);},l=()=>this.onIceState(a),d=()=>this.onConnectionState(a),m=p=>{this.onHandshakeMessage(a,p.data).catch(y=>this.report_(y));},c=()=>{this.onReliableOpen(a).catch(p=>this.report_(p));};return i.addEventListener("icecandidate",s),i.addEventListener("iceconnectionstatechange",l),i.addEventListener("connectionstatechange",d),o.reliable.addEventListener("open",c),o.reliable.addEventListener("message",m),a.detachHandshake=()=>{o.reliable.removeEventListener("open",c),o.reliable.removeEventListener("message",m);},a.detachAll=()=>{a.detachHandshake(),i.removeEventListener("icecandidate",s),i.removeEventListener("iceconnectionstatechange",l),i.removeEventListener("connectionstatechange",d);},this.links.set(e,a),a}flushCandidates(e){let t=e.pendingCandidates;e.pendingCandidates=[];for(let r of t)e.connection.addIceCandidate(r).catch(()=>{});}async onReliableOpen(e){if(this.mark("channel open"),this.isHost)return;let t=this.credential;if(t===null)throw new w("peer_unauthenticated","the channel opened before the platform issued a join credential, so there is nothing to present");if(this.options.clock.now()>=t.expiresAt)throw new w("timeout","the join credential expired before the channel opened \u2014 reconnect for a fresh one");e.channels.reliable.send(Wn({jwt:t.credential,protocolVersion:32769,assignedPeerId:e.assignedPeerId}));}async onHandshakeMessage(e,t){if(!(t instanceof ArrayBuffer))return;let r=Vn(t);if(r!==null){if(this.isHost&&r.kind==="join"){await this.admit(e,r.jwt,r.protocolVersion);return}if(!this.isHost&&r.kind==="admit"){e.assignedPeerId=r.assignedPeerId,this.finishAdmission(e),this.migration?.complete();return}!this.isHost&&r.kind==="reject"&&(this.fail(new w(xi(r.reason),Si(r.reason))),this.dropLink(e,"rejected",false));}}async admit(e,t,r){if(r!==32769){this.refuseLink(e,2,"version_mismatch",`it presented protocol version ${r}, not this SDK's ${32769} \u2014 an engine peer cannot join a browser-hosted match`);return}let i=ke(e.connection.localDescription?.sdp??"");if(i===null||e.remoteFingerprint===null){this.refuseLink(e,1,"fingerprint_unavailable","this connection's DTLS fingerprints could not be read from its SDP, so no credential can be checked against it");return}let o;try{o=await jt(t,{matchId:this.options.matchId,gameId:this.options.gameId,hostEpoch:this.hostEpoch,peerFingerprint:e.remoteFingerprint,hostFingerprint:i,now:this.options.clock.now()},this.keys,this.replays,{issuer:this.options.baseUrl.replace(/\/+$/,"")});}catch(a){let s=a instanceof w&&a.reason==="keys_unavailable";this.refuseLink(e,s?131:1,s?"keys_unavailable":"credential_invalid",at(a)),s&&this.fail(a);return}if(o.playerId!==e.playerId.toLowerCase()){this.refuseLink(e,129,"credential_player_mismatch",`its credential was issued to ${o.playerId}, not to the player who signalled this connection`);return}if(this.migration?.isKicked(o.playerId)===true){this.refuseLink(e,130,"kicked",`it was kicked from this match at host epoch ${this.hostEpoch}`);return}e.channels.reliable.send(Hn({protocolVersion:32769,assignedPeerId:e.assignedPeerId})),this.finishAdmission(e);}refuseLink(e,t,r,i){this.warn(`refused to admit ${e.playerId}: ${i}`);try{e.channels.reliable.send(Bn(t));}catch{}this.dropLink(e,r,false);}finishAdmission(e){this.mark(this.isHost?"peer admitted":"admitted"),e.admitted=true,e.detachHandshake(),e.detachHandshake=()=>{},this.emitter.emit("peerJoined",this.expose(e)),this.settle();}onIceState(e){this.progressRestart(e);let t=e.connection.iceConnectionState;if(!(t!=="failed"&&t!=="disconnected"&&t!=="closed")){if(this.isHost){(t==="failed"||t==="closed")&&this.dropLink(e,t,true);return}if(t==="disconnected"){if(e.recovering===true)return;if(e.admitted&&e.connection.restartIce!==void 0&&this.reportedUnreachableEpoch!==this.hostEpoch){this.recoverHostLink(e).catch(r=>this.report_(r));return}}this.voteHostUnreachable(t);}}async recoverHostLink(e){let t=this.hostEpoch,r=()=>!this.closed&&this.hostEpoch===t&&this.links.get(e.playerId)===e;e.recovering=true;try{if(await this.refreshLease(!0),!r())return;if(Kn(e.connection.iceConnectionState))return this.reportRestart("skipped","disconnected");if(await this.restartIceToHost(e,"disconnected")||!r()||Kn(e.connection.iceConnectionState))return;this.voteHostUnreachable(Mi(e.connection.iceConnectionState));}finally{e.recovering=false;}}voteHostUnreachable(e){if(this.reportedUnreachableEpoch!==this.hostEpoch){this.reportedUnreachableEpoch=this.hostEpoch;try{this.options.signaling.reportHostUnreachable(this.hostEpoch,e);}catch(t){this.report_(t);}}}onConnectionState(e){let t=e.connection.connectionState;t!=="failed"&&t!=="closed"||(e.recovering===true&&this.voteHostUnreachable(Mi(e.connection.iceConnectionState)),this.dropLink(e,t,true));}expose(e){return {playerId:e.playerId,assignedPeerId:e.assignedPeerId,connection:e.connection,channels:e.channels,send:(t,r="reliable")=>e.channels[r].send(t)}}dropLink(e,t,r){this.links.delete(e.playerId),e.restart?.settle(false),e.detachAll();for(let i of ot)Oi(e.channels[i]);Oi(e.connection),r&&e.admitted&&this.emitter.emit("peerLeft",e.playerId,t);}settle(){this.ready?.resolve(),this.ready=null;}fail(e){if(this.count(e.reason),this.ready!==null){let t=this.ready;this.ready=null,t.reject(e);return}this.emitter.emit("error",e);}report_(e){this.fail(e instanceof w?e:new w("connection_failed",at(e),{cause:e}));}count(e){this.counted||!Sa.has(e)||(this.counted=true,this.report([{type:"connection_failures_total",value:1}]));}refuseMedia(e,t){if(Lt(e))throw new w("media_refused",`the ${t} carries an active audio or video section. This path carries data channels only; voice belongs to the platform voice service, and gem-api age-gates relayed SDP that proposes media.`)}mark(e){this.options.boot?.mark(e);}sleep(e){return new Promise(t=>this.options.clock.setTimeout(t,e))}warn(e){let t=this.options.log;t!==null&&(t??(r=>console.warn(r)))(`[gem] ${e}`);}get endedReason(){return this.ended}};function D(n){return typeof n=="string"&&n.length>0?n:null}function st(n){return typeof n=="number"&&Number.isInteger(n)?n:null}function at(n){return n instanceof Error?n.message:String(n)}async function Ci(n,e){if(n.getStats===void 0)return null;let t=new Map;try{(await n.getStats()).forEach((s,l)=>{typeof s=="object"&&s!==null&&t.set(l,s);});}catch{return null}let r=[...t.values()],i=r.find(s=>s.type==="transport")?.selectedCandidatePairId,o=(typeof i=="string"?t.get(i):void 0)??r.find(s=>s.type==="candidate-pair"&&s.selected===true)??r.find(s=>s.type==="candidate-pair"&&s.nominated===true&&s.state==="succeeded");if(o===void 0)return null;let a=[o.localCandidateId,o.remoteCandidateId].map(s=>typeof s=="string"?t.get(s):void 0);return a.every(s=>s===void 0)?null:a.some(s=>{let l=s?.address??s?.ip;return typeof l=="string"&&e.has(l.toLowerCase())})}function xa(n){let e=/^(?:turns?|stuns?):(?:\[([^\]]+)\]|([^:?/]+))/i.exec(n),t=e?.[1]??e?.[2];return t===void 0?null:t.toLowerCase()}function Kn(n){return n==="connected"||n==="completed"}function Mi(n){return n==="failed"||n==="closed"?n:"disconnected"}function Oi(n){try{n.close();}catch{}}var Aa=100,Pa=50,De=class{constructor(e){this.options=e;}options;queue=[];drainHandle=null;dropped=0;closed=false;dropListener=null;onDropped(e){this.dropListener=e;}join(){this.assertUsable(),this.options.bridge.signal("join",this.options.matchId,{});}offer(e,t){this.assertUsable(),this.options.bridge.signal("offer",this.options.matchId,{sdp:e,intent:t});}answer(e,t,r){this.assertUsable(),this.options.bridge.signal("answer",this.options.matchId,{peerId:e,sdp:t,assignedPeerId:r});}candidate(e,t,r,i){if(this.assertUsable(),this.queue.length>=(this.options.maxQueuedCandidates??Aa)){this.dropped+=1;return}this.queue.push({peerId:e,candidate:t,...r===void 0?{}:{sdpMid:r},...i===void 0?{}:{sdpMLineIndex:i}}),this.schedule();}migrationAck(e){this.assertUsable(),this.options.bridge.signal("migrationAck",this.options.matchId,{hostEpoch:e});}reportHostUnreachable(e,t){this.assertUsable(),this.options.bridge.signal("hostUnreachable",this.options.matchId,{hostEpoch:e,evidence:{iceState:t,lastSeenAt:Math.floor(this.options.clock.now()/1e3)}});}discardQueued(){this.queue.length=0;}close(){this.closed=true,this.discardQueued(),this.drainHandle!==null&&(this.options.clock.clearTimeout(this.drainHandle),this.drainHandle=null);}assertUsable(){if(this.closed)throw new w("signaling_unavailable","this signaling client has been closed");if(!this.options.bridge.parentSupports("p2p"))throw new w("signaling_unavailable","the page embedding this game did not advertise the `p2p` capability, so there is nothing to carry signaling. P2P needs the notification WebSocket, and only the parent can hold one \u2014 see docs/p2p-and-parties.md \xA72.")}schedule(){this.drainHandle!==null||this.closed||(this.drainHandle=this.options.clock.setTimeout(()=>{this.drainHandle=null;let e=this.queue.shift();if(e!==void 0){let{peerId:t,candidate:r,sdpMid:i,sdpMLineIndex:o}=e;this.options.bridge.signal("ice",this.options.matchId,{peerId:t,candidate:r,...i===void 0?{}:{sdpMid:i},...o===void 0?{}:{sdpMLineIndex:o}});}if(this.dropped>0&&this.queue.length===0){let t=this.dropped;this.dropped=0,this.options.onCandidatesDropped?.(t),this.dropListener?.(t);}this.queue.length>0&&this.schedule();},this.options.candidateIntervalMs??Pa));}};var zn="webrtc_p2p";function Ia(n){let e=new Oe({client:n.client,gameId:n.gameId}),t=new Map;return {handleSignalEvent(r,i,o){t.get(r)?.handleSignal(i,o);},session(r){return t.get(r)??null},async connect(r={}){if(n.bridge===null)throw new w("signaling_unavailable","P2P signaling rides the notification WebSocket, which only the page embedding this game can hold. There is no bridge here \u2014 this is `mock` mode, or a game running outside the Arcade frame. See docs/p2p-and-parties.md \xA72.");let i=H(r.clusterTargetId),o=i===void 0?await Ta(n):[i];n.rooms.isHost&&n.rooms.currentMatch===null&&n.rooms.currentRoom?.matchId===void 0&&await n.rooms.startMatch({...i===void 0?{}:{clusterTargetId:i},...r.signal===void 0?{}:{signal:r.signal}});let a=await n.rooms.awaitMatch({...r.timeoutMs===void 0?{}:{timeoutMs:r.timeoutMs},...r.signal===void 0?{}:{signal:r.signal}});if(a.protocol!==zn)throw new w("unsupported_protocol",`match ${a.matchId} speaks '${a.protocol}', not '${zn}'. Use \`dedicatedServer.connect()\` for a dedicated match; the two flows share WebRTC and nothing else.`);let s=new De({bridge:n.bridge,matchId:a.matchId,clock:n.clock}),l=new Le({api:e,clock:n.clock,matchId:a.matchId,gameId:n.gameId(),baseUrl:typeof n.baseUrl=="function"?n.baseUrl():n.baseUrl,signaling:s,...o.length===0?{}:{clusterTargetIds:o},...r.peerConnectionFactory===void 0?{}:{peerConnectionFactory:r.peerConnectionFactory},certificate:r.certificate??await Ht(),...r.timeoutMs===void 0?{}:{connectTimeoutMs:r.timeoutMs},...n.boot===void 0?{}:{boot:n.boot},...n.log===void 0?{}:{log:n.log}});s.onDropped(d=>l.notifyCandidatesDropped(d)),t.set(a.matchId,l),l.on("matchEnded",()=>t.delete(a.matchId));try{await l.start(r.signal);}catch(d){throw t.delete(a.matchId),l.close(),d}return l}}}async function Ta(n){if(n.resolveClusterTargets!==void 0)return V(await n.resolveClusterTargets());let e=H(await n.resolveClusterTarget?.());return e===void 0?[]:[e]}function Li(n){return typeof n=="string"&&n.trim()!==""}function Di(n,e){return Li(n)?n.trim():Li(e)?e:null}function lt(n){let e=String(n);if(!/^[A-Za-z0-9_.-]{1,128}$/.test(e)||e==="."||e==="..")throw new TypeError(`${JSON.stringify(e)} is not a usable path segment \u2014 it is interpolated into a URL`);return encodeURIComponent(e)}async function qi(n,e,t){let r=`/v1/games/${lt(n.gameId())}/matchmaking/tickets/${lt(e)}`;return n.client.delete(r,t?.signal)}async function $i(n,e,t){let r=`/v1/games/${lt(n.gameId())}/matchmaking/tickets`;return n.client.post(r,e,t?.signal)}async function Fi(n,e,t){let r=`/v1/games/${lt(n.gameId())}/matchmaking/tickets/${lt(e)}/heartbeat`;return n.client.post(r,void 0,t?.signal)}function dt(n){let e=n??{};if(typeof e.ticket_id!="string"||e.ticket_id.length===0)throw new Error("[gem] a matchmaking-ticket response carried no id");let t=e.heartbeat_interval_seconds;return {id:e.ticket_id,status:typeof e.status=="string"?e.status:"queued",...typeof e.reason=="string"?{reason:e.reason}:{},...typeof e.game_room_id=="string"?{roomId:e.game_room_id}:{},partySize:typeof e.party_size=="number"?e.party_size:1,queuedAt:Ni(e.queued_at),expiresAt:Ni(e.expires_at),heartbeatIntervalSeconds:typeof t=="number"&&t>0?t:0}}function Ni(n){if(typeof n!="string")return 0;let e=Date.parse(n);return Number.isFinite(e)?Math.floor(e/1e3):0}var Ea=3,ji=1e3,Bt=class{constructor(e){this.options=e;this.snapshot=e.first;let t=new Promise((o,a)=>{this.resolve=o,this.reject=a;});t.catch(()=>{});let r=()=>this.snapshot,i=()=>this.status;this.ticket={get id(){return r().id},get status(){return i()},get partySize(){return r().partySize},get queuedAt(){return r().queuedAt},get expiresAt(){return r().expiresAt},outcome:t,cancel:()=>(this.requestCancel(),t)};}options;ticket;snapshot;status="queued";timer=null;busy=false;again=false;cancelRequested=false;soon=false;misses=0;settled=false;resolve;reject;start(){this.apply(this.snapshot);}poke(){this.settled||(this.soon=true,!this.busy&&(this.clearTimer(),this.tick()));}abandon(e=false){this.settled||(this.options.cancel(this.snapshot.id,e).catch(()=>{}),this.settle({status:"cancelled",reason:"requested"}));}requestCancel(){this.settled||(this.cancelRequested=true,this.trigger());}trigger(){if(this.busy){this.again=true;return}this.clearTimer(),this.tick();}schedule(e=false){if(this.settled)return;this.clearTimer();let t=e?ji:Math.max(ji,this.snapshot.heartbeatIntervalSeconds*1e3);this.timer=this.options.clock.setTimeout(()=>{this.timer=null,this.tick();},t);}clearTimer(){this.timer!==null&&(this.options.clock.clearTimeout(this.timer),this.timer=null);}async tick(){if(this.settled||this.busy)return;this.busy=true,this.again=false;let e=null,t=null;try{e=this.cancelRequested?await this.options.cancel(this.snapshot.id):await this.options.heartbeat(this.snapshot.id);}catch(r){t=r;}if(this.busy=false,!this.settled){if(e===null){this.missed(t);return}this.misses=0,await this.apply(e);}}missed(e){if(e instanceof q&&e.reason==="not_found"){this.settle({status:"lost"});return}if(this.misses+=1,this.misses<Ea){this.next();return}this.options.cancel(this.snapshot.id).catch(()=>{});let t=e instanceof q&&e.code==="matchmaking_unavailable";this.settle({status:t?"unavailable":"lost"});}async apply(e){if(this.snapshot=e,e.status==="queued"){this.next();return}if(e.status==="matched"){await this.seat(e);return}e.status==="expired"?this.settle({status:"expired",reason:e.reason??"timed_out"}):e.status==="cancelled"?this.settle({status:"cancelled",reason:e.reason??"requested"}):this.settle({status:"failed",reason:e.reason??e.status});}next(){if(this.again){this.again=false,this.tick();return}let e=this.soon;this.soon=false,this.schedule(e);}async seat(e){if(e.roomId===void 0){this.fail(new Error("[gem] a matched matchmaking ticket named no game room"));return}this.busy=true;try{let t=await this.options.seat(e.roomId);this.busy=!1,this.settle({status:"matched",room:t});}catch(t){this.busy=false,this.fail(t);}}settle(e){this.settled||(this.finish(e.status),this.resolve(e));}fail(e){this.settled||(this.finish("matched"),this.reject(e));}finish(e){this.settled=true,this.status=e,this.clearTimer(),this.options.onSettled();}};function Ui(n){let e=typeof n=="object"&&n!==null?n.play_with:void 0;return e==null?null:e==="joined"?"joined":"unavailable"}function Z(n){return typeof n=="string"&&n!==""?n:void 0}var Ca=new Set(["localhost","127.0.0.1","[::1]"]);function qe(n){return Ma(n)}function Ma(n){let e=Z(n);if(e===void 0)return;let t;try{t=new URL(e);}catch{return}if(!(t.username!==""||t.password!=="")&&(t.protocol==="https:"||t.protocol==="http:"&&Ca.has(t.hostname)))return e}var Oa=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,Ga=/^[a-z][a-z0-9_]{0,31}$/;function La(n){if(!Array.isArray(n))return;let e=[],t=new Set;for(let r of n.slice(0,8)){let i=r??{},o=Z(i.family),a=Z(i.avatar_3d_id),s=qe(i.model_url),l=qe(i.icon_url);o===void 0||!Ga.test(o)||t.has(o)||a===void 0||!Oa.test(a)||s===void 0||l===void 0||(t.add(o),e.push({family:o,id:a,modelUrl:s,iconUrl:l}));}return e}function Da(n){if(!Array.isArray(n))return [];let e=[];for(let t of n){let r=t??{},i=Z(r.item_id),o=qe(r.icon_url);i!==void 0&&o!==void 0&&e.push({itemId:i,iconUrl:o});}return e}function Wi(n){let e=n??{},t=Z(e.player_id),r=Z(e.display_name),i=Z(e.discriminator),o=Z(e.display_tag),a=Z(e.avatar_icon_id),s=qe(e.avatar_icon_url),l=Z(e.avatar_3d_id),d=qe(e.avatar_3d_model_url),m=qe(e.avatar_3d_icon_url),c=La(e.avatars_3d);return {...t===void 0?{}:{playerId:t},...r===void 0?{}:{displayName:be(r,"displayName")},...i===void 0?{}:{discriminator:i},...o===void 0?{}:{displayTag:be(o,"displayTag")},...a===void 0?{}:{avatarIconId:a},...s===void 0?{}:{avatarIconUrl:s},...l===void 0?{}:{avatar3dId:l},...d===void 0?{}:{avatar3dModelUrl:d},...m===void 0?{}:{avatar3dIconUrl:m},...c===void 0?{}:{avatars3d:c},badges:Da(e.badges)}}function Vt(n){if(typeof n!="object"||n===null)return;let e={};for(let[t,r]of Object.entries(n)){let i=Xn(r);i!==void 0&&(e[t]=i);}return e}function Kt(n){if(Array.isArray(n))return n.map(qa).filter(e=>!!e)}function Xn(n){if(Yn(n)||Array.isArray(n)&&n.every(Yn))return n}function Yn(n){return typeof n=="string"||typeof n=="boolean"||typeof n=="number"&&Number.isFinite(n)}function qa(n){let e=n??{};if(typeof e.name!="string"||e.name.length===0)return null;let t=Xn(e.fixed),r=Xn(e.default);return {name:e.name,type:typeof e.type=="string"?e.type:"string",list:e.list===true,required:e.required===true,mutable:e.mutable!==false,...t===void 0?{}:{fixed:t},...r===void 0?{}:{default:r},...$e("description",$a(e.description)),...Array.isArray(e.allowed)&&e.allowed.every(Yn)?{allowed:e.allowed}:{},...$e("min",ct(e.min)),...$e("max",ct(e.max)),...$e("minItems",ct(e.min_items)),...$e("maxItems",ct(e.max_items)),...$e("maxLength",ct(e.max_length))}}function $a(n){return typeof n=="string"&&n.length>0?n:void 0}function ct(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}function $e(n,e){return e===void 0?{}:{[n]:e}}function Y(n){let e=n??{},t=C(e.id)??C(e.game_room_id);if(t===void 0)throw new Error("[gem] a game-room response carried no id");let r=Array.isArray(e.members)?e.members.map(Qn):[];return {id:t,gameId:C(e.game_id)??"",name:C(e.name)??"",status:C(e.status)??"open",hostPlayerId:C(e.host_player_id)??"",maxPlayers:ee(e.max_players)??0,memberCount:ee(e.member_count)??r.length,members:r,isPublic:e.is_public!==false,...J("joinCode",C(e.join_code)),...e.settings===void 0?{}:{settings:e.settings},...J("roomArgs",Vt(e.room_args)),...J("roomArgsTemplate",Kt(e.room_args_template)),gameChannel:C(e.game_channel)??"release",...J("region",C(e.region)),...J("clusterTargetId",C(e.cluster_target_id)),selfHosted:e.self_hosted===true,...J("matchId",C(e.match_id)),version:ee(e.version)??0,...J("templateId",C(e.template_id)),...J("templateSlug",C(e.template_slug)),roomType:C(e.game_room_type)??"Standard",...J("transports",Ze(e.transports)??void 0),createdAt:ee(e.created_at)??0,updatedAt:ee(e.updated_at)??0,...J("startedAt",ee(e.started_at))}}function Qn(n){let e=n??{};return {...Wi(e),playerId:C(e.player_id)??"",isReady:e.is_ready===true,role:C(e.role)??"member",joinedAt:ee(e.joined_at)??0,...J("groupId",C(e.group_id)),...J("platform",C(e.platform))}}function ut(n){let e=n??{};return {id:C(e.id)??C(e.game_room_id)??"",gameId:C(e.game_id)??"",name:C(e.name)??"",status:C(e.status)??"open",hostPlayerId:C(e.host_player_id)??"",maxPlayers:ee(e.max_players)??0,memberCount:ee(e.member_count)??0,isPublic:e.is_public!==false,gameChannel:C(e.game_channel)??"release",roomType:C(e.game_room_type)??"Standard",createdAt:ee(e.created_at)??0}}function C(n){return typeof n=="string"&&n.length>0?n:void 0}function ee(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}function J(n,e){return e===void 0?{}:{[n]:e}}var Fe=class{constructor(e){this.options=e;}options;list(e){let t=new URLSearchParams;e.status&&t.set("status",e.status),e.templateSlug&&t.set("template_slug",e.templateSlug),e.limit!==void 0&&t.set("limit",String(e.limit)),e.cursor&&t.set("cursor",e.cursor);let r=t.size>0?`?${t.toString()}`:"";return this.options.client.get(`${this.baseV1()}${r}`,e.signal).then(i=>({rooms:(i.game_rooms??[]).map(ut),...typeof i.next_cursor=="string"?{nextCursor:i.next_cursor}:{}}))}myRooms(e){return this.options.client.get(`/v1/games/${encodeURIComponent(this.options.gameId())}/players/me/game-rooms`,e).then(t=>({rooms:(t.game_rooms??[]).map(ut)}))}create(e,t,r){return Na(e.maxPlayers),this.options.client.post(this.baseV2(),{name:e.name,...e.isPublic===void 0?{}:{is_public:e.isPublic},...e.settings===void 0?{}:{settings:e.settings},...e.region===void 0?{}:{region:e.region},...r===void 0?{}:{cluster_target_id:r},...e.templateId===void 0?{}:{template_id:e.templateId},...e.templateSlug===void 0?{}:{template_slug:e.templateSlug},...e.partyId===void 0?{}:{party_id:e.partyId},...this.asksSelfHosted(e.partyId)?{self_hosted:true}:{},...e.roomArgs===void 0?{}:{room_args:e.roomArgs},...pt(t)},e.signal).then(Y)}get(e,t){return this.options.client.get(this.roomV2(e),t).then(Y)}join(e,t,r){return this.options.client.post(`${this.roomV2(e)}/join`,{...t.partyId===void 0?{}:{party_id:t.partyId},...pt(r)},t.signal).then(Y)}joinByCode(e,t,r){return this.options.client.post(`${this.baseV2()}/join`,{join_code:e,...t.partyId===void 0?{}:{party_id:t.partyId},...pt(r)},t.signal).then(Y)}quickJoin(e,t,r=[]){return Zn(this.options,e,t,r).then(i=>i.room)}createTicket(e,t,r=[]){return $i(this.generated(),{templates:e.templateSlugs.map(i=>({template_slug:i})),...r.length===0?{}:{cluster_target_ids:[...r]},...e.partyId===void 0?{}:{party_id:e.partyId},...pt(t)},{...e.signal===void 0?{}:{signal:e.signal}}).then(dt)}heartbeatTicket(e){return Fi(this.generated(),e).then(dt)}cancelTicket(e,t=false){return t?this.options.client.delete(`/v1/games/${encodeURIComponent(this.options.gameId())}/matchmaking/tickets/${encodeURIComponent(e)}`,void 0,{keepalive:true}).then(dt):qi(this.generated(),e).then(dt)}leave(e,t,r,i=false){return this.options.client.post(`${this.roomV2(e)}/leave`,{leave_as_group:t},r,{keepalive:i}).then(()=>{})}asksSelfHosted(e){return Bi(this.options,e)}close(e,t){return this.options.client.delete(this.roomV1(e),t).then(()=>{})}update(e,t){return this.options.client.patch(this.roomV2(e),{...t.name===void 0?{}:{name:t.name},...t.maxPlayers===void 0?{}:{max_players:t.maxPlayers},...t.isPublic===void 0?{}:{is_public:t.isPublic},...t.settings===void 0?{}:{settings:t.settings},...t.roomArgs===void 0?{}:{room_args:t.roomArgs}},t.signal).then(Y)}setReady(e,t,r){return this.options.client.post(`${this.roomV2(e)}/ready`,{is_ready:t},r).then(Y)}kick(e,t,r){return this.options.client.post(`${this.roomV1(e)}/kick/${encodeURIComponent(t)}`,{},r).then(()=>this.get(e,r))}transferHost(e,t,r){return this.options.client.post(`${this.roomV2(e)}/transfer/${encodeURIComponent(t)}`,{},r).then(Y)}leaveMatch(e,t){return this.options.client.post(`/v1/games/${encodeURIComponent(this.options.gameId())}/matches/${encodeURIComponent(e)}/leave`,{},void 0,{keepalive:t}).then(()=>{})}generated(){return {client:this.options.client,gameId:this.options.gameId}}baseV2(){return Hi(this.options)}baseV1(){return `/v1/games/${encodeURIComponent(this.options.gameId())}/game-rooms`}roomV2(e){return `${this.baseV2()}/${encodeURIComponent(e)}`}roomV1(e){return `${this.baseV1()}/${encodeURIComponent(e)}`}};function Hi(n){return `/v2/games/${encodeURIComponent(n.gameId())}/game-rooms`}function Bi(n,e){return e===void 0&&n.selfHosted?.()===true}function Zn(n,e,t,r=[],i){let o=Bi(n,e.partyId);return n.client.post(`${Hi(n)}/quick-join`,{...e.templateId===void 0?{}:{template_id:e.templateId},...e.templateSlug===void 0?{}:{template_slug:e.templateSlug},...Fa(e.templateSlugs,o),...e.region===void 0?{}:{region:e.region},...r.length===0?{}:{cluster_target_ids:[...r]},...e.name===void 0?{}:{name:e.name},...e.partyId===void 0?{}:{party_id:e.partyId},...o?{self_hosted:true}:{},...i===void 0?{}:{play_with_ticket:i},...pt(t)},e.signal).then(a=>({room:Y(a),playWith:i===void 0?null:Ui(a)}))}function Fa(n,e){return n===void 0?{}:{templates:(e?n.slice(0,1):n).map(r=>({template_slug:r}))}}function pt(n){return {platform:n.platform,game_version:n.gameVersion,engine_version:n.engineVersion}}function Na(n){n!==void 0&&console.warn(`[gem] createRoom: maxPlayers (${n}) is deprecated and was not sent. A room's seats come from the room template published in your game config \u2014 name one with templateSlug \u2014 and a room created without a template seats 8. To change a room that already exists, call update({ maxPlayers }) as its host.`);}var Ne=class{listeners=new Map;retainedMatch=null;on(e,t){let r=this.listeners.get(e);if(r||(r=new Set,this.listeners.set(e,r)),r.add(t),e==="matchReady"&&this.retainedMatch!==null){let i=this.retainedMatch;queueMicrotask(()=>{this.listeners.get(e)?.has(t)&&this.retainedMatch===i&&t(i);});}return ()=>this.off(e,t)}hasListeners(e){return (this.listeners.get(e)?.size??0)>0}off(e,t){this.listeners.get(e)?.delete(t);}once(e,t){let r=this.on(e,((...i)=>{r(),t(...i);}));return r}emit(e,...t){e==="matchReady"&&(this.retainedMatch=t[0]);let r=this.listeners.get(e);if(r)for(let i of [...r])i(...t);}get retained(){return this.retainedMatch}retain(e){this.retainedMatch=e;}clearRetained(){this.retainedMatch=null;}clear(){this.listeners.clear(),this.retainedMatch=null;}};var Jt=class{constructor(e){this.options=e;let t=e.first,r=new Promise(s=>{this.resolve=s;}),i=()=>this.status;this.ticket={id:t.ticketId,get status(){return i()},partySize:t.partySize??0,queuedAt:t.queuedAt??0,expiresAt:t.expiresAt??0,outcome:r};let o=t.queuedAt!==void 0&&t.expiresAt!==void 0?(t.expiresAt-t.queuedAt)*1e3:9e5,a=Math.min(Math.max(o,0),9e5)+5e3;this.timer=e.clock.setTimeout(()=>{this.timer=null,this.end({status:"lost"});},a);}options;ticket;status="queued";timer=null;resolve;get id(){return this.ticket.id}end(e){this.status==="queued"&&(this.status=e.status,this.timer!==null&&(this.options.clock.clearTimeout(this.timer),this.timer=null),this.options.onSettled(),this.resolve(e));}};function ja(n,e){return Math.min(e,500*2**Math.max(0,n-1))}var Ua=1e3,Wa=3e3,Ha=6e4,Ba=2,Vi=new Set(["game_room_closed","game_room_gone"]),Va=12e4,Ka=5e3,Ja=new Set(["invalid_cluster_target","capacity_unavailable"]),za=new Set(["already_in_matching_room","game_room_limit_per_game","game_room_limit_global"]);function Xi(n){return new tr(n)}var tr=class{constructor(e,t={}){this.options=e;this.internals=t;this.autoFollow=e.followMatch!==false,this.apiOptions={client:e.client,gameId:()=>this.requireGameId(),selfHosted:()=>e.localHostAvailable?.()===true},this.api=new Fe(this.apiOptions),this.installUnloadNotifier(),this.installResumeWatch(),this.retuneSeatWatch();}options;internals;emitter=new Ne;api;apiOptions;playWithAnswer=null;room=null;ticketWatch=null;queueing=false;partyWatch=null;heldQueued=null;queuedFromRoomId=null;session={phase:"none"};seenMatches=new Set;refusedMatches=new Set;reconciling=null;reconcileAgain=false;pendingHint=null;settling=null;pollHandle=null;polling=false;pollIntervalMs=null;pinnedIntervalMs=null;lastPushAt=-1/0;inFlight=[];unloadListener=null;visibilityListener=null;seatPollHandle=null;reported=new Set;disposed=false;followAbort=null;followingMatchId=null;autoFollow;reconnect=null;startsInFlight=0;autoStartRetried=false;autoStartRetry=null;async create(e){this.assertNotInRoom("create"),this.assertNotQueued("create");let t=this.clusterTargets(),r=await this.attachParty(e),i=await this.run("create",async()=>{let o=await t,a=s=>this.api.create(r,this.version("create"),s);for(let s=0;;s++)try{return await a(o[s])}catch(l){if(!er(l))throw l;if(s+1<o.length)continue;let d=(await this.afterRefusedClusters(l,o)).find(m=>!o.includes(m));if(d===void 0)throw l;return a(d)}});return this.enter(i,"created"),this.askForLocalServer(i),i}async join(e,t={}){this.assertNotInRoom("join"),this.assertNotQueued("join");let r=await this.attachParty(t),i=await this.run("join",()=>this.api.join(e,r,this.version("join")));return this.enter(i,"joined"),this.askForLocalServer(i),i}async joinByCode(e,t={}){this.assertNotInRoom("joinByCode"),this.assertNotQueued("joinByCode");let r=await this.attachParty(t),i=await this.run("joinByCode",()=>this.api.joinByCode(e,r,this.version("joinByCode")));return this.enter(i,"joined"),this.askForLocalServer(i),i}async quickJoin(e){this.assertNotInRoom("quickJoin"),this.assertNotQueued("quickJoin"),Qa(e);let t=this.clusterTargets(),r=await this.attachParty(e),i=s=>this.postQuickJoin(r,s).catch(l=>this.retryPastStaleMembership(l,r,s)),o=await this.run("quickJoin",async()=>{let s=await t;return i(s).catch(async l=>{let d=await this.afterRefusedClusters(l,s);if(d.length===0||zi(d,s))throw l;return i(d)})}),{room:a}=o;return this.playWithAnswer=o.playWith,this.enter(a,"joined"),this.askForLocalServer(a),a}async queueForMatch(e){this.assertNotQueued("queueForMatch"),Ya(e?.templateSlugs),this.queueing=true;try{let t=this.clusterTargets(),r=await this.attachParty(e),i=await this.run("queueForMatch",async()=>{let a=await t;return this.api.createTicket(r,this.version("queueForMatch"),a).catch(async s=>{let l=await this.afterRefusedClusters(s,a);if(l.length===0||zi(l,a))throw s;return this.api.createTicket(r,this.version("queueForMatch"),l)})});this.heldQueued?.ticketId===i.id&&(this.heldQueued=null),this.partyWatch?.end({status:"lost"});let o=new Bt({clock:this.options.clock,first:i,heartbeat:a=>this.track(this.api.heartbeatTicket(a)),cancel:(a,s)=>this.track(this.api.cancelTicket(a,s)),seat:a=>this.enterMatchedRoom(a),onSettled:()=>{this.ticketWatch===o&&(this.ticketWatch=null),this.retuneSeatWatch();}});return this.ticketWatch=o,this.retuneSeatWatch(),o.start(),o.ticket}finally{this.queueing=false;let t=this.heldQueued;this.heldQueued=null,t!==null&&this.showPartyTicket(t);}}async enterMatchedRoom(e){let t=await this.run("queueForMatch",()=>this.api.get(e));return this.disposed?t:(this.room!==null&&this.room.id!==t.id&&(this.queuedFromRoomId=this.room.id,this.exit()),this.room===null&&this.enter(t,"joined"),this.room?.id===t.id?this.room:t)}async list(e={}){let t=await this.run("list",()=>this.api.list(e));return this.emitter.emit("listed",t),t}async myRooms(e){let t=await this.run("myRooms",()=>this.api.myRooms(e));return this.emitter.emit("mineListed",t.rooms),t.rooms}async leave(e={}){let t=this.requireRoom("leave"),r=e.keepalive===true,i=()=>this.run("leave",()=>this.api.leave(t.id,e.asGroup!==false,e.signal,r));if(r){let o=i();this.track(this.notifyLeavingMatch({keepalive:true})),await o;}else await this.notifyLeavingMatch(),await i();this.exit(),this.emitter.emit("left");}async leaveAll(e){let t=await this.myRooms(e),r=[];for(let i of t)try{await this.run("leaveAll",()=>this.api.leave(i.id,!0,e)),r.push(i.id),this.room?.id===i.id&&(this.exit(),this.emitter.emit("left"));}catch{}return r}async close(e){let t=this.requireRoom("close");await this.run("close",()=>this.api.close(t.id,e)),this.exit(),this.emitter.emit("closed");}async refresh(e){let t=this.room;if(!t)return null;let r;try{r=await this.track(this.api.get(t.id,e));}catch(i){if(i instanceof q&&i.reason==="not_found"){let o=this.heldMatchId;if(o!==null){let a=await this.askWhyEnded(o,e);this.endMatch(o,a??"game_room_gone",false);}return this.exit(),this.emitter.emit("closed"),null}throw this.reportError("refresh",i),i}return this.apply(r),await this.settleHeldMatch(e),this.room}async settleHeldMatch(e){let t=this.room,r=this.heldMatchId;if(!(t===null||r===null)&&!(t.status==="in_game"&&t.matchId===r)&&!(t.matchId!==void 0&&t.matchId!==r)&&this.settling!==r){this.settling=r;try{let i=await this.askWhyEnded(r,e);i!==null&&this.endMatch(r,i,t.status!=="closed");}finally{this.settling===r&&(this.settling=null);}}}async askWhyEnded(e,t){let r=this.room;if(r===null)return null;let i=this.takeHint(e),o;try{o=await this.track(this.onceGameIdKnown(()=>hn({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:r.id,matchId:e,version:this.version("reconcile"),knownToExist:this.seenMatches.has(e),...t===void 0?{}:{signal:t}})));}catch(a){return a instanceof q&&a.reason==="not_found"?i??null:(this.reportError("reconcile",a),null)}return o===null?null:o.status==="ready"?(this.seenMatches.add(e),null):o.status==="failed"?(this.settleFailed(o.matchId,o.reason),null):o.reason==="unknown"&&i!==null?i:o.reason}takeHint(e){let t=this.pendingHint;if(t===null||t.matchId!==void 0&&t.matchId!==e||(this.pendingHint=null,t.reason===void 0))return null;let r=Ce(t.reason);return r==="unknown"?null:r}async reconcile(e){if(this.disposed)return;if(e!==void 0&&(e.matchId!==void 0||e.reason!==void 0)&&(this.pendingHint=e),this.reconciling!==null)return this.reconcileAgain=true,this.reconciling;let t=this.reconcileOnce().finally(()=>{this.reconciling=null,this.reconcileAgain&&(this.reconcileAgain=false,this.track(this.reconcile().catch(()=>{})));});return this.reconciling=t,this.track(t)}async reconcileOnce(){if(this.room===null){await this.reconcileSeats();return}await this.refresh().catch(()=>null);}async setReady(e,t){let r=this.requireRoom("setReady"),i=await this.run("setReady",()=>this.api.setReady(r.id,e,t));return this.apply(i),i}async kick(e,t){let r=this.requireRoom("kick"),i=await this.run("kick",()=>this.api.kick(r.id,e,t));return this.apply(i),i}async transferHost(e,t){let r=this.requireRoom("transferHost"),i=await this.run("transferHost",()=>this.api.transferHost(r.id,e,t));return this.apply(i),i}async update(e){let t=this.requireRoom("update"),r=await this.run("update",()=>this.api.update(t.id,e));return this.apply(r),r}startMatch(e={}){return this.requestStart(e,true)}async requestStart(e,t){let r=this.requireRoom("startMatch");if(!this.isHost)throw new u("misconfigured",`only the host of room ${r.id} can start a match, and this player is not it. Every other member waits for \`matchAllocationStarted\` instead \u2014 that is the whole point of the room carrying the match.`);this.startsInFlight++;try{return await this.startIn(r,e,t)}finally{this.startsInFlight--;}}async startIn(e,t,r){let i=H(t.clusterTargetId),o=i??(e.clusterTargetId===void 0?(await this.clusterTargets())[0]:void 0),a=o??e.clusterTargetId;await this.options.ensureLocalServer?.(e.id);let s=(m,c)=>St({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:e.id,...m===void 0?{}:{clusterTargetId:m},...i===void 0?{nextClusterTarget:()=>this.nextCluster(c)}:{},retry:{...t.retry,onWaiting:p=>{this.emitter.emit("matchStartWaiting",p),t.retry?.onWaiting?.(p);}},...t.signal===void 0?{}:{signal:t.signal}}),l=()=>s(o,[a]).catch(async m=>{if(i!==void 0||!er(m))throw m;let c=await this.nextCluster([a],true);if(c===void 0)throw m;return s(c,[a,c])}),d=await(r?this.run("startMatch",l):this.track(this.onceGameIdKnown(l)));return this.beginMatch(d.matchId),d}async awaitMatch(e={}){if(this.session.phase==="suspended")throw new u("misconfigured","this match is suspended \u2014 the transport dropped and nobody has decided to reconnect yet. Call reconnectToMatch(), or dedicatedServer.connect(), which does it for you.");let t=this.emitter.retained;return t!==null&&this.session.phase==="ready"?t:this.pollMatchStatus(e)}async pollMatchStatus(e={}){let t=this.requireRoom("pollMatchStatus"),r=await this.matchIdOrWait(e),i=await this.run("pollMatchStatus",()=>this.chase(t.id,r,e));return this.settleOutcome(i)}chase(e,t,r){return xt({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:e,matchId:t,version:this.version("pollMatchStatus"),knownToExist:this.seenMatches.has(t),...r.timeoutMs===void 0?{}:{timeoutMs:r.timeoutMs},...r.pollIntervalMs===void 0?{}:{pollIntervalMs:r.pollIntervalMs},...r.signal===void 0?{}:{signal:r.signal}})}settleOutcome(e){if(e.status==="failed")throw this.settleFailed(e.matchId,e.reason),new u("allocation_failed",`match ${e.matchId} did not come up: ${e.reason}`);if(e.status==="ended")throw this.endMatch(e.matchId,e.reason,!Vi.has(e.reason)),this.track(this.reconcile().catch(()=>{})),new u("match_ended",`match ${e.matchId} ended: ${e.reason}`);return this.seenMatches.add(e.matchId),this.emitter.retained?.matchId===e.matchId?this.emitter.retain(e):this.emitter.emit("matchReady",e),this.heldMatchId===e.matchId&&this.session.phase!=="connected"&&(this.session={phase:"ready",matchId:e.matchId}),e}settleFailed(e,t){this.heldMatchId===e&&(this.abandonReconnect(e,t),this.emitter.emit("matchFailed",e,t),this.abortFollow(),this.refusedMatches.add(e),this.session={phase:"none"},this.emitter.clearRetained());}endMatch(e,t,r){this.heldMatchId===e&&(this.abortFollow(),this.session={phase:"ended",matchId:e,reason:t,roomReopened:r},this.emitter.clearRetained(),this.emitter.emit("matchEnded",{matchId:e,reason:t,roomReopened:r}),this.abandonReconnect(e,t));}armReconnect(e,t){let r=this.options.autoReconnect;if(r===void 0||r.attempts<=0||r.budgetMs<=0)return;if(this.finishReconnect(),t==="kicked"){r.abandon(e,t);return}let i={matchId:e,cause:t,attempt:0,deadlineAt:this.options.clock.now()+r.budgetMs,parkedAt:null,timer:null,parked:null,done:false};this.reconnect=i,this.track(this.reconnectStep(i));}park(e,t){e.parkedAt=this.options.clock.now(),e.parked=()=>(e.parkedAt!==null&&(e.deadlineAt+=this.options.clock.now()-e.parkedAt,e.parkedAt=null),t());}reconnecting(e){return !this.disposed&&!e.done&&this.reconnect===e&&this.session.phase==="suspended"&&this.session.matchId===e.matchId}finishReconnect(){let e=this.reconnect;e!==null&&(e.done=true,e.timer!==null&&(this.options.clock.clearTimeout(e.timer),e.timer=null),e.parked=null,this.reconnect=null);}abandonReconnect(e,t){let r=this.reconnect;r===null||r.matchId!==e||(this.finishReconnect(),this.options.autoReconnect?.abandon(e,t));}async reconnectStep(e){if(!this.reconnecting(e))return;if(!zt()){this.park(e,()=>this.reconnectStep(e));return}let t=this.options.autoReconnect;if(t===void 0||e.attempt===0&&(await this.reconcile().catch(()=>{}),!this.reconnecting(e)))return;let r=e.deadlineAt-this.options.clock.now();if(e.attempt>=t.attempts||r<Ua){this.abandonReconnect(e.matchId,e.cause);return}e.attempt+=1,this.emitter.emit("matchReconnecting",{matchId:e.matchId,attempt:e.attempt,attempts:t.attempts,remainingMs:r}),e.timer=this.options.clock.setTimeout(()=>{e.timer=null,this.track(this.reconnectAttempt(e));},Math.min(ja(e.attempt,t.maxDelayMs),r));}async reconnectAttempt(e){if(!this.reconnecting(e))return;if(!zt()){this.park(e,()=>this.reconnectAttempt(e));return}let t=this.options.autoReconnect,r=this.room;if(t===void 0||r===null)return;let i=null;try{i=await this.track(this.onceGameIdKnown(()=>hn({client:this.options.client,clock:this.options.clock,gameId:this.requireGameId(),roomId:r.id,matchId:e.matchId,version:this.version("reconcile"),knownToExist:this.seenMatches.has(e.matchId)})));}catch(o){this.reportError("reconcile",o);}if(this.reconnecting(e)){if(i?.status==="ended"){this.endMatch(i.matchId,i.reason,!Vi.has(i.reason)),this.track(this.reconcile().catch(()=>{}));return}if(i?.status==="failed"){this.abandonReconnect(e.matchId,i.reason),this.settleFailed(i.matchId,i.reason);return}if(i?.status==="ready"){this.seenMatches.add(e.matchId),this.session={phase:"ready",matchId:e.matchId},this.emitter.retain(i);let o;try{o=await t.redial(e.matchId,e.deadlineAt);}catch(a){this.reportError("reconcile",a),o="failed";}if(e.done||this.reconnect!==e)return;if(o==="closed"){this.finishReconnect();return}if(o==="connected")return;this.session.phase==="ready"&&this.session.matchId===e.matchId&&(this.session={phase:"suspended",matchId:e.matchId,reason:e.cause});}await this.reconnectStep(e);}}notifyMatchConnected(e){if(this.heldMatchId!==e||this.session.phase!=="ready"&&this.session.phase!=="suspended")return;let t=this.session.phase==="suspended";this.session={phase:"connected",matchId:e};let r=this.reconnect;r!==null&&r.matchId===e?(this.finishReconnect(),this.emitter.emit("matchReconnected",{matchId:e,attempt:r.attempt})):t&&this.emitter.emit("matchReconnected",{matchId:e,attempt:0});}async notifyMatchDisconnected(e="link_closed"){if(this.session.phase!=="ready"&&this.session.phase!=="connected")throw new u("misconfigured",`no match is connected, so there is nothing to suspend (state '${this.matchConnectionState}').`);let t=this.session.matchId;this.session={phase:"suspended",matchId:t,reason:e},this.emitter.emit("matchSuspended",{matchId:t,cause:e}),this.armReconnect(t,e);}async reconnectToMatch(e={}){if(this.session.phase!=="suspended")throw new u("misconfigured",`reconnectToMatch expects a suspended match; this one is '${this.matchConnectionState}'. Call notifyMatchDisconnected() when the transport drops.`);let t=this.heldMatchId;if(t===null)throw new u("misconfigured","there is no match to reconnect to.");return this.finishReconnect(),this.session={phase:"starting",matchId:t},this.emitter.clearRetained(),this.pollMatchStatus(e)}async notifyLeavingMatch(e={}){let t=this.heldMatchId;if(t!==null)try{await this.api.leaveMatch(t,e.keepalive??!1);}catch{}}get gameId(){let e=typeof this.options.gameId=="function"?this.options.gameId():this.options.gameId;return e===""?null:e}get currentRoom(){return this.room}get currentTicket(){return this.ticketWatch?.ticket??null}get partyTicket(){return this.partyWatch?.ticket??null}get currentMatch(){return this.emitter.retained}get matchConnectionState(){switch(this.session.phase){case "none":case "ended":return "disconnected";case "ready":case "connected":return "connected";case "suspended":return "suspended";default:return "connecting"}}get matchSession(){return this.session}holdsMatch(){return this.session.phase!=="none"&&this.session.phase!=="ended"}get heldMatchId(){return this.holdsMatch()?this.session.matchId??null:null}get isInRoom(){return this.room!==null}get isHost(){let e=this.playerId();return e!==null&&this.room?.hostPlayerId===e}get members(){return this.room?.members??[]}get playWith(){return this.playWithAnswer}on(e,t){let r=this.emitter.on(e,t);return e==="seated"&&(this.retuneSeatWatch(),this.room===null&&this.track(this.reconcileSeats().catch(()=>{}))),r}off(e,t){this.emitter.off(e,t);}once(e,t){return this.emitter.once(e,t)}handleRoomEvent(e){if(this.ticketWatch!==null&&e.event!=="version_stale"&&e.roomId!==this.room?.id&&this.ticketWatch.poke(),this.room===null)return;if(e.event==="version_stale"){this.emitter.emit("versionUpdateAvailable",e.platform);return}if(e.roomId!==this.room.id||(this.lastPushAt=this.options.clock.now(),this.retune(),e.event==="updated"&&e.version!==void 0&&e.version<=this.room.version))return;let t=e.event==="updated"&&e.reason!==void 0?{...e.matchId===void 0?{}:{matchId:e.matchId},reason:e.reason}:void 0;this.track(this.reconcile(t).catch(()=>{}));}handleAutoStartFailed(e,t){if(this.disposed||this.room?.id!==e)return;let r=this.answerAutoStart(t);if(this.emitter.emit("matchAutoStartFailed",{roomId:e,code:t,handled:r!=="leave"}),r!=="start"||this.startsInFlight>0||this.room?.id!==e)return;this.autoStartRetried=true;let i=new AbortController;this.autoStartRetry=i,this.track(this.requestStart({signal:i.signal},false).then(()=>{},o=>{i.signal.aborted||this.reportError("startMatch",o);}).finally(()=>{this.autoStartRetry===i&&(this.autoStartRetry=null);}));}answerAutoStart(e){return this.options.retryAutoStart===false||!this.isHost||!Ja.has(e)?"leave":this.startsInFlight>0?"in_flight":this.autoStartRetried?"leave":"start"}abortAutoStartRetry(){this.autoStartRetry?.abort(),this.autoStartRetry=null;}handleSeated(e){if(!this.disposed){if(this.ticketWatch!==null){this.ticketWatch.poke();return}this.watchingSeats&&this.room?.id!==e&&(this.reported.has(e)||this.track(this.reportSeat(e).catch(()=>{})));}}handleTicketEvent(e){if(!this.disposed){if(this.ticketWatch!==null){this.ticketWatch.poke();return}if(this.queueing){e.event==="queued"?this.heldQueued=e:this.heldQueued?.ticketId===e.ticketId&&(this.heldQueued=null);return}if(e.event==="queued"){this.showPartyTicket(e);return}if(e.event==="matched"){e.roomId!==void 0&&this.handleSeated(e.roomId),this.partyWatch?.id===e.ticketId&&this.partyWatch.end({status:"matched"});return}this.partyWatch?.id===e.ticketId&&this.partyWatch.end({status:e.event,reason:e.reason??e.event});}}showPartyTicket(e){if(this.disposed||this.partyWatch?.id===e.ticketId)return;this.partyWatch?.end({status:"lost"});let t=new Jt({clock:this.options.clock,first:e,onSettled:()=>{this.partyWatch===t&&(this.partyWatch=null);}});this.partyWatch=t,this.emitter.emit("partyQueued",t.ticket);}async reconcileSeats(){if(this.disposed||!this.watchingSeats||this.ticketWatch!==null)return;let e;try{e=await this.myRooms();}catch{return}let t=new Set;for(let r of e)r.status!=="closed"&&(t.add(r.id),r.id!==this.room?.id&&(this.reported.has(r.id)||await this.reportSeat(r.id).catch(()=>{})));for(let r of [...this.reported])t.has(r)||this.reported.delete(r);}async reportSeat(e){let t=this.playerId(),r=await this.run("handleSeated",()=>this.api.get(e));this.disposed||t===null||!r.members.some(i=>i.playerId===t)||this.room?.id!==r.id&&(this.reported.has(r.id)||(this.reported.add(r.id),this.partyWatch?.end({status:"matched"}),this.emitter.emit("seated",r)));}retuneSeatWatch(){if(!(!this.disposed&&this.watchingSeats&&this.room===null&&this.ticketWatch===null&&this.seatPollMs>0&&zt())){this.seatPollHandle!==null&&(this.options.clock.clearTimeout(this.seatPollHandle),this.seatPollHandle=null);return}if(this.seatPollHandle!==null)return;let t=()=>{this.seatPollHandle=null,this.track(this.reconcileSeats().catch(()=>{}).then(()=>this.retuneSeatWatch()));};this.seatPollHandle=this.options.clock.setTimeout(t,this.seatPollMs);}installResumeWatch(){let e=globalThis;if(typeof e.addEventListener!="function")return;this.visibilityListener=()=>{if(!this.disposed&&(this.retuneSeatWatch(),!!zt())){if(this.room!==null){let r=this.reconnect?.parked??null;r!==null&&this.reconnect!==null?(this.reconnect.parked=null,this.track(r())):this.track(this.reconcile().catch(()=>{}));return}if(this.ticketWatch!==null){this.ticketWatch.poke();return}this.options.watchSeats!==false&&this.track(this.reconcileSeats().catch(()=>{}));}};for(let r of Ki)e.addEventListener(r,this.visibilityListener);let t=globalThis.document;typeof t?.addEventListener=="function"&&t.addEventListener("resume",this.visibilityListener);}startPolling(e){this.stopPolling(),!this.disposed&&(this.pinnedIntervalMs=e??null,this.polling=true,this.schedule());}stopPolling(){this.pollHandle!==null&&(this.options.clock.clearTimeout(this.pollHandle),this.pollHandle=null),this.polling=false,this.pollIntervalMs=null;}async settled(){for(let e=0;e<20&&this.inFlight.length>0;e++){let t=this.inFlight;this.inFlight=[],await Promise.allSettled(t);}}dispose(){if(this.disposed=true,this.ticketWatch?.abandon(),this.heldQueued=null,this.partyWatch?.end({status:"lost"}),this.abortAutoStartRetry(),this.finishReconnect(),this.stopPolling(),this.emitter.clear(),this.unloadListener&&(globalThis.removeEventListener?.("pagehide",this.unloadListener),this.unloadListener=null),this.seatPollHandle!==null&&(this.options.clock.clearTimeout(this.seatPollHandle),this.seatPollHandle=null),this.visibilityListener){let e=globalThis;for(let r of Ki)e.removeEventListener?.(r,this.visibilityListener);globalThis.document?.removeEventListener?.("resume",this.visibilityListener),this.visibilityListener=null;}}get watchingSeats(){return this.options.watchSeats!==false&&this.emitter.hasListeners("seated")}get seatPollMs(){return this.options.seatPollIntervalMs??Ka}requireGameId(){let e=this.gameId;if(e!==null)return e;throw new u("misconfigured","the game id is not known yet. It comes from the play token, and this manager was given no way to wait for one \u2014 pass gameId, or awaitGameId so a call made before the first token waits for it the way createGem wires it.")}playerId(){return typeof this.options.playerId=="function"?this.options.playerId():this.options.playerId}version(e){let t=this.options.clientVersion,r=Di(this.options.platformGameVersion?.(),t?.gameVersion);if(r===null)throw new u("misconfigured",`${e} has no game version to report \u2014 gem-api requires platform, game_version and engine_version together on every room route, and refuses a request carrying two of the three. The arcade normally supplies it with the play token; an embedder that does not, or a build running outside the arcade, leaves nobody to ask. Pass \`clientVersion: { gameVersion }\` to createGem (or to createRoomManager) as the fallback. It has to name a build you actually published, and a wrong one pins the room to the wrong build \u2014 which is why the platform answers it wherever it can.`);return ci({...t,gameVersion:r},e)}async clusterTargets(){try{if(this.options.resolveClusterTargets!==void 0)return V(await this.options.resolveClusterTargets());let e=H(await this.options.resolveClusterTarget?.());return e===void 0?[]:[e]}catch{return []}}async afterRefusedClusters(e,t){return !er(e)||t.length===0?[]:V(await this.options.refreshClusterTargets?.(t)??[])}async nextCluster(e,t=false){let r=a=>a.find(s=>!e.includes(s)),i=await this.clusterTargets(),o=r(i);return o!==void 0||!t?o:r(V(await this.options.refreshClusterTargets?.(i)??[]))}async attachParty(e){if(e.partyId!==void 0)return e;let t=this.options.resolveParty;if(t===void 0)return e;let r=await t();return r===void 0?e:{...e,partyId:r}}async retryPastStaleMembership(e,t,r){if(!(e instanceof q)||e.reason!=="conflict"||!za.has(e.code??""))throw e;if(this.options.recoverStaleMembership===false)throw new q(e.reason,e.status,`${e.detail} \u2014 quick-join is refused while this player already holds a game room, and \`recoverStaleMembership\` is off, so nothing cleared it. A membership that outlived its page is visible only through myRooms(); leaveAll() is what clears it.`,e.retryAfterSeconds,e.code);let i;try{i=await this.leaveAll(t.signal);}catch{throw new q(e.reason,e.status,`${e.detail} \u2014 quick-join is refused while this player already holds a game room, and the recovery could not list the rooms it would have cleared, so nothing was tried and the refusal stands. That is the listing failing, not a room refusing to be left: retry once the connection is back. myRooms() shows what is held.`,e.retryAfterSeconds,e.code)}if(i.length===0)throw new q(e.reason,e.status,`${e.detail} \u2014 quick-join is refused while this player already holds a game room, and the recovery cleared nothing, so the refusal stands. myRooms() shows what is held. A room this player was seated in as a party member cannot be cleared from here at all \u2014 only the leader of a party may take it out of a room \u2014 so a member joins by adopting that room, myRooms() then join(), rather than quick-joining.`,e.retryAfterSeconds,e.code);return this.postQuickJoin(t,r)}postQuickJoin(e,t){return Zn(this.apiOptions,e,this.version("quickJoin"),t,this.internals.playWithTicket)}assertNotInRoom(e){if(this.room!==null)throw new u("misconfigured",`${e} refused: already in room ${this.room.id}. Leave it first \u2014 this manager tracks one room, and a second would leave its state disagreeing with the server's about which one you are in.`)}assertNotQueued(e){if(!(this.ticketWatch===null&&!this.queueing))throw new u("misconfigured",`${e} refused: this player is waiting on a matchmaking ticket, which will seat them in a room of its own. Cancel it first \u2014 currentTicket.cancel() \u2014 or wait for its outcome.`)}requireRoom(e){if(this.room===null)throw new u("misconfigured",`${e} refused: not in a room.`);return this.room}async run(e,t){try{return await this.track(this.onceGameIdKnown(t))}catch(r){throw this.reportError(e,r),r}}onceGameIdKnown(e){return this.gameId!==null||this.options.awaitGameId===void 0?e():this.options.awaitGameId().then(e)}reportError(e,t){let r=t instanceof q?{operation:e,code:t.status,reason:t.reason,message:t.message,retryable:t.retryable}:{operation:e,code:0,reason:t instanceof u?t.reason:"unavailable",message:t instanceof Error?t.message:String(t),retryable:false};this.emitter.emit("error",r);}askForLocalServer(e){let t=this.options.ensureLocalServer;t===void 0||!e.selfHosted||e.status!=="open"||t(e.id).catch(()=>{});}track(e){return this.inFlight.push(e),e}enter(e,t){this.room=e,this.queuedFromRoomId===e.id&&(this.queuedFromRoomId=null),this.abortFollow(),this.autoStartRetried=false,this.session={phase:"none"},this.seenMatches.clear(),this.refusedMatches.clear(),this.pendingHint=null,this.emitter.clearRetained(),this.emitter.emit(t,e),this.adoptMatch(e),this.options.poll!==false&&this.startPolling(),this.retuneSeatWatch();}exit(){this.room=null,this.abortFollow(),this.abortAutoStartRetry(),this.autoStartRetried=false,this.finishReconnect(),this.session.phase!=="ended"&&(this.session={phase:"none"}),this.seenMatches.clear(),this.refusedMatches.clear(),this.pendingHint=null,this.emitter.clearRetained(),this.stopPolling(),this.retuneSeatWatch();}apply(e){let t=this.room;if(t===null||t.id!==e.id)return;let r=new Map(t.members.map(s=>[s.playerId,s])),i=new Map(e.members.map(s=>[s.playerId,s])),o=this.playerId();for(let s of i.keys())r.has(s)||this.emitter.emit("memberJoined",s);for(let s of r.keys())if(!i.has(s)){if(s===o){this.exit(),this.emitter.emit("kicked");return}this.emitter.emit("memberLeft",s);}for(let[s,l]of i){let d=r.get(s);d&&d.isReady!==l.isReady&&this.emitter.emit("readyChanged",s,l.isReady);}t.hostPlayerId!==e.hostPlayerId&&e.hostPlayerId.length>0&&this.emitter.emit("hostChanged",e.hostPlayerId);let a=Xa(t,e);this.room=e,this.adoptMatch(e),this.session.phase==="starting"&&this.followMatch(),a&&this.emitter.emit("updated",e);}adoptMatch(e){e.status!=="in_game"||e.matchId===void 0||this.session.matchId!==e.matchId&&(this.refusedMatches.has(e.matchId)||this.beginMatch(e.matchId));}beginMatch(e){let t=this.heldMatchId;t!==null&&t!==e&&this.endMatch(t,"replaced",true),this.session={phase:"starting",matchId:e},this.autoStartRetried=false,this.emitter.clearRetained(),this.emitter.emit("matchAllocationStarted",e),this.followMatch();}followMatch(){if(this.autoFollow===false)return;let e=this.room,t=this.heldMatchId;if(e===null||t===null||this.followAbort!==null&&this.followingMatchId===t)return;this.abortFollow();let r=new AbortController;this.followAbort=r,this.followingMatchId=t,this.track(this.onceGameIdKnown(()=>this.chase(e.id,t,{signal:r.signal})).then(i=>{if(!r.signal.aborted)try{this.settleOutcome(i);}catch{}}).catch(i=>{r.signal.aborted||this.reportError("pollMatchStatus",i);}).finally(()=>{this.followAbort===r&&(this.followAbort=null,this.followingMatchId=null);}));}abortFollow(){this.followAbort?.abort(),this.followAbort=null,this.followingMatchId=null;}matchIdOrWait(e){let t=this.heldMatchId;if(t!==null)return Promise.resolve(t);let r=e.timeoutMs??Va;return new Promise((i,o)=>{let a=c=>{s(),l(),this.options.clock.clearTimeout(m),e.signal?.removeEventListener("abort",d),c();},s=this.emitter.on("matchAllocationStarted",c=>a(()=>i(c))),l=this.emitter.on("matchFailed",(c,p)=>a(()=>o(new u("allocation_failed",`match ${c} did not come up: ${p}`)))),d=()=>a(()=>o(new u("aborted","the caller aborted while awaiting a match"))),m=this.options.clock.setTimeout(()=>a(()=>o(new u("timeout",`no match was allocated for room ${this.room?.id??"(none)"} within ${r}ms`))),r);e.signal?.aborted?d():e.signal?.addEventListener("abort",d);})}baseInterval(){return this.pushed()?this.pushedInterval():this.options.pollIntervalMs??Wa}pushedInterval(){return this.options.pushedPollIntervalMs??Ha}pushed(){return this.options.clock.now()-this.lastPushAt<Ba*this.pushedInterval()}retune(){!this.polling||this.pinnedIntervalMs!==null||this.baseInterval()!==this.pollIntervalMs&&this.startPolling();}schedule(){this.disposed||!this.polling||this.room===null||(this.pollIntervalMs=this.pinnedIntervalMs??this.baseInterval(),this.pollHandle=this.options.clock.setTimeout(()=>{this.pollHandle=null,this.track(this.reconcile().catch(()=>null).then(()=>{this.schedule();}));},this.pollIntervalMs));}installUnloadNotifier(){if(this.options.notifyOnUnload===false)return;let e=globalThis;typeof e.addEventListener=="function"&&(this.unloadListener=t=>{if(this.ticketWatch?.abandon(true),t?.persisted!==true&&(this.room!==null&&this.track(this.leave({keepalive:true}).catch(()=>{})),this.queuedFromRoomId!==null)){let r=this.queuedFromRoomId;this.queuedFromRoomId=null,this.track(this.api.leave(r,true,void 0,true).catch(()=>{}));}},e.addEventListener("pagehide",this.unloadListener));}};function zt(){let n=globalThis.document;return n?.visibilityState===void 0||n.visibilityState==="visible"}var Ki=["visibilitychange","focus","pageshow","online"];function Xa(n,e){return n.version!==e.version||n.status!==e.status||n.matchId!==e.matchId||n.hostPlayerId!==e.hostPlayerId||n.name!==e.name||n.maxPlayers!==e.maxPlayers||n.isPublic!==e.isPublic||Ji(n)!==Ji(e)}function Ji(n){return n.members.map(e=>`${e.playerId}:${e.isReady?1:0}:${e.role}`).sort().join(",")}function er(n){return n instanceof q&&n.code==="invalid_cluster_target"}var Xt=8;function Ya(n){if(!(Array.isArray(n)&&n.length>=1&&n.length<=Xt&&n.every(t=>typeof t=="string"&&t.length>0)))throw new u("misconfigured",`queueForMatch needs templateSlugs: an array naming one to ${Xt} room templates by slug, each with matchmaking turned on in your game config \u2014 queueForMatch({ templateSlugs: ['duel'] }).`)}function Qa(n){let e=n?.templateSlugs;if(e===void 0)return;if(!(Array.isArray(e)&&e.length>=1&&e.length<=Xt&&e.every(r=>typeof r=="string"&&r.length>0)))throw new u("misconfigured",`quickJoin's templateSlugs is an array naming one to ${Xt} room templates by slug \u2014 quickJoin({ templateSlugs: ['duel', 'brawl'] }). A room that has to be created is made from the first.`);if(n?.templateSlug!==void 0||n?.templateId!==void 0)throw new u("misconfigured","quickJoin takes templateSlugs or a single templateSlug / templateId, not both. Put every template this player accepts in templateSlugs, with the one to create from first.")}function zi(n,e){return n.length===e.length&&n.every((t,r)=>t===e[r])}function Yi(n){let e=0,t=new Map;return {resolve(){if(!n.bridge.parentSupports("parties"))return Promise.resolve(void 0);let r=`party-${String(++e)}`;return new Promise(i=>{let o={},a=l=>{t.delete(r)&&(n.clock.clearTimeout(o.timer),i(l));};t.set(r,a),o.timer=n.clock.setTimeout(()=>a(void 0),n.timeoutMs??2e3);let s=false;try{s=n.bridge.requestParty(r);}catch{}s||a(void 0);})},settle(r,i){t.get(r)?.(i);}}}
|
|
3
|
+
export{po as BUILT_IN_GRAPHICS_ROUTES,en as CAPABILITY_GATES,eo as CAPABILITY_NAMESPACE_GATES,Ut as CHANNELS,ot as CHANNEL_KINDS,ve as ClientTelemetry,on as DEDICATED_INBOUND_EVENTS,Rn as DEFAULT_WELCOME_CEILING_MS,es as DEVICE_SETTINGS_MAX_BYTES,u as DedicatedServerError,It as DedicatedSignalingHub,tl as FRAME_TO_PARENT,un as GemFrame,cn as GemHost,Zt as HANDSHAKE_TIMEOUT_MS,Ge as HostMigration,xe as MALFORMED_INBOUND_KEY,or as MAX_ARRAY_LENGTH,ft as MAX_CLUSTER_TARGETS,Zi as MAX_MESSAGE_BYTES,ir as MAX_MESSAGE_DEPTH,gt as MAX_MESSAGE_NODES,yt as MAX_SIGNALS_PER_SECOND,He as MAX_SOCKET_FRAME_BYTES,Ai as MIGRATION_TIMEOUT_MS,Oe as P2pApi,w as P2pError,Le as P2pSession,el as PARENT_TO_FRAME,g as PROTOCOL,B as PlayerText,ge as RATE_LIMITS,oe as RateLimiter,Ne as RoomEmitter,Fe as RoomsApi,ne as SUPPORTED_VERSIONS,De as SignalingClient,kt as TELEMETRY_EVENTS,Lr as TRANSPORT_LABELS,de as VALIDATORS,Xe as WEBRTC_DEDICATED,zn as WEBRTC_P2P,nt as WEB_ENGINE_VERSION,Se as WEB_PROTOCOL_VERSION,be as asPlayerText,xt as awaitMatchOutcome,Wt as browserPeerConnection,vt as buildDedicatedOfferFrame,Rt as buildSocketFrame,he as capabilityFor,co as createClusterTargetSource,Ks as createDedicatedServerApi,Nt as createKeySource,Ia as createP2pApi,Yi as createPartyResolver,Xi as createRoomManager,ts as deviceSettingsByteLength,tt as establishDataChannel,ke as fingerprintOf,we as fingerprintsMatch,Ht as generateCertificate,ht as isOrigin,At as listClusterTargets,Qn as memberFromWire,sn as negotiateVersion,H as normaliseClusterTarget,V as normaliseClusterTargets,rt as normaliseFingerprint,ye as originsEqual,Ye as presentJoinCredential,_t as projectDedicatedEvent,Ke as projectPartyEvent,wt as projectSignal,Vt as roomArgValuesFromWire,Kt as roomArgsTemplateFromWire,Y as roomFromWire,St as startMatch,ut as summaryFromWire,ce as systemClock,Vo as text,Tt as unavailableSignaling,hd as validateGemAdResult,Ad as validateGemAppLinksResult,Hl as validateGemAudioState,bl as validateGemAutoStartFailedEvent,Sl as validateGemClusterTarget,dl as validateGemCohorts,Jl as validateGemControlsDeclare,wl as validateGemDedicatedEvent,Ml as validateGemDedicatedOffer,ml as validateGemDeviceSettings,pd as validateGemDeviceSettingsPut,md as validateGemDeviceSettingsResult,Al as validateGemDevtools,cl as validateGemEarnAllowanceChanged,Ll as validateGemEngaged,ad as validateGemError,jl as validateGemExit,od as validateGemFrameStats,Rd as validateGemFriendChallengeResult,El as validateGemGameContext,Yl as validateGemGemCorner,xl as validateGemGraphicsRoutes,nl as validateGemHandshake,rl as validateGemHandshakeAccept,ul as validateGemInventoryChanged,Sd as validateGemInviteParams,Fl as validateGemLocalServer,Nl as validateGemLocalServerDenied,zl as validateGemMenu,Xl as validateGemMenuSelect,Ql as validateGemMenuStyle,yd as validateGemMomentResult,Id as validateGemOpenAppResult,Ul as validateGemOpenExternal,Ed as validateGemOpenGameResult,Ol as validateGemPartyRequest,kl as validateGemPartyResult,al as validateGemPause,kd as validateGemPlayInviteResult,sl as validateGemPlayer,sd as validateGemPortraitBorders,ll as validateGemPrefs,Dl as validateGemProgress,cd as validateGemPurchaseResult,ud as validateGemPurchaseSettled,Gl as validateGemReady,Rl as validateGemRelayDraining,gd as validateGemRequestAd,xd as validateGemRequestAppLinks,dd as validateGemRequestAvatar3dPurchase,rd as validateGemRequestAvatarPicker,Tl as validateGemRequestContext,_d as validateGemRequestFriendChallenge,Wl as validateGemRequestFullscreen,$l as validateGemRequestLocalServer,Pd as validateGemRequestOpenApp,Td as validateGemRequestOpenGame,wd as validateGemRequestPlayInvite,ld as validateGemRequestPurchase,td as validateGemRequestShare,bd as validateGemRequestSignIn,Cd as validateGemRequestSimilarGames,$d as validateGemRequestStoreOfferSheet,Od as validateGemRequestStoreOffers,Ld as validateGemRequestStorePurchase,ql as validateGemRequestToken,Bl as validateGemRewardCue,Vl as validateGemRewardPresentation,hl as validateGemRoomEvent,Pl as validateGemScreenshot,Il as validateGemScreenshotDenied,yl as validateGemSeatedEvent,ed as validateGemSettingChange,Zl as validateGemSettingsDeclare,nd as validateGemShareResult,vd as validateGemSignInResult,Cl as validateGemSignal,_l as validateGemSignalEvent,fd as validateGemSignalMoment,Md as validateGemSimilarGamesResult,fl as validateGemSocketState,Fd as validateGemStoreOfferSheetResult,Gd as validateGemStoreOffersResult,Dd as validateGemStorePurchaseResult,qd as validateGemStorePurchaseSettled,vl as validateGemTicketEvent,il as validateGemToken,ol as validateGemTokenDenied,id as validateGemVibrate,pl as validateGemViewport,Kl as validateGemWalletDisplay,gl as validateGemWillTerminate,oo as validateSize,jt as verifyCredential};//# sourceMappingURL=gem-web-sdk.esm.js.map
|
|
4
4
|
//# sourceMappingURL=gem-web-sdk.esm.js.map
|