@indiegems/gem-web-sdk 18.0.0 → 18.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,20 @@ Its entries are grouped under up to five headings:
16
16
  - **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
17
17
  - **New capability**: something a game can now do. Nothing changes unless you use it.
18
18
 
19
+ ## 18.1.1
20
+
21
+ Upgrading from 18.1.0. Impact: none.
22
+
23
+ Nothing in this release asks anything of a game.
24
+
25
+ ## 18.1.0
26
+
27
+ Upgrading from 18.0.0. Impact: recommended.
28
+
29
+ ### New capability
30
+
31
+ - A number sent for an analytics dimension that your game config declares as a range is now delivered as that dimension, written as its decimal. Previously such events went out as a measure and were refused as missing a dimension. If your game dropped, skipped or worked around these events, you can send the number directly. Numbers on other fields remain measures, and events your config does not declare are sent as before. The analytics options also gain a declarations field, which the SDK fills in from your game config, so you normally do not set it. Code that runs on a server or in a function should still send a range as its decimal string.
32
+
19
33
  ## 18.0.0
20
34
 
21
35
  Upgrading from 17.0.0. Impact: required.
@@ -130,6 +130,10 @@ interface AnalyticsApiOptions {
130
130
  readonly gameId: () => string | null;
131
131
  readonly playerId: () => string | null;
132
132
  readonly awaitPlayerId?: () => Promise<string>;
133
+ readonly declarations?: () => Promise<readonly {
134
+ readonly id: string;
135
+ readonly dimensions?: Readonly<Record<string, unknown>> | null;
136
+ }[] | null | undefined>;
133
137
  readonly options?: AnalyticsOptions;
134
138
  readonly log?: ((message: string) => void) | null;
135
139
  }
package/dist/game.d.ts CHANGED
@@ -1516,6 +1516,10 @@ interface AnalyticsApiOptions {
1516
1516
  readonly gameId: () => string | null;
1517
1517
  readonly playerId: () => string | null;
1518
1518
  readonly awaitPlayerId?: () => Promise<string>;
1519
+ readonly declarations?: () => Promise<readonly {
1520
+ readonly id: string;
1521
+ readonly dimensions?: Readonly<Record<string, unknown>> | null;
1522
+ }[] | null | undefined>;
1519
1523
  readonly options?: AnalyticsOptions;
1520
1524
  readonly log?: ((message: string) => void) | null;
1521
1525
  }
@@ -1,4 +1,4 @@
1
- var at=Object.defineProperty;var o=(r,t)=>at(r,"name",{value:t,configurable:true});var c="gem-web-bridge",R={min:1,max:1},st=65536,Te=8,te=5e3,Ie=1024,P=1e4,M=Object.create(null),ne="__malformed";var ye=Object.create(null),lt=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"]]);function re(r){let t=ye[r];if(t!==void 0)return t;for(let[e,n]of lt)if(r.startsWith(e))return n}o(re,"capabilityFor");function H(r){let t=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(r);return t===null?false:t[4]===void 0||Number(t[4])<=65535}o(H,"isOrigin");function dt(r){let t;try{t=new URL(r);}catch{return false}return t.username!==""||t.password!==""?false:t.protocol==="https:"?true:t.protocol==="http:"&&(t.hostname==="localhost"||t.hostname==="127.0.0.1")}o(dt,"isSafeHttpUrl");function ct(r){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(r)}o(ct,"isBcp47");function ut(r){return /^[a-z_]+$/.test(r)}o(ut,"isCode");function mt(r){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(r)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(r)&&!r.includes("--")}o(mt,"isGameRef");function ge(r,t,e){if(t>Te)return `nesting deeper than ${Te}`;if(++e.nodes>te)return `more than ${te} values`;if(e.bytes>e.cap)return `over the ${e.cap} byte limit`;if(r==null)return e.bytes+=4,null;let n=typeof r;if(n==="boolean")return e.bytes+=5,null;if(n==="number")return e.bytes+=8,null;if(n==="string")return e.bytes+=r.length*3+2,null;if(n!=="object")return `${n} is not plain data`;let i=r;if(e.seen.has(i))return "a repeated or cyclic object reference";if(e.seen.add(i),Array.isArray(r)){if(r.length>Ie)return `an array of ${r.length} entries, over the ${Ie} limit`;e.bytes+=2;for(let a of r){let u=ge(a,t+1,e);if(u)return u}return null}let s=Object.getPrototypeOf(r);if(s!==Object.prototype&&s!==null)return "a value that is not a plain object";e.bytes+=2;let d=r;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++e.nodes>te)return `more than ${te} values`;if(e.bytes+=a.length*3+4,e.bytes>e.cap)return `over the ${e.cap} byte limit`;let u=ge(d[a],t+1,e);if(u)return u}return null}o(ge,"walk");function pt(r,t=st){let e={nodes:0,bytes:0,seen:new Set,cap:t},n=ge(r,0,e);return n?`message contains ${n}`:e.bytes>t?`message is over the ${t} byte limit`:null}o(pt,"validateSize");var gt={origin:[H,"not a well-formed origin"],url:[dt,"not a safe absolute http(s) URL"],bcp47:[ct,"not a well-formed BCP 47 tag"],code:[ut,"not a code of lower-case letters and underscores"],gameRef:[mt,"not a game id or web address"]};function Ce(r,t,e){for(let n of t){let i=e===""?n[0]:`${e}.${n[0]}`,s=r[n[0]];if(s==null){if(n[1]<"a")continue;return `${i}: required`}let d=fe(s,n,i);if(d)return d}return null}o(Ce,"checkFields");function fe(r,t,e){let n=t[1].toLowerCase();switch(n){case "u":return typeof r=="boolean"?null:fe(r,typeof r=="string"?[t[0],"s",t[2]]:[t[0],"n",t[3],t[4]],e);case "s":{if(typeof r!="string")return `${e}: expected string`;if(r.length>t[2])return `${e}: exceeds ${t[2]} characters`;if(t[3]){let[i,s]=gt[t[3]];if(!i(r))return `${e}: ${s}`}return null}case "n":case "i":return typeof r!="number"||!(n=="i"?Number.isInteger:Number.isFinite)(r)?`${e}: expected ${n=="i"?"integer":"number"}`:r<t[2]||r>t[3]?`${e}: out of range ${t[2]}..${t[3]}`:null;case "b":return typeof r=="boolean"?null:`${e}: expected boolean`;case "e":{let i=t[2];return i.includes(r)?null:`${e}: not one of ${i.join("|")}`}case "o":return typeof r!="object"||r===null||Array.isArray(r)?`${e}: expected object`:Ce(r,t[2],e);case "j":return typeof r!="object"||r===null||Array.isArray(r)?`${e}: expected a plain object`:null;case "a":{if(!Array.isArray(r))return `${e}: expected array`;if(r.length>t[2])return `${e}: exceeds ${t[2]} items`;let i=t[3],s=`${e}[]`;for(let d of r){if(d==null){if(i[1]<"a")continue;return `${s}: required`}let a=fe(d,i,s);if(a)return a}return null}default:return `${e}: unknown kind ${String(n)}`}}o(fe,"checkValue");var Oe=Object.create(null);function l(r,t,e,n,i){return M[r]=e,n&&(ye[r]=n),Oe[r]=s=>{let d=pt(s,i);if(d)return d;if(typeof s!="object"||s===null||Array.isArray(s))return "message: expected object";let a=s;return a.gem!==c?"gem: wrong protocol marker":a.type!==r?`type: expected ${r}`:Ce(a,t,"")}}o(l,"makeValidator");var m=["requestId","s",64],E=["outcome","e",["ok","declined","unavailable"]],T=["reason","S",64,"code"];l("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,["","s",64]],["embedder","s",2048,"origin"],["unbidden","B"]],30);l("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,["","s",64]]],5);l("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);l("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);l("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);l("gem.pause",[["paused","b"]],120);l("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);l("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");l("gem.earnAllowanceChanged",[],30,"earnAllowance");l("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");l("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");l("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);l("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");l("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");l("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");l("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");l("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("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],["matchId","s",64],["data","j"]],1800,"p2p");l("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],["matchId","s",64],["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");l("gem.partyResult",[m,["partyId","S",64]],30,"parties");l("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,["","s",64]]],12,"clusterTarget");l("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");l("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");l("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");l("gem.requestContext",[],12,"feedbackContext");l("gem.gameContext",[["context","j"]],12,"feedbackContext");l("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],["matchId","s",64],["data","j"]],1200,"p2p");l("gem.dedicatedOffer",[["matchId","s",64],["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");l("gem.partyRequest",[m],12,"parties");l("gem.ready",[],5);l("gem.engaged",[],5,"engagement");l("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);l("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);l("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");l("gem.localServer",[m,["serverId","S",128]],30,"localHost");l("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");l("gem.exit",[["reason","S",64]],5);l("gem.openExternal",[["url","s",2048,"url"]],6);l("gem.requestFullscreen",[["enabled","b"]],30);l("gem.audioState",[["muted","b"]],120,"audioState");l("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");l("gem.walletDisplay",[["visible","b"],["currencies","A",8,["","s",64]],["score","B"]],60,"walletDisplay");l("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");l("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");l("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");l("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");l("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");l("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");l("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");l("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");l("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");l("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");l("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");l("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");l("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);l("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");l("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");l("gem.purchaseResult",[m,E,T,["purchaseId","S",64]],30,"purchases");l("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");l("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");l("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");l("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");l("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");l("gem.momentResult",[m,["shown","b"]],12,"moments");l("gem.requestSignIn",[m],6,"signIn");l("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");l("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");l("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");l("gem.requestAppLinks",[m],12,"appLinks");l("gem.appLinksResult",[m,E,T,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");l("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");l("gem.openAppResult",[m,E,T],30,"appLinks");l("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");l("gem.openGameResult",[m,E,T],30,"openGame");l("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");l("gem.similarGamesResult",[m,E,T,["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);l("gem.requestStoreOffers",[m],12,"store");l("gem.storeOffersResult",[m,E,T,["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);l("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");l("gem.storePurchaseResult",[m,E,T],30,"store");l("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");l("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");l("gem.storeOfferSheetResult",[m,E,T],30,"storeOfferSheet");var D=Object.freeze(Oe);M.__malformed=60;Object.freeze(M);Object.freeze(ye);var yt=/^[a-z0-9][a-z0-9_-]{0,39}$/;function ht(r){return typeof r=="string"&&yt.test(r)}o(ht,"isBorderName");function he(r){if(r===null||ht(r))return r;let t=typeof r=="string"?JSON.stringify(r.length>48?`${r.slice(0,48)}\u2026`:r):`a value of type ${typeof r}`;console.warn(`[gem] showPortraitBorders: ${t} 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.`);}o(he,"checkBorderName");var I={now:o(()=>Date.now(),"now"),setTimeout:o((r,t)=>setTimeout(r,t),"setTimeout"),clearTimeout:o(r=>clearTimeout(r),"clearTimeout")},K=class{constructor(t=I){this.clock=t;}clock;static{o(this,"RateLimiter");}tokens=new Map;lastRefill=new Map;allow(t){let e=M[t];if(e===void 0)return false;let n=this.clock.now(),i=this.lastRefill.get(t)??n,s=this.tokens.get(t)??e,d=Math.max(0,n-i),a=Math.min(e,s+d*e/6e4);return a<1?(this.tokens.set(t,a),this.lastRefill.set(t,n),false):(this.tokens.set(t,a-1),this.lastRefill.set(t,n),true)}};function J(r,t){return r.toLowerCase()===t.toLowerCase()}o(J,"originsEqual");function F(r){let t=r.toLowerCase();return t==="localhost"||t.endsWith(".localhost")||t==="[::1]"||t==="::1"?true:/^127(\.\d{1,3}){3}$/.test(t)}o(F,"isLoopbackHostname");function be(r,t){if(typeof t!="string"||t==="")return false;let e,n;try{e=new URL(r),n=new URL(t);}catch{return false}return e.protocol!=="http:"&&e.protocol!=="https:"||n.protocol!=="http:"&&n.protocol!=="https:"||e.username!==""||e.password!==""||e.pathname!=="/"||e.search!==""||e.hash!==""||e.port===""?false:F(e.hostname)&&F(n.hostname)}o(be,"isLoopbackDevHostEmbedder");function Me(r,t){if(r.min>r.max||t.min>t.max)return null;let e=Math.min(r.max,t.max),n=Math.max(r.min,t.min);return e>=n?e:null}o(Me,"negotiateVersion");var $e="[gem: use gem.text.toPlain()]",Ge=new Set,Le;function bt(r){try{if(Ge.has(r))return;let t=globalThis.location;if(Le??=!!t?.port&&F(t.hostname??""),!Le)return;Ge.add(r),console.warn(`[gem] ${r} is PlayerText, turned into a string: players see ${$e}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
2
- ${new Error().stack}`);}catch{}}o(bt,"warnStringified");var oe=class{static{o(this,"PlayerText");}#e;#t;constructor(t,e="player text"){this.#e=t,this.#t=e,Object.freeze(this);}toString(){return bt(this.#t),$e}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(t){return t.#e}};function G(r,t){return new oe(r,t)}o(G,"asPlayerText");function De(r){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(r)?r:0)))}o(De,"cuePoint");function Ne(r,t){if(r!==true)return {visible:false};let e=[],n=false;for(let i of Array.isArray(t)?t:[])i==="score"?n=true:typeof i=="string"&&i.length>0&&i.length<=64&&!e.includes(i)&&e.push(i);return !n&&e.length===0?{visible:true}:{visible:true,currencies:e.slice(0,8),score:n}}o(Ne,"walletState");function je(r){let t=[];for(let e of Array.isArray(r)?r:[]){if(t.length===4)break;if(typeof e!="object"||e===null)continue;let{id:n,label:i}=e;typeof n!="string"||n.length===0||n.length>32||typeof i!="string"||i.trim().length===0||t.some(s=>s.id===n)||t.push({id:n,label:i.trim().slice(0,40)});}return t.length===0?{settings:false}:{settings:false,items:t}}o(je,"menuState");function Fe(r,t){return Math.min(t,Math.max(0,Math.round(Number.isFinite(r)?r:0)))}o(Fe,"clampStat");function vt(r){let t=r.untrusted,e=G(t.displayName,"gem.player.displayName"),n=t.displayTag||t.displayHandle,i=n?G(n,"gem.player.displayTag"):void 0,s=r.discriminator||r.displayTag,d=s&&/^[0-9]{1,8}$/.test(s)?s:void 0,a=r.avatarIconUrl||r.avatarUrl,u=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,y=r.avatarIconId,w=r.avatar3dId,b={displayName:e,displayHandle:i??e,badges:(r.badges??[]).map(v=>({itemId:v.itemId,iconUrl:v.iconUrl}))};r.playerId&&(b.playerId=r.playerId),d&&(b.discriminator=d),i&&(b.displayTag=i),a&&(b.avatarIconUrl=b.avatarUrl=a),y&&u.test(y)&&(b.avatarIconId=y),w&&u.test(w)&&(b.avatar3dId=w),r.avatar3dModelUrl&&(b.avatar3dModelUrl=r.avatar3dModelUrl),r.avatar3dIconUrl&&(b.avatar3dIconUrl=r.avatar3dIconUrl),r.avatars3d&&(b.avatars3d=r.avatars3d.filter(v=>u.test(v.id)&&/^[a-z][a-z0-9_]*$/.test(v.family)).filter((v,ue,me)=>me.findIndex(pe=>pe.family==v.family)==ue).map(({family:v,id:ue,modelUrl:me,iconUrl:pe})=>({family:v,id:ue,modelUrl:me,iconUrl:pe})));let O=r.boost,de=(O?.multipliers??[]).map(v=>({currencyId:v.currencyId,currencySlug:v.currencySlug,multiplier:v.multiplierBp/1e4})),ce=de.some(v=>v.multiplier>1);return b.boost=ce&&O?.expiresInDays?{active:ce,multipliers:de,expiresInDays:O.expiresInDays}:{active:ce,multipliers:de},b}o(vt,"playerFromWire");var _t=1e4,wt=["appLinks","openGame","store","storeOfferSheet"],St=new WeakMap;var q=class{constructor(t){this.options=t;if(t.expectedSource===void 0||t.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=t.expectedSource,this.clock=t.clock??I,this.inbound=new K(this.clock),this.outbound=new K(this.clock);}options;static{o(this,"GemFrame");}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(t){return this.peerCapabilities.includes(t)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(t){if(t.source!==this.expectedSource)return this.refuse(t,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>J(a,t.origin))||be(t.origin,this.options.selfOrigin)||(St.get(this)??[]).some(a=>J(a,t.origin))))return this.refuse(t,`embedder '${t.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 n=D["gem.handshake"]?.(t.data);if(n)return this.refuse(t,`malformed handshake: ${n}`),false;let i=t.data;if(!J(i.embedder,t.origin))return this.refuse(t,`handshake claims embedder '${i.embedder}' but arrived from '${t.origin}'`),false;let s=t.ports?.[0];if(!s)return this.refuse(t,"handshake transferred no MessagePort"),false;let d=Me(R,i.versions);return d===null?(s.onmessage=null,s.postMessage({gem:c,type:"gem.error",code:"version_mismatch",message:`frame supports v${R.min}..v${R.max}`}),s.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=s,this.version=d,s.onmessage=a=>this.receive(a.data,a.ports??[]),s.start?.(),this.peerCapabilities=i.capabilities??[],this.unbidden=i.unbidden===true,s.postMessage({gem:c,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:c,type:"gem.ready"});}settled(t=_t){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(e=>{let n=o(s=>{this.clock.clearTimeout(i),e(s);},"waiter"),i=this.clock.setTimeout(()=>{let s=this.settleWaiters.indexOf(n);s>=0&&this.settleWaiters.splice(s,1),e(this.state);},t);this.settleWaiters.push(n);})}progress(t,e){this.send({gem:c,type:"gem.progress",loaded:t,total:e});}requestToken(t,e){this.send({gem:c,type:"gem.requestToken",requestId:t,reason:e});}requestLocalServer(t,e){return this.parentSupports("localHost")?(this.send({gem:c,type:"gem.requestLocalServer",requestId:t,gameRoomId:e}),true):false}exit(t){this.send(t===void 0?{gem:c,type:"gem.exit"}:{gem:c,type:"gem.exit",reason:t});}openExternal(t){this.send({gem:c,type:"gem.openExternal",url:t});}requestFullscreen(t){this.send({gem:c,type:"gem.requestFullscreen",enabled:t});}reportMuted(t){return this.gated("audioState")?(this.send({gem:c,type:"gem.audioState",muted:t}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:c,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(t){if(!this.gated("rewardCue"))return false;let e={gem:c,type:"gem.rewardCue",currencySlug:String(t.currencySlug).slice(0,32)};return t.expected!==void 0&&Number.isFinite(t.expected)&&t.expected>=1&&(e.expected=Math.min(1e9,Math.round(t.expected))),t.origin&&(e.origin={x:De(t.origin.x),y:De(t.origin.y)}),this.send(e),true}showPortraitBorders(t){let e=he(t);return e===void 0||!this.gated("portraitBorders")?false:this.send(e===null?{gem:c,type:"gem.portraitBorders"}:{gem:c,type:"gem.portraitBorders",name:e})}setWalletVisible(t,e){return this.gated("walletDisplay")?this.send({gem:c,type:"gem.walletDisplay",...Ne(t,e?.only)}):false}setMenu(t){return this.gated("gameMenu")?this.send({gem:c,type:"gem.menu",...je(t)}):false}declareSettings(t){return this.gated("gameSettings")?this.send({gem:c,type:"gem.settingsDeclare",settings:t}):false}setGemCorner(t){return this.gated("gemCorner")?this.send({gem:c,type:"gem.gemCorner",corner:t}):false}setMenuStyle(t){return this.gated("menuStyle")?this.send(t===null?{gem:c,type:"gem.menuStyle"}:{gem:c,type:"gem.menuStyle",ground:t.ground,accent:t.accent}):false}requestShare(t){return this.gated("share")?this.send({gem:c,type:"gem.requestShare",params:t.map(({key:e,value:n})=>({key:e,value:n}))}):false}requestAvatarPicker(t){return this.gated("avatarPicker")?this.send({gem:c,type:"gem.requestAvatarPicker",...t==="2d"||t==="3d"?{tab:t}:{}}):false}requestVibrate(t){return this.gated("vibration")?this.send({gem:c,type:"gem.vibrate",pattern:[...t]}):false}reportFrameStats(t){return this.gated("frameStats")?(this.send({gem:c,type:"gem.frameStats",fps:Fe(t.fps,1e3),frameTimeP95Ms:Fe(t.frameTimeP95Ms,1e5),counts:t.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(t){return this.state!=="connected"?false:(this.send({gem:c,type:"gem.gameContext",context:t}),true)}signal(t,e,n){this.send({gem:c,type:"gem.signal",signal:t,matchId:e,data:n});}dedicatedOffer(t,e,n){this.send({gem:c,type:"gem.dedicatedOffer",matchId:t,sdp:e,intent:n});}requestParty(t){return this.send({gem:c,type:"gem.partyRequest",requestId:t})}requestPurchase(t,e,n,i,s,d){return this.send({gem:c,type:"gem.requestPurchase",requestId:t,purchaseKind:e,targetId:n,currencySlug:i,...s===void 0?{}:{quantity:s},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(t,e,n){return this.parentSupports("avatar3dPurchases")?this.send({gem:c,type:"gem.requestAvatar3dPurchase",requestId:t,itemId:e,currencySlug:n}):false}putDeviceSettings(t,e){return this.send({gem:c,type:"gem.deviceSettings.put",requestId:t,settings:e})}requestAd(t,e,n){return this.send({gem:c,type:"gem.requestAd",requestId:t,placement:e,format:n})}signalMoment(t,e,n,i){return this.send({gem:c,type:"gem.signalMoment",requestId:t,kind:e,...n===void 0?{}:{placement:n},...i===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(t){return this.send({gem:c,type:"gem.requestSignIn",requestId:t})}requestFriendChallenge(t,e,n,i){return this.send({gem:c,type:"gem.requestFriendChallenge",requestId:t,playerId:e,template:n,...i===void 0?{}:{params:i}})}requestArcade(t){return this.gated(re(t.type))&&this.send({gem:c,...t})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(t){if(this.state!==t){this.state=t;for(let e of this.settleWaiters.splice(0))e(t);this.options.onStateChange?.(t);}}refuseMalformed(t,e){if(!this.inbound.allow(ne)){this.options.onDropped?.(t,e);return}this.fail(t,e);}dropUnknownType(t){this.inbound.allow(ne)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${t}'`);}refuse(t,e){t.ports?.[0]?.close?.(),this.inbound.allow(ne)&&this.options.onRefused?.(e);}fail(t,e){this.options.onError?.(t,e),this.state==="connected"&&this.port&&this.send({gem:c,type:"gem.error",code:t,...e===void 0?{}:{message:e}});}gated(t){return this.state==="connected"&&this.parentSupports(t)}send(t){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(t.type)} before connect`),false;let e=t.type;if(!this.outbound.allow(e))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${e}`),false;let n=D[e]?.(t);return n?(this.options.onError?.("internal",`refusing to send malformed ${e}: ${n}`),false):(this.port.postMessage(this.version===null?t:{...t,v:this.version}),true)}receive(t,e=[]){let n=false;try{n=this.dispatchInbound(t,e);}finally{n||qe(e);}}dispatchInbound(t,e){if(this.state!=="connected")return false;let n=typeof t=="object"&&t!==null?t.type:void 0;if(typeof n!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let i=D[n];if(!i)return this.dropUnknownType(n),false;if(!this.inbound.allow(n))return this.fail("rate_limited",`parent exceeded the limit for ${n}`),false;let s=re(n);if(s!==void 0&&!this.options.capabilities?.includes(s))return this.fail("unsupported_capability",`${n} requires the '${s}' capability, which this frame did not advertise`),false;let d=i(t);if(d)return this.fail("invalid_message",`${n}: ${d}`),false;let a=t;if(a.v!==this.version)return this.fail("invalid_message",`${n}: expected v ${String(this.version)}`),false;switch(n){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.settingChange":this.options.onSettingChange?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let u=e[0];if(!u){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let y={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){u.close(),this.screenshotDenied("not_wired",false);break}return qe(e.slice(1)),this.options.onScreenshot(y,u,(w,b)=>this.screenshotDenied(w,b)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(vt(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:if(wt.includes(re(n))){this.options.onArcadeAnswer?.(a);break}this.fail("unsupported",`no frame handler for '${n}'`);}return false}screenshotDenied(t,e){this.send({gem:c,type:"gem.screenshot.denied",reason:t,retryable:e});}};function qe(r){for(let t of r)try{t?.close?.();}catch{}}o(qe,"closeAll");globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:18.0.0]";var Be=["if-match","if-none-match"];var xt={unknown_currency:1,currency_not_in_game_payable:1,not_purchasable:1,no_price_in_currency:1,busy:1,cancelled:1,insufficient_funds:1,price_mismatch:1,config_version_stale:1,platform_currency_unavailable:1,not_available:1,recipe_locked:1,already_owned:1,recipe_on_cooldown:1,recipe_max_uses_exceeded:1,item_max_stack_exceeded:1,recipe_costs_unmet:1,quantity_invalid:1,error:1};new Set(Object.keys(xt));new Set(Be);new TextEncoder;var We=["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"];new Set(We);new TextEncoder;new TextDecoder;new TextEncoder;var rn=8;(class{constructor(t=rn){this.perMinute=t;}perMinute;static{o(this,"CredentialPacing");}sent=[];delayUntilAllowed(t){return this.prune(t),this.sent.length<this.perMinute?0:Math.max(0,(this.sent[0]??t)+6e4-t)}record(t){this.prune(t),this.sent.push(t);}prune(t){let e=0;for(let n of this.sent)n<=t&&t-n<6e4&&(this.sent[e++]=n);this.sent.length=e;}});function p(r){return t=>typeof t=="string"&&t.length>0&&t.length<=r?t:void 0}o(p,"text");function C(r,t){return e=>typeof e=="number"&&Number.isInteger(e)&&e>=r&&e<=t?e:void 0}o(C,"integer");function j(...r){return t=>typeof t=="string"&&r.includes(t)?t:void 0}o(j,"oneOf");function He(r){return typeof r=="boolean"?r:void 0}o(He,"flag");var _=p(64),Q=C(0,2147483647);function Re(r){if(!Array.isArray(r))return;let t=[];for(let e of r){if(t.length>=64)break;let n=_(e);typeof n=="string"&&t.push(n);}return t}o(Re,"ids");function f(r,t,e){return {to:r,from:t,read:e,required:true}}o(f,"required");function g(r,t,e){return {to:r,from:t,read:e,required:false}}o(g,"optional");var sn=o(r=>{if(typeof r!="object"||r===null||Array.isArray(r))return;let t=r,e=j("disconnected","failed","closed")(t.iceState),n=C(0,4102444800)(t.lastSeenAt);if(!(e===void 0&&n===void 0))return {...e===void 0?{}:{ice_state:e},...n===void 0?{}:{last_seen_timestamp:n}}},"evidence");Object.freeze(Object.assign(Object.create(null),{join:{type:"p2p_join",gameScoped:true,args:[]},offer:{type:"p2p_offer",gameScoped:false,args:[f("sdp","sdp",p(10240)),f("signaling_intent","intent",j("initial","ice_restart","full_reconnect")),g("sdp_hash","sdpHash",p(128))]},answer:{type:"p2p_answer",gameScoped:false,args:[f("peer_id","peerId",_),f("sdp","sdp",p(10240)),f("assigned_peer_id","assignedPeerId",C(0,4294967295)),g("sdp_hash","sdpHash",p(128))]},ice:{type:"p2p_ice_candidate",gameScoped:false,args:[f("peer_id","peerId",_),f("candidate","candidate",p(512)),g("sdp_mid","sdpMid",p(64)),g("sdp_mline_index","sdpMLineIndex",C(0,255))]},migrationAck:{type:"p2p_migration_ack",gameScoped:false,args:[f("host_epoch","hostEpoch",Q)]},hostUnreachable:{type:"p2p_report_host_unreachable",gameScoped:false,args:[f("host_epoch","hostEpoch",Q),g("evidence","evidence",sn)]}}));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"}));Object.freeze(Object.assign(Object.create(null),{hello:[f("playerId","player_id",_),f("hostPlayerId","host_player_id",_),f("hostEpoch","host_epoch",Q),g("members","members",Re)],offer:[f("fromPlayerId","from_player_id",_),f("sdp","sdp",p(10240)),g("sessionId","signaling_session_id",p(128)),g("sdpHash","sdp_hash",p(128)),g("iceUfrag","ice_ufrag",p(64)),g("intent","signaling_intent",j("initial","ice_restart","full_reconnect"))],answer:[f("fromPlayerId","from_player_id",_),f("sdp","sdp",p(10240)),g("sessionId","signaling_session_id",p(128)),g("sdpHash","sdp_hash",p(128)),g("iceUfrag","ice_ufrag",p(64)),g("assignedPeerId","assigned_peer_id",C(0,4294967295))],ice:[f("fromPlayerId","from_player_id",_),f("candidate","candidate",p(512)),g("sdpMid","sdp_mid",p(64)),g("sdpMLineIndex","sdp_m_line_index",C(0,255)),g("sdpMLineIndexAlt","sdp_mline_index",C(0,255))],peerJoined:[f("playerId","player_id",_)],peerLeft:[f("playerId","player_id",_),g("reason","reason",p(64))],migration:[f("hostPlayerId","new_host_player_id",_),f("hostEpoch","host_epoch",Q),g("reason","reason",p(64)),g("kickedPlayerIds","kicked_player_ids",Re),g("migrationComplete","migration_complete",He)],matchEnded:[g("reason","reason",p(64))],error:[g("code","code",p(64)),g("message","message",p(256))]}));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"}));Object.freeze(Object.assign(Object.create(null),{webrtc_sdp_answer:"answer",webrtc_error:"error",match_ended:"matchEnded"}));Object.freeze(Object.assign(Object.create(null),{answer:[f("sdp","sdp",p(10240)),g("sessionId","session_id",p(64))],error:[g("code","code",p(64)),g("message","message",p(256))],matchEnded:[g("reason","reason",p(64))]}));var nt=Object.freeze(["https://gemarcade.com","https://developer.gemarcade.com","http://gemarcade.localhost","gemarcade://localhost"]);var V=class extends Error{constructor(e,n){super(`[${e}] ${n}`);this.caseId=e;this.name="ParentConformanceError";}caseId;static{o(this,"ParentConformanceError");}},Mv=3,rt="https://game.conformance.invalid",Xn="https://stranger.conformance.invalid",Ae=M["gem.handshake"]??1,ee=6e4,ae=class{static{o(this,"KitClock");}current=1e6;seq=0;pending=new Map;now(){return this.current}setTimeout(t,e){let n=++this.seq;return this.pending.set(n,{at:this.current+Math.max(0,e),fn:t}),n}clearTimeout(t){this.pending.delete(t);}get pendingTimers(){return this.pending.size}advance(t){let e=this.current+t;for(;;){let n;for(let i of this.pending)i[1].at<=e&&(n===void 0||i[1].at<n[1].at)&&(n=i);if(n===void 0)break;this.pending.delete(n[0]),this.current=n[1].at,n[1].fn();}this.current=e;}},ot=Object.freeze({conformance:"parent-window"}),se=class{constructor(t,e,n,i){this.caseId=t;this.options=n;let s=i??n.frameOrigin??rt,d={frame:{postMessage:o((a,u,y)=>{this.offers.push({data:a,targetOrigin:u,transfer:y??[],at:this.clock.now()});},"postMessage")},frameOrigin:s,embedderOrigin:n.embedderOrigin,clock:this.clock,makeChannel:o(()=>{let a=new MessageChannel,u={port1:a.port1,port2:a.port2,closed:false},y=a.port1.close.bind(a.port1);return a.port1.close=()=>{u.closed=true,y();},this.count(a.port1),this.count(a.port2),this.minted.push(u),a},"makeChannel"),reportError:o((a,u)=>{this.errors.push(u===void 0?{code:a}:{code:a,message:u});},"reportError")};Object.defineProperty(d,"userActivation",{enumerable:true,get:o(()=>this.activation===void 0?void 0:{...this.activation},"get")}),this.parent=e(d);}caseId;options;static{o(this,"Rig");}clock=new ae;frameClock=new ae;offers=[];minted=[];parent;activation={isActive:false};errors=[];inFlight=0;frames=[];get frameOrigin(){return this.options.frameOrigin??rt}fail(t){throw new V(this.caseId,t)}count(t){let e=t.postMessage.bind(t);t.postMessage=((...n)=>{this.inFlight+=1,e(...n);}),t.addEventListener("message",()=>{this.inFlight-=1;});}async flush(){let t=o(()=>new Promise(e=>setTimeout(e,0)),"turn");await t(),await t();for(let e=0;this.inFlight>0&&e<200;e++)await t();}advance(t,e=250){for(let n=0;n<t;n+=e){let i=Math.min(e,t-n);this.clock.advance(i),this.frameClock.advance(i);}}start(){try{this.parent.start();}catch(t){this.fail(`start() threw: ${it(t)}`);}this.offers.length===0&&this.fail("start() posted no handshake to the frame window. The offer must be posted at start, on the window the fixture names as `frame`.");}latest(){let t=this.offers[this.offers.length-1];return t===void 0&&this.fail("no handshake has been offered"),t}transferred(t){t.transfer.length!==1&&this.fail(`an offer transferred ${t.transfer.length} objects; it must transfer exactly one MessagePort.`);let e=this.minted.find(n=>n.port2===t.transfer[0]);return e===void 0&&this.fail("an offer transferred a port the fixture did not mint. Take every offer\u2019s channel from `fixture.makeChannel`, so the kit can see which ports the parent closes."),e}frame(t){let e=t??[...new Set([...nt,...this.options.allowedEmbedders??[]])],n=[],i=new q({allowedEmbedders:e,expectedSource:ot,selfOrigin:this.frameOrigin,clock:this.frameClock,onRefused:o(s=>n.push(s),"onRefused")});return i.kitRefusals=n,this.frames.push(i),i}deliver(t,e){let n=this.transferred(e);return t.handleWindowMessage({data:e.data,origin:this.options.embedderOrigin,source:ot,ports:[n.port2]})}answer(t,e,n=[]){this.transferred(t).port2.postMessage({gem:c,type:"gem.handshake.accept",v:e,capabilities:[...n]});}advertised(){let t=this.latest().data.capabilities;return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}async raw(t){let e=this.latest(),n=this.transferred(e),i=[];n.port2.onmessage=a=>{a.data!==null&&typeof a.data=="object"&&i.push(a.data);};let s=Math.min(W(this).max,R.max);this.answer(e,s,t),await this.flush(),this.state()!=="connected"&&this.fail(`a hand-written frame accepted at v${s}, and the parent reports '${this.state()}'.`);let d=0;return {received:i,ask:o(async(a,u,y)=>{let w=`conformance-${++d}`;return this.activation=y==="missing"?void 0:{isActive:y==="pressed"},n.port2.postMessage({gem:c,type:a,v:s,requestId:w,...u}),await this.flush(),this.activation={isActive:false},w},"ask"),answers:o(a=>i.filter(u=>u.requestId===a),"answers")}}surfaces(t){let e=this.parent.surfacesShown;return typeof e!="number"&&this.fail(`${t}, so the kit must see what it shows: expose \`surfacesShown\` on the adapter (every checkout, card, QR code, toast or tab the parent opened for a game).`),e}offersInAnyMinute(){let t=0;for(let e=0;e<this.offers.length;e++){let n=this.offers[e].at,i=0;for(let s=e;s<this.offers.length&&this.offers[s].at<n+ee;s++)i++;t=Math.max(t,i);}return t}state(){return this.parent.state}dispose(){try{this.parent.close();}catch{}for(let t of this.frames)t.close();for(let t of this.minted)try{t.port1.close(),t.port2.close();}catch{}}};function it(r){return r instanceof Error?r.message:String(r)}o(it,"describe");function Yn(r){return r.kitRefusals??[]}o(Yn,"refusalsOf");function W(r){let t=r.latest().data.versions,e=t?.min,n=t?.max;return (typeof e!="number"||typeof n!="number")&&r.fail("the handshake carries no numeric `versions.min` / `versions.max`"),{min:e,max:n}}o(W,"offeredRange");async function h(r,t,e,n){let i=new se(r,t,e);try{await n(i);}finally{i.dispose();}}o(h,"withRig");async function Z(r){let t=r.frame();return r.deliver(t,r.latest())||r.fail(`a game built with this SDK refused the handshake from ${r.options.embedderOrigin}: ${Yn(t).join("; ")||"no reason recorded"}. A production origin the SDK does not carry needs an SDK that lists it; a non-production one goes in \`allowedEmbedders\` here, as a game would name it.`),await r.flush(),r.state()!=="connected"&&r.fail(`the frame accepted, but the parent reports '${r.state()}' rather than 'connected'.`),t}o(Z,"connect");var x=["appLinks","openGame","store","storeOfferSheet"],Qn="00000000-0000-4000-8000-00000000c0de",le="conformance-unknown-sku",xe={appLinks:{type:"gem.requestOpenApp",fields:{target:"arcade"}},openGame:{type:"gem.requestOpenGame",fields:{ref:Qn}},store:{type:"gem.requestStorePurchase",fields:{skuId:le}},storeOfferSheet:{type:"gem.requestStoreOfferSheet",fields:{skuId:le}}};function $(r,t,e,n,i){let s=t.answers(e);s.length!==1&&r.fail(`${i}: the parent sent ${s.length} answers; it owes exactly one.`);let[d]=s;(d.outcome!=="declined"||d.reason!=="busy")&&r.fail(`${i}: answered ${JSON.stringify(d.outcome)} / ${JSON.stringify(d.reason)}, not 'declined' / 'busy'.`);let a=r.surfaces(i);a!==n&&r.fail(`${i}: the parent showed ${a-n} surface(s); it must show nothing.`);}o($,"expectBusy");async function Pe(r,t,e="gem.requestStorePurchase"){let n=await t.ask("gem.requestStoreOffers",{},"unpressed"),[i]=t.answers(n),d=(i?.offers??[])[0]?.skuId;(i?.outcome!=="ok"||typeof d!="string")&&r.fail(`the parent advertises \`store\` but listed no offer to a signed-in player (${JSON.stringify(i?.outcome)} / ${JSON.stringify(i?.reason)}). Give the adapter's store a signed-in player and at least one SKU, so the kit can open a checkout.`);let a=r.surfaces("the parent advertises `store`"),u=await t.ask(e,{skuId:d},"pressed"),y=r.surfaces("the parent advertises `store`");return y!==a+1&&r.fail(`a pressed ${e} of the listed SKU ${JSON.stringify(d)} showed ${y-a} surface(s); it must open exactly one.`),t.answers(u).length>0&&r.fail(`${e} was answered while what it opened was still open. Answer once the player has acted, so the kit can hold it open.`),{requestId:u,skuId:d,shown:y}}o(Pe,"openStoreCheckout");function Ee(r){typeof r.parent.dismiss!="function"&&r.fail("the parent advertises `store`, so the kit must be able to close its checkout: expose `dismiss()` on the adapter (the player closing the checkout without paying)."),r.parent.dismiss();}o(Ee,"dismiss");var Zn=["*","null","","https://game.conformance.invalid/","https://game.conformance.invalid/play"],er=[{id:"origin.refuses-imprecise-target",title:"refuses a frame origin that is not one exact origin, before anything is posted",async run(r,t){for(let e of Zn){let n=JSON.stringify(e),i,s=false;try{i=new se(this.id,r,{...t,frameOrigin:e},e),i.parent.start();}catch(d){if(d instanceof V)throw d;s=true;}try{if((i?.offers??[]).some(a=>a.targetOrigin==="*"||a.targetOrigin===e))throw new V(this.id,`with frame origin ${n} the parent posted the handshake anyway \u2014 the offer transfers the session port, so an imprecise target hands the session to whatever the frame has navigated to.`);if(!s)throw new V(this.id,`a frame origin of ${n} was accepted. Refuse it at construction or at start(), by throwing.`)}finally{i?.dispose();}}}},{id:"handshake.targets-frame-origin",title:"posts a valid handshake to exactly the frame origin, naming the page\u2019s own origin",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(P);for(let s of e.offers){s.targetOrigin!==e.frameOrigin&&e.fail(`an offer was posted with targetOrigin ${JSON.stringify(s.targetOrigin)}, not the frame origin ${e.frameOrigin}.`),e.transferred(s);let d=D["gem.handshake"]?.(s.data);d&&e.fail(`the handshake does not validate against the schema: ${d}`);let a=s.data;a.gem!==c&&e.fail(`the handshake's \`gem\` is not ${c}`),a.embedder!==t.embedderOrigin&&e.fail(`the handshake names embedder ${JSON.stringify(a.embedder)}, but the page is served from ${t.embedderOrigin}. Every game compares the two and refuses a mismatch: name the origin the page is actually served from.`);}let{min:n,max:i}=W(e);n>i&&e.fail(`the offered range is empty: min ${n} > max ${i}`),(i<R.min||n>R.max)&&e.fail(`the offered range v${n}\u2013v${i} shares no version with this SDK (v${R.min}\u2013v${R.max}).`);});}},{id:"handshake.default-game-connects",title:"connects to a game that names no embedders, at the highest common version",async run(r,t){await h(this.id,r,t,async e=>{e.start();let n=await Z(e),{max:i}=W(e),s=Math.min(i,R.max);n.protocolVersion!==s&&e.fail(`the frame chose v${String(n.protocolVersion)}, expected v${s}`),e.parent.protocolVersion!==n.protocolVersion&&e.fail(`the parent reports v${String(e.parent.protocolVersion)} while the frame chose v${String(n.protocolVersion)}. Adopt the version on the accept.`);});}},{id:"handshake.refusal-is-not-a-verdict",title:"keeps offering, still handshaking, when a game refuses this page",async run(r,t){await h(this.id,r,t,async e=>{e.start();let n=e.frame([Xn]);e.deliver(n,e.latest())&&e.fail("a frame that does not allow this page accepted it \u2014 the kit is misconfigured"),await e.flush();let i=e.offers.length;e.advance(P*3),await e.flush(),e.state()!=="handshaking"&&e.fail(`after a refusal and ${P*3}ms the parent reports '${e.state()}'. A refusing game answers nothing, by design, so silence is not evidence: stay handshaking until the page closes the bridge.`),e.offers.length<=i&&e.fail("the parent stopped offering after a refusal; keep offering on the cadence.");});}},{id:"version.adopts-frame-choice",title:"adopts the version the frame chose from inside the offered range",async run(r,t){await h(this.id,r,t,async e=>{e.start();let{min:n}=W(e);e.answer(e.latest(),n),await e.flush(),(e.state()!=="connected"||e.parent.protocolVersion!==n)&&e.fail(`answered with v${n}, the bottom of the offered range, the parent reports '${e.state()}' at v${String(e.parent.protocolVersion)}.`);});}},{id:"version.refuses-out-of-range",title:"never connects at a version outside the range it offered",async run(r,t){for(let e of ["above","below"])await h(this.id,r,t,async n=>{n.start();let{min:i,max:s}=W(n),d=e==="above"?s+1:i-1;n.answer(n.latest(),d),await n.flush(),(n.state()==="connected"||n.parent.protocolVersion===d)&&n.fail(`answered with v${d}, outside the offered v${i}\u2013v${s}, the parent connected. A frame choosing a version this parent never offered is not a session.`);});}},{id:"lifecycle.reoffers-on-fresh-ports",title:"re-offers on a fresh port while unanswered, inside the contract\u2019s budget",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(P*2),e.offers.length<2&&e.fail(`one offer in ${P*2}ms. A message to a document that is not listening yet is lost, not queued: re-offer until something answers.`);let n=e.offers.map(s=>e.transferred(s).port2);new Set(n).size!==n.length&&e.fail("an offer re-used a port; each offer needs its own channel."),e.advance(ee*2);let i=e.offersInAnyMinute();i>Ae&&e.fail(`${i} offers inside one minute; the contract budgets ${Ae} \`gem.handshake\` per minute.`);});}},{id:"lifecycle.late-answer-adopts-and-closes-the-rest",title:"adopts the offer a late frame answers, and closes every other offered port",async run(r,t){await h(this.id,r,t,async e=>{e.start(),e.advance(P*2);let n=e.transferred(e.latest());await Z(e),n.closed&&e.fail("the parent closed the port the frame answered on.");let i=e.minted.filter(s=>s!==n&&!s.closed).length;i>0&&e.fail(`${i} unanswered offer port(s) still open after connecting. Close every other offer when one is answered.`);});}},{id:"lifecycle.no-offers-after-connect",title:"stops offering once connected",async run(r,t){await h(this.id,r,t,async e=>{e.start(),await Z(e);let n=e.offers.length;e.advance(ee*5,1e3),e.offers.length!==n&&e.fail(`${e.offers.length-n} offer(s) after the session connected.`);});}},{id:"lifecycle.duplicate-accept-keeps-session",title:"keeps the session when a second accept arrives, even an out-of-range one",async run(r,t){await h(this.id,r,t,async e=>{e.start(),await Z(e);let n=e.parent.protocolVersion;e.answer(e.latest(),W(e).max+1),await e.flush(),(e.state()!=="connected"||e.parent.protocolVersion!==n)&&e.fail(`a second accept moved the parent to '${e.state()}' at v${String(e.parent.protocolVersion)}. One answer settles the handshake.`);});}},{id:"lifecycle.close-releases-everything",title:"on close, releases every port and timer and offers nothing more",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(P),e.parent.close(),e.state()!=="closed"&&e.fail(`after close() the parent reports '${e.state()}'`);let n=e.minted.filter(s=>!s.closed).length;n>0&&e.fail(`${n} offer port(s) left open after close().`),e.clock.pendingTimers>0&&e.fail(`${e.clock.pendingTimers} timer(s) still armed after close().`);let i=e.offers.length;e.advance(ee*2,1e3),e.offers.length!==i&&e.fail("the parent offered again after close().");}),await h(this.id,r,t,async e=>{e.start(),await Z(e),e.parent.close(),e.state()!=="closed"&&e.fail(`after close() the parent reports '${e.state()}'`),e.minted.filter(i=>!i.closed).length>0&&e.fail("the session port is still open after close().");});}},{id:"lifecycle.unanswered-offers-are-bounded",title:"holds at most about a minute of unanswered offer ports, however long it runs",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(ee*10,1e3);let n=e.minted.filter(i=>!i.closed).length;n>Ae+1&&e.fail(`${n} unanswered offer ports open after ten minutes. Close offers nothing answered within a grace (a minute is the reference), or the loop leaks a channel per tick for as long as the page holds the frame.`);});}},{id:"capability.unadvertised-is-refused",title:"refuses an arcade-feature or store ask under a capability either side did not advertise, answering nothing and showing nothing",async run(r,t){for(let e of x)await h(this.id,r,t,async n=>{n.start();let i=n.advertised().includes(e),s=x.filter(O=>!i||O!==e),d=await n.raw(s),a=n.parent.surfacesShown,{type:u,fields:y}=xe[e],w=await d.ask(u,y,"pressed"),b=i?"the frame":"the parent";d.answers(w).length>0&&n.fail(`${u} under '${e}', which ${b} did not advertise, was answered. Refuse it before anything else and send nothing back.`),n.parent.surfacesShown!==a&&n.fail(`${u} under unadvertised '${e}' showed a surface.`),n.errors.some(O=>O.code==="unsupported_capability")||n.fail(`${u} under '${e}', which ${b} did not advertise, was not reported as 'unsupported_capability'. Report it through \`fixture.reportError\` (what GemHost calls onError).`);});}},{id:"acting.refuses-without-activation",title:"answers an acting ask with no press \u2014 or no activation API \u2014 declined / busy, showing nothing",async run(r,t){for(let e of x)for(let n of ["unpressed","missing"])await h(this.id,r,t,async i=>{if(i.start(),!i.advertised().includes(e))return;let s=await i.raw(x),d=i.surfaces(`the parent advertises '${e}'`),{type:a,fields:u}=xe[e],y=await s.ask(a,u,n);$(i,s,y,d,n==="missing"?`${a} with no userActivation API (it must fail closed)`:`${a} with no press`);});}},{id:"store.unknown-sku-is-unavailable",title:"answers a store purchase of a SKU it does not list unavailable, showing nothing",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("store"))return;let n=await e.raw(x),i=e.surfaces("the parent advertises `store`"),s=await n.ask("gem.requestStorePurchase",{skuId:le},"pressed"),d=n.answers(s);(d.length!==1||d[0].outcome!=="unavailable")&&e.fail(`a pressed purchase of an unknown SKU was answered ${JSON.stringify(d)}; it owes exactly one 'unavailable'.`),e.surfaces("the parent advertises `store`")!==i&&e.fail("a purchase of an unknown SKU showed a surface; it must show nothing.");});}},{id:"acting.second-ask-is-busy",title:"answers any acting ask declined / busy, showing nothing, while a store checkout is open",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("store"))return;let n=await e.raw(x),i=await Pe(e,n),s=await n.ask("gem.requestStorePurchase",{skuId:i.skuId},"pressed");$(e,n,s,i.shown,"a second store purchase while one is open");for(let a of ["appLinks","openGame","storeOfferSheet"]){if(!e.advertised().includes(a))continue;let{type:u,fields:y}=xe[a],w=await n.ask(u,y,"pressed");$(e,n,w,i.shown,`${u} while a store checkout is open`);}Ee(e),await e.flush();let[d]=n.answers(i.requestId);(d?.outcome!=="declined"||d.reason!=="cancelled")&&e.fail(`the closed checkout was answered ${JSON.stringify(d?.outcome)} / ${JSON.stringify(d?.reason)}, not 'declined' / 'cancelled'.`);});}},{id:"store.backs-off-after-cancel",title:"answers the next store purchase declined / busy, showing nothing, after a dismissal",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("store"))return;let n=await e.raw(x),i=await Pe(e,n);Ee(e),await e.flush();let s=e.surfaces("the parent advertises `store`"),d=await n.ask("gem.requestStorePurchase",{skuId:i.skuId},"pressed");if($(e,n,d,s,"a store purchase straight after the player dismissed one"),!e.advertised().includes("storeOfferSheet"))return;let a=await n.ask("gem.requestStoreOfferSheet",{skuId:i.skuId},"pressed");$(e,n,a,s,"an offer sheet straight after the player dismissed a checkout");});}},{id:"sheet.needs-store",title:"advertises storeOfferSheet only beside store",async run(r,t){await h(this.id,r,t,async e=>{e.start();let n=e.advertised();n.includes("storeOfferSheet")&&!n.includes("store")&&e.fail("the parent advertises `storeOfferSheet` without `store`. A game takes its SKU from gem.storeOffersResult and hears a pending purchase settle through gem.storePurchaseSettled, and both are gated on `store`.");});}},{id:"sheet.unknown-sku-is-unavailable",title:"answers an offer sheet for a SKU it does not list unavailable, showing nothing",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("storeOfferSheet"))return;let n=await e.raw(x),i=e.surfaces("the parent advertises `storeOfferSheet`"),s=await n.ask("gem.requestStoreOfferSheet",{skuId:le},"pressed"),d=n.answers(s);(d.length!==1||d[0].outcome!=="unavailable")&&e.fail(`a pressed offer sheet for an unknown SKU was answered ${JSON.stringify(d)}; it owes exactly one 'unavailable'.`),e.surfaces("the parent advertises `storeOfferSheet`")!==i&&e.fail("an offer sheet for an unknown SKU showed a surface; it must show nothing.");});}},{id:"sheet.shares-the-slot-and-the-back-off",title:"holds the acting slot while an offer sheet is open, and backs both store asks off after the player leaves it",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("storeOfferSheet"))return;let n=await e.raw(x),i=await Pe(e,n,"gem.requestStoreOfferSheet"),s=await n.ask("gem.requestStorePurchase",{skuId:i.skuId},"pressed");$(e,n,s,i.shown,"a store purchase while an offer sheet is open"),Ee(e),await e.flush();let[d]=n.answers(i.requestId);(d?.outcome!=="declined"||d.reason!=="cancelled")&&e.fail(`the closed offer sheet was answered ${JSON.stringify(d?.outcome)} / ${JSON.stringify(d?.reason)}, not 'declined' / 'cancelled'.`);let a=e.surfaces("the parent advertises `storeOfferSheet`");for(let u of ["gem.requestStoreOfferSheet","gem.requestStorePurchase"]){let y=await n.ask(u,{skuId:i.skuId},"pressed");$(e,n,y,a,`${u} straight after the player left a sheet`);}});}}],tr=Object.freeze(er);async function Gv(r,t){if(!H(t.embedderOrigin))throw new Error(`runParentConformance: embedderOrigin must be one exact origin, got ${JSON.stringify(t.embedderOrigin)}`);let e=[];for(let n of tr)try{await n.run(r,t),e.push({id:n.id,title:n.title,ok:!0});}catch(i){e.push({id:n.id,title:n.title,ok:false,message:it(i)});}return e}o(Gv,"runParentConformance");
1
+ var at=Object.defineProperty;var i=(r,t)=>at(r,"name",{value:t,configurable:true});var c="gem-web-bridge",R={min:1,max:1},st=65536,Te=8,te=5e3,Ie=1024,P=1e4,M=Object.create(null),ne="__malformed";var ye=Object.create(null),lt=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"]]);function re(r){let t=ye[r];if(t!==void 0)return t;for(let[e,n]of lt)if(r.startsWith(e))return n}i(re,"capabilityFor");function H(r){let t=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(r);return t===null?false:t[4]===void 0||Number(t[4])<=65535}i(H,"isOrigin");function dt(r){let t;try{t=new URL(r);}catch{return false}return t.username!==""||t.password!==""?false:t.protocol==="https:"?true:t.protocol==="http:"&&(t.hostname==="localhost"||t.hostname==="127.0.0.1")}i(dt,"isSafeHttpUrl");function ct(r){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(r)}i(ct,"isBcp47");function ut(r){return /^[a-z_]+$/.test(r)}i(ut,"isCode");function mt(r){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(r)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(r)&&!r.includes("--")}i(mt,"isGameRef");function ge(r,t,e){if(t>Te)return `nesting deeper than ${Te}`;if(++e.nodes>te)return `more than ${te} values`;if(e.bytes>e.cap)return `over the ${e.cap} byte limit`;if(r==null)return e.bytes+=4,null;let n=typeof r;if(n==="boolean")return e.bytes+=5,null;if(n==="number")return e.bytes+=8,null;if(n==="string")return e.bytes+=r.length*3+2,null;if(n!=="object")return `${n} is not plain data`;let o=r;if(e.seen.has(o))return "a repeated or cyclic object reference";if(e.seen.add(o),Array.isArray(r)){if(r.length>Ie)return `an array of ${r.length} entries, over the ${Ie} limit`;e.bytes+=2;for(let a of r){let u=ge(a,t+1,e);if(u)return u}return null}let s=Object.getPrototypeOf(r);if(s!==Object.prototype&&s!==null)return "a value that is not a plain object";e.bytes+=2;let d=r;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++e.nodes>te)return `more than ${te} values`;if(e.bytes+=a.length*3+4,e.bytes>e.cap)return `over the ${e.cap} byte limit`;let u=ge(d[a],t+1,e);if(u)return u}return null}i(ge,"walk");function pt(r,t=st){let e={nodes:0,bytes:0,seen:new Set,cap:t},n=ge(r,0,e);return n?`message contains ${n}`:e.bytes>t?`message is over the ${t} byte limit`:null}i(pt,"validateSize");var gt={origin:[H,"not a well-formed origin"],url:[dt,"not a safe absolute http(s) URL"],bcp47:[ct,"not a well-formed BCP 47 tag"],code:[ut,"not a code of lower-case letters and underscores"],gameRef:[mt,"not a game id or web address"]};function Ce(r,t,e){for(let n of t){let o=e===""?n[0]:`${e}.${n[0]}`,s=r[n[0]];if(s==null){if(n[1]<"a")continue;return `${o}: required`}let d=fe(s,n,o);if(d)return d}return null}i(Ce,"checkFields");function fe(r,t,e){let n=t[1].toLowerCase();switch(n){case "u":return typeof r=="boolean"?null:fe(r,typeof r=="string"?[t[0],"s",t[2]]:[t[0],"n",t[3],t[4]],e);case "s":{if(typeof r!="string")return `${e}: expected string`;if(r.length>t[2])return `${e}: exceeds ${t[2]} characters`;if(t[3]){let[o,s]=gt[t[3]];if(!o(r))return `${e}: ${s}`}return null}case "n":case "i":return typeof r!="number"||!(n=="i"?Number.isInteger:Number.isFinite)(r)?`${e}: expected ${n=="i"?"integer":"number"}`:r<t[2]||r>t[3]?`${e}: out of range ${t[2]}..${t[3]}`:null;case "b":return typeof r=="boolean"?null:`${e}: expected boolean`;case "e":{let o=t[2];return o.includes(r)?null:`${e}: not one of ${o.join("|")}`}case "o":return typeof r!="object"||r===null||Array.isArray(r)?`${e}: expected object`:Ce(r,t[2],e);case "j":return typeof r!="object"||r===null||Array.isArray(r)?`${e}: expected a plain object`:null;case "a":{if(!Array.isArray(r))return `${e}: expected array`;if(r.length>t[2])return `${e}: exceeds ${t[2]} items`;let o=t[3],s=`${e}[]`;for(let d of r){if(d==null){if(o[1]<"a")continue;return `${s}: required`}let a=fe(d,o,s);if(a)return a}return null}default:return `${e}: unknown kind ${String(n)}`}}i(fe,"checkValue");var Oe=Object.create(null);function l(r,t,e,n,o){return M[r]=e,n&&(ye[r]=n),Oe[r]=s=>{let d=pt(s,o);if(d)return d;if(typeof s!="object"||s===null||Array.isArray(s))return "message: expected object";let a=s;return a.gem!==c?"gem: wrong protocol marker":a.type!==r?`type: expected ${r}`:Ce(a,t,"")}}i(l,"makeValidator");var m=["requestId","s",64],E=["outcome","e",["ok","declined","unavailable"]],T=["reason","S",64,"code"];l("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,["","s",64]],["embedder","s",2048,"origin"],["unbidden","B"]],30);l("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,["","s",64]]],5);l("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);l("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);l("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);l("gem.pause",[["paused","b"]],120);l("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);l("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");l("gem.earnAllowanceChanged",[],30,"earnAllowance");l("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");l("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");l("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);l("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");l("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");l("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");l("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");l("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("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],["matchId","s",64],["data","j"]],1800,"p2p");l("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],["matchId","s",64],["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");l("gem.partyResult",[m,["partyId","S",64]],30,"parties");l("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,["","s",64]]],12,"clusterTarget");l("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");l("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");l("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");l("gem.requestContext",[],12,"feedbackContext");l("gem.gameContext",[["context","j"]],12,"feedbackContext");l("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],["matchId","s",64],["data","j"]],1200,"p2p");l("gem.dedicatedOffer",[["matchId","s",64],["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");l("gem.partyRequest",[m],12,"parties");l("gem.ready",[],5);l("gem.engaged",[],5,"engagement");l("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);l("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);l("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");l("gem.localServer",[m,["serverId","S",128]],30,"localHost");l("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");l("gem.exit",[["reason","S",64]],5);l("gem.openExternal",[["url","s",2048,"url"]],6);l("gem.requestFullscreen",[["enabled","b"]],30);l("gem.audioState",[["muted","b"]],120,"audioState");l("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");l("gem.walletDisplay",[["visible","b"],["currencies","A",8,["","s",64]],["score","B"]],60,"walletDisplay");l("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");l("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");l("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");l("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");l("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");l("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");l("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");l("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");l("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");l("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");l("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");l("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");l("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);l("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");l("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");l("gem.purchaseResult",[m,E,T,["purchaseId","S",64]],30,"purchases");l("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");l("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");l("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");l("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");l("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");l("gem.momentResult",[m,["shown","b"]],12,"moments");l("gem.requestSignIn",[m],6,"signIn");l("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");l("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");l("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");l("gem.requestAppLinks",[m],12,"appLinks");l("gem.appLinksResult",[m,E,T,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");l("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");l("gem.openAppResult",[m,E,T],30,"appLinks");l("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");l("gem.openGameResult",[m,E,T],30,"openGame");l("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");l("gem.similarGamesResult",[m,E,T,["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);l("gem.requestStoreOffers",[m],12,"store");l("gem.storeOffersResult",[m,E,T,["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);l("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");l("gem.storePurchaseResult",[m,E,T],30,"store");l("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");l("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");l("gem.storeOfferSheetResult",[m,E,T],30,"storeOfferSheet");var D=Object.freeze(Oe);M.__malformed=60;Object.freeze(M);Object.freeze(ye);var yt=/^[a-z0-9][a-z0-9_-]{0,39}$/;function ht(r){return typeof r=="string"&&yt.test(r)}i(ht,"isBorderName");function he(r){if(r===null||ht(r))return r;let t=typeof r=="string"?JSON.stringify(r.length>48?`${r.slice(0,48)}\u2026`:r):`a value of type ${typeof r}`;console.warn(`[gem] showPortraitBorders: ${t} 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.`);}i(he,"checkBorderName");var I={now:i(()=>Date.now(),"now"),setTimeout:i((r,t)=>setTimeout(r,t),"setTimeout"),clearTimeout:i(r=>clearTimeout(r),"clearTimeout")},K=class{constructor(t=I){this.clock=t;}clock;static{i(this,"RateLimiter");}tokens=new Map;lastRefill=new Map;allow(t){let e=M[t];if(e===void 0)return false;let n=this.clock.now(),o=this.lastRefill.get(t)??n,s=this.tokens.get(t)??e,d=Math.max(0,n-o),a=Math.min(e,s+d*e/6e4);return a<1?(this.tokens.set(t,a),this.lastRefill.set(t,n),false):(this.tokens.set(t,a-1),this.lastRefill.set(t,n),true)}};function J(r,t){return r.toLowerCase()===t.toLowerCase()}i(J,"originsEqual");function F(r){let t=r.toLowerCase();return t==="localhost"||t.endsWith(".localhost")||t==="[::1]"||t==="::1"?true:/^127(\.\d{1,3}){3}$/.test(t)}i(F,"isLoopbackHostname");function be(r,t){if(typeof t!="string"||t==="")return false;let e,n;try{e=new URL(r),n=new URL(t);}catch{return false}return e.protocol!=="http:"&&e.protocol!=="https:"||n.protocol!=="http:"&&n.protocol!=="https:"||e.username!==""||e.password!==""||e.pathname!=="/"||e.search!==""||e.hash!==""||e.port===""?false:F(e.hostname)&&F(n.hostname)}i(be,"isLoopbackDevHostEmbedder");function Me(r,t){if(r.min>r.max||t.min>t.max)return null;let e=Math.min(r.max,t.max),n=Math.max(r.min,t.min);return e>=n?e:null}i(Me,"negotiateVersion");var $e="[gem: use gem.text.toPlain()]",Ge=new Set,Le;function bt(r){try{if(Ge.has(r))return;let t=globalThis.location;if(Le??=!!t?.port&&F(t.hostname??""),!Le)return;Ge.add(r),console.warn(`[gem] ${r} is PlayerText, turned into a string: players see ${$e}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
2
+ ${new Error().stack}`);}catch{}}i(bt,"warnStringified");var ie=class{static{i(this,"PlayerText");}#e;#t;constructor(t,e="player text"){this.#e=t,this.#t=e,Object.freeze(this);}toString(){return bt(this.#t),$e}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(t){return t.#e}};function G(r,t){return new ie(r,t)}i(G,"asPlayerText");function De(r){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(r)?r:0)))}i(De,"cuePoint");function Ne(r,t){if(r!==true)return {visible:false};let e=[],n=false;for(let o of Array.isArray(t)?t:[])o==="score"?n=true:typeof o=="string"&&o.length>0&&o.length<=64&&!e.includes(o)&&e.push(o);return !n&&e.length===0?{visible:true}:{visible:true,currencies:e.slice(0,8),score:n}}i(Ne,"walletState");function je(r){let t=[];for(let e of Array.isArray(r)?r:[]){if(t.length===4)break;if(typeof e!="object"||e===null)continue;let{id:n,label:o}=e;typeof n!="string"||n.length===0||n.length>32||typeof o!="string"||o.trim().length===0||t.some(s=>s.id===n)||t.push({id:n,label:o.trim().slice(0,40)});}return t.length===0?{settings:false}:{settings:false,items:t}}i(je,"menuState");function Fe(r,t){return Math.min(t,Math.max(0,Math.round(Number.isFinite(r)?r:0)))}i(Fe,"clampStat");function vt(r){let t=r.untrusted,e=G(t.displayName,"gem.player.displayName"),n=t.displayTag||t.displayHandle,o=n?G(n,"gem.player.displayTag"):void 0,s=r.discriminator||r.displayTag,d=s&&/^[0-9]{1,8}$/.test(s)?s:void 0,a=r.avatarIconUrl||r.avatarUrl,u=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,y=r.avatarIconId,w=r.avatar3dId,b={displayName:e,displayHandle:o??e,badges:(r.badges??[]).map(v=>({itemId:v.itemId,iconUrl:v.iconUrl}))};r.playerId&&(b.playerId=r.playerId),d&&(b.discriminator=d),o&&(b.displayTag=o),a&&(b.avatarIconUrl=b.avatarUrl=a),y&&u.test(y)&&(b.avatarIconId=y),w&&u.test(w)&&(b.avatar3dId=w),r.avatar3dModelUrl&&(b.avatar3dModelUrl=r.avatar3dModelUrl),r.avatar3dIconUrl&&(b.avatar3dIconUrl=r.avatar3dIconUrl),r.avatars3d&&(b.avatars3d=r.avatars3d.filter(v=>u.test(v.id)&&/^[a-z][a-z0-9_]*$/.test(v.family)).filter((v,ue,me)=>me.findIndex(pe=>pe.family==v.family)==ue).map(({family:v,id:ue,modelUrl:me,iconUrl:pe})=>({family:v,id:ue,modelUrl:me,iconUrl:pe})));let O=r.boost,de=(O?.multipliers??[]).map(v=>({currencyId:v.currencyId,currencySlug:v.currencySlug,multiplier:v.multiplierBp/1e4})),ce=de.some(v=>v.multiplier>1);return b.boost=ce&&O?.expiresInDays?{active:ce,multipliers:de,expiresInDays:O.expiresInDays}:{active:ce,multipliers:de},b}i(vt,"playerFromWire");var _t=1e4,wt=["appLinks","openGame","store","storeOfferSheet"],St=new WeakMap;var q=class{constructor(t){this.options=t;if(t.expectedSource===void 0||t.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=t.expectedSource,this.clock=t.clock??I,this.inbound=new K(this.clock),this.outbound=new K(this.clock);}options;static{i(this,"GemFrame");}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(t){return this.peerCapabilities.includes(t)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(t){if(t.source!==this.expectedSource)return this.refuse(t,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>J(a,t.origin))||be(t.origin,this.options.selfOrigin)||(St.get(this)??[]).some(a=>J(a,t.origin))))return this.refuse(t,`embedder '${t.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 n=D["gem.handshake"]?.(t.data);if(n)return this.refuse(t,`malformed handshake: ${n}`),false;let o=t.data;if(!J(o.embedder,t.origin))return this.refuse(t,`handshake claims embedder '${o.embedder}' but arrived from '${t.origin}'`),false;let s=t.ports?.[0];if(!s)return this.refuse(t,"handshake transferred no MessagePort"),false;let d=Me(R,o.versions);return d===null?(s.onmessage=null,s.postMessage({gem:c,type:"gem.error",code:"version_mismatch",message:`frame supports v${R.min}..v${R.max}`}),s.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=s,this.version=d,s.onmessage=a=>this.receive(a.data,a.ports??[]),s.start?.(),this.peerCapabilities=o.capabilities??[],this.unbidden=o.unbidden===true,s.postMessage({gem:c,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:c,type:"gem.ready"});}settled(t=_t){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(e=>{let n=i(s=>{this.clock.clearTimeout(o),e(s);},"waiter"),o=this.clock.setTimeout(()=>{let s=this.settleWaiters.indexOf(n);s>=0&&this.settleWaiters.splice(s,1),e(this.state);},t);this.settleWaiters.push(n);})}progress(t,e){this.send({gem:c,type:"gem.progress",loaded:t,total:e});}requestToken(t,e){this.send({gem:c,type:"gem.requestToken",requestId:t,reason:e});}requestLocalServer(t,e){return this.parentSupports("localHost")?(this.send({gem:c,type:"gem.requestLocalServer",requestId:t,gameRoomId:e}),true):false}exit(t){this.send(t===void 0?{gem:c,type:"gem.exit"}:{gem:c,type:"gem.exit",reason:t});}openExternal(t){this.send({gem:c,type:"gem.openExternal",url:t});}requestFullscreen(t){this.send({gem:c,type:"gem.requestFullscreen",enabled:t});}reportMuted(t){return this.gated("audioState")?(this.send({gem:c,type:"gem.audioState",muted:t}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:c,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(t){if(!this.gated("rewardCue"))return false;let e={gem:c,type:"gem.rewardCue",currencySlug:String(t.currencySlug).slice(0,32)};return t.expected!==void 0&&Number.isFinite(t.expected)&&t.expected>=1&&(e.expected=Math.min(1e9,Math.round(t.expected))),t.origin&&(e.origin={x:De(t.origin.x),y:De(t.origin.y)}),this.send(e),true}showPortraitBorders(t){let e=he(t);return e===void 0||!this.gated("portraitBorders")?false:this.send(e===null?{gem:c,type:"gem.portraitBorders"}:{gem:c,type:"gem.portraitBorders",name:e})}setWalletVisible(t,e){return this.gated("walletDisplay")?this.send({gem:c,type:"gem.walletDisplay",...Ne(t,e?.only)}):false}setMenu(t){return this.gated("gameMenu")?this.send({gem:c,type:"gem.menu",...je(t)}):false}declareSettings(t){return this.gated("gameSettings")?this.send({gem:c,type:"gem.settingsDeclare",settings:t}):false}setGemCorner(t){return this.gated("gemCorner")?this.send({gem:c,type:"gem.gemCorner",corner:t}):false}setMenuStyle(t){return this.gated("menuStyle")?this.send(t===null?{gem:c,type:"gem.menuStyle"}:{gem:c,type:"gem.menuStyle",ground:t.ground,accent:t.accent}):false}requestShare(t){return this.gated("share")?this.send({gem:c,type:"gem.requestShare",params:t.map(({key:e,value:n})=>({key:e,value:n}))}):false}requestAvatarPicker(t){return this.gated("avatarPicker")?this.send({gem:c,type:"gem.requestAvatarPicker",...t==="2d"||t==="3d"?{tab:t}:{}}):false}requestVibrate(t){return this.gated("vibration")?this.send({gem:c,type:"gem.vibrate",pattern:[...t]}):false}reportFrameStats(t){return this.gated("frameStats")?(this.send({gem:c,type:"gem.frameStats",fps:Fe(t.fps,1e3),frameTimeP95Ms:Fe(t.frameTimeP95Ms,1e5),counts:t.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(t){return this.state!=="connected"?false:(this.send({gem:c,type:"gem.gameContext",context:t}),true)}signal(t,e,n){this.send({gem:c,type:"gem.signal",signal:t,matchId:e,data:n});}dedicatedOffer(t,e,n){this.send({gem:c,type:"gem.dedicatedOffer",matchId:t,sdp:e,intent:n});}requestParty(t){return this.send({gem:c,type:"gem.partyRequest",requestId:t})}requestPurchase(t,e,n,o,s,d){return this.send({gem:c,type:"gem.requestPurchase",requestId:t,purchaseKind:e,targetId:n,currencySlug:o,...s===void 0?{}:{quantity:s},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(t,e,n){return this.parentSupports("avatar3dPurchases")?this.send({gem:c,type:"gem.requestAvatar3dPurchase",requestId:t,itemId:e,currencySlug:n}):false}putDeviceSettings(t,e){return this.send({gem:c,type:"gem.deviceSettings.put",requestId:t,settings:e})}requestAd(t,e,n){return this.send({gem:c,type:"gem.requestAd",requestId:t,placement:e,format:n})}signalMoment(t,e,n,o){return this.send({gem:c,type:"gem.signalMoment",requestId:t,kind:e,...n===void 0?{}:{placement:n},...o===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(t){return this.send({gem:c,type:"gem.requestSignIn",requestId:t})}requestFriendChallenge(t,e,n,o){return this.send({gem:c,type:"gem.requestFriendChallenge",requestId:t,playerId:e,template:n,...o===void 0?{}:{params:o}})}requestArcade(t){return this.gated(re(t.type))&&this.send({gem:c,...t})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(t){if(this.state!==t){this.state=t;for(let e of this.settleWaiters.splice(0))e(t);this.options.onStateChange?.(t);}}refuseMalformed(t,e){if(!this.inbound.allow(ne)){this.options.onDropped?.(t,e);return}this.fail(t,e);}dropUnknownType(t){this.inbound.allow(ne)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${t}'`);}refuse(t,e){t.ports?.[0]?.close?.(),this.inbound.allow(ne)&&this.options.onRefused?.(e);}fail(t,e){this.options.onError?.(t,e),this.state==="connected"&&this.port&&this.send({gem:c,type:"gem.error",code:t,...e===void 0?{}:{message:e}});}gated(t){return this.state==="connected"&&this.parentSupports(t)}send(t){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(t.type)} before connect`),false;let e=t.type;if(!this.outbound.allow(e))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${e}`),false;let n=D[e]?.(t);return n?(this.options.onError?.("internal",`refusing to send malformed ${e}: ${n}`),false):(this.port.postMessage(this.version===null?t:{...t,v:this.version}),true)}receive(t,e=[]){let n=false;try{n=this.dispatchInbound(t,e);}finally{n||qe(e);}}dispatchInbound(t,e){if(this.state!=="connected")return false;let n=typeof t=="object"&&t!==null?t.type:void 0;if(typeof n!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let o=D[n];if(!o)return this.dropUnknownType(n),false;if(!this.inbound.allow(n))return this.fail("rate_limited",`parent exceeded the limit for ${n}`),false;let s=re(n);if(s!==void 0&&!this.options.capabilities?.includes(s))return this.fail("unsupported_capability",`${n} requires the '${s}' capability, which this frame did not advertise`),false;let d=o(t);if(d)return this.fail("invalid_message",`${n}: ${d}`),false;let a=t;if(a.v!==this.version)return this.fail("invalid_message",`${n}: expected v ${String(this.version)}`),false;switch(n){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.settingChange":this.options.onSettingChange?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let u=e[0];if(!u){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let y={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){u.close(),this.screenshotDenied("not_wired",false);break}return qe(e.slice(1)),this.options.onScreenshot(y,u,(w,b)=>this.screenshotDenied(w,b)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(vt(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:if(wt.includes(re(n))){this.options.onArcadeAnswer?.(a);break}this.fail("unsupported",`no frame handler for '${n}'`);}return false}screenshotDenied(t,e){this.send({gem:c,type:"gem.screenshot.denied",reason:t,retryable:e});}};function qe(r){for(let t of r)try{t?.close?.();}catch{}}i(qe,"closeAll");globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:18.1.1]";var Be=["if-match","if-none-match"];var xt={unknown_currency:1,currency_not_in_game_payable:1,not_purchasable:1,no_price_in_currency:1,busy:1,cancelled:1,insufficient_funds:1,price_mismatch:1,config_version_stale:1,platform_currency_unavailable:1,not_available:1,recipe_locked:1,already_owned:1,recipe_on_cooldown:1,recipe_max_uses_exceeded:1,item_max_stack_exceeded:1,recipe_costs_unmet:1,quantity_invalid:1,error:1};new Set(Object.keys(xt));new Set(Be);new TextEncoder;var We=["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"];new Set(We);new TextEncoder;new TextDecoder;new TextEncoder;var rn=8;(class{constructor(t=rn){this.perMinute=t;}perMinute;static{i(this,"CredentialPacing");}sent=[];delayUntilAllowed(t){return this.prune(t),this.sent.length<this.perMinute?0:Math.max(0,(this.sent[0]??t)+6e4-t)}record(t){this.prune(t),this.sent.push(t);}prune(t){let e=0;for(let n of this.sent)n<=t&&t-n<6e4&&(this.sent[e++]=n);this.sent.length=e;}});function p(r){return t=>typeof t=="string"&&t.length>0&&t.length<=r?t:void 0}i(p,"text");function C(r,t){return e=>typeof e=="number"&&Number.isInteger(e)&&e>=r&&e<=t?e:void 0}i(C,"integer");function j(...r){return t=>typeof t=="string"&&r.includes(t)?t:void 0}i(j,"oneOf");function He(r){return typeof r=="boolean"?r:void 0}i(He,"flag");var _=p(64),Q=C(0,2147483647);function Re(r){if(!Array.isArray(r))return;let t=[];for(let e of r){if(t.length>=64)break;let n=_(e);typeof n=="string"&&t.push(n);}return t}i(Re,"ids");function f(r,t,e){return {to:r,from:t,read:e,required:true}}i(f,"required");function g(r,t,e){return {to:r,from:t,read:e,required:false}}i(g,"optional");var sn=i(r=>{if(typeof r!="object"||r===null||Array.isArray(r))return;let t=r,e=j("disconnected","failed","closed")(t.iceState),n=C(0,4102444800)(t.lastSeenAt);if(!(e===void 0&&n===void 0))return {...e===void 0?{}:{ice_state:e},...n===void 0?{}:{last_seen_timestamp:n}}},"evidence");Object.freeze(Object.assign(Object.create(null),{join:{type:"p2p_join",gameScoped:true,args:[]},offer:{type:"p2p_offer",gameScoped:false,args:[f("sdp","sdp",p(10240)),f("signaling_intent","intent",j("initial","ice_restart","full_reconnect")),g("sdp_hash","sdpHash",p(128))]},answer:{type:"p2p_answer",gameScoped:false,args:[f("peer_id","peerId",_),f("sdp","sdp",p(10240)),f("assigned_peer_id","assignedPeerId",C(0,4294967295)),g("sdp_hash","sdpHash",p(128))]},ice:{type:"p2p_ice_candidate",gameScoped:false,args:[f("peer_id","peerId",_),f("candidate","candidate",p(512)),g("sdp_mid","sdpMid",p(64)),g("sdp_mline_index","sdpMLineIndex",C(0,255))]},migrationAck:{type:"p2p_migration_ack",gameScoped:false,args:[f("host_epoch","hostEpoch",Q)]},hostUnreachable:{type:"p2p_report_host_unreachable",gameScoped:false,args:[f("host_epoch","hostEpoch",Q),g("evidence","evidence",sn)]}}));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"}));Object.freeze(Object.assign(Object.create(null),{hello:[f("playerId","player_id",_),f("hostPlayerId","host_player_id",_),f("hostEpoch","host_epoch",Q),g("members","members",Re)],offer:[f("fromPlayerId","from_player_id",_),f("sdp","sdp",p(10240)),g("sessionId","signaling_session_id",p(128)),g("sdpHash","sdp_hash",p(128)),g("iceUfrag","ice_ufrag",p(64)),g("intent","signaling_intent",j("initial","ice_restart","full_reconnect"))],answer:[f("fromPlayerId","from_player_id",_),f("sdp","sdp",p(10240)),g("sessionId","signaling_session_id",p(128)),g("sdpHash","sdp_hash",p(128)),g("iceUfrag","ice_ufrag",p(64)),g("assignedPeerId","assigned_peer_id",C(0,4294967295))],ice:[f("fromPlayerId","from_player_id",_),f("candidate","candidate",p(512)),g("sdpMid","sdp_mid",p(64)),g("sdpMLineIndex","sdp_m_line_index",C(0,255)),g("sdpMLineIndexAlt","sdp_mline_index",C(0,255))],peerJoined:[f("playerId","player_id",_)],peerLeft:[f("playerId","player_id",_),g("reason","reason",p(64))],migration:[f("hostPlayerId","new_host_player_id",_),f("hostEpoch","host_epoch",Q),g("reason","reason",p(64)),g("kickedPlayerIds","kicked_player_ids",Re),g("migrationComplete","migration_complete",He)],matchEnded:[g("reason","reason",p(64))],error:[g("code","code",p(64)),g("message","message",p(256))]}));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"}));Object.freeze(Object.assign(Object.create(null),{webrtc_sdp_answer:"answer",webrtc_error:"error",match_ended:"matchEnded"}));Object.freeze(Object.assign(Object.create(null),{answer:[f("sdp","sdp",p(10240)),g("sessionId","session_id",p(64))],error:[g("code","code",p(64)),g("message","message",p(256))],matchEnded:[g("reason","reason",p(64))]}));var nt=Object.freeze(["https://gemarcade.com","https://developer.gemarcade.com","http://gemarcade.localhost","gemarcade://localhost"]);var V=class extends Error{constructor(e,n){super(`[${e}] ${n}`);this.caseId=e;this.name="ParentConformanceError";}caseId;static{i(this,"ParentConformanceError");}},Mv=3,rt="https://game.conformance.invalid",Xn="https://stranger.conformance.invalid",Ae=M["gem.handshake"]??1,ee=6e4,ae=class{static{i(this,"KitClock");}current=1e6;seq=0;pending=new Map;now(){return this.current}setTimeout(t,e){let n=++this.seq;return this.pending.set(n,{at:this.current+Math.max(0,e),fn:t}),n}clearTimeout(t){this.pending.delete(t);}get pendingTimers(){return this.pending.size}advance(t){let e=this.current+t;for(;;){let n;for(let o of this.pending)o[1].at<=e&&(n===void 0||o[1].at<n[1].at)&&(n=o);if(n===void 0)break;this.pending.delete(n[0]),this.current=n[1].at,n[1].fn();}this.current=e;}},it=Object.freeze({conformance:"parent-window"}),se=class{constructor(t,e,n,o){this.caseId=t;this.options=n;let s=o??n.frameOrigin??rt,d={frame:{postMessage:i((a,u,y)=>{this.offers.push({data:a,targetOrigin:u,transfer:y??[],at:this.clock.now()});},"postMessage")},frameOrigin:s,embedderOrigin:n.embedderOrigin,clock:this.clock,makeChannel:i(()=>{let a=new MessageChannel,u={port1:a.port1,port2:a.port2,closed:false},y=a.port1.close.bind(a.port1);return a.port1.close=()=>{u.closed=true,y();},this.count(a.port1),this.count(a.port2),this.minted.push(u),a},"makeChannel"),reportError:i((a,u)=>{this.errors.push(u===void 0?{code:a}:{code:a,message:u});},"reportError")};Object.defineProperty(d,"userActivation",{enumerable:true,get:i(()=>this.activation===void 0?void 0:{...this.activation},"get")}),this.parent=e(d);}caseId;options;static{i(this,"Rig");}clock=new ae;frameClock=new ae;offers=[];minted=[];parent;activation={isActive:false};errors=[];inFlight=0;frames=[];get frameOrigin(){return this.options.frameOrigin??rt}fail(t){throw new V(this.caseId,t)}count(t){let e=t.postMessage.bind(t);t.postMessage=((...n)=>{this.inFlight+=1,e(...n);}),t.addEventListener("message",()=>{this.inFlight-=1;});}async flush(){let t=i(()=>new Promise(e=>setTimeout(e,0)),"turn");await t(),await t();for(let e=0;this.inFlight>0&&e<200;e++)await t();}advance(t,e=250){for(let n=0;n<t;n+=e){let o=Math.min(e,t-n);this.clock.advance(o),this.frameClock.advance(o);}}start(){try{this.parent.start();}catch(t){this.fail(`start() threw: ${ot(t)}`);}this.offers.length===0&&this.fail("start() posted no handshake to the frame window. The offer must be posted at start, on the window the fixture names as `frame`.");}latest(){let t=this.offers[this.offers.length-1];return t===void 0&&this.fail("no handshake has been offered"),t}transferred(t){t.transfer.length!==1&&this.fail(`an offer transferred ${t.transfer.length} objects; it must transfer exactly one MessagePort.`);let e=this.minted.find(n=>n.port2===t.transfer[0]);return e===void 0&&this.fail("an offer transferred a port the fixture did not mint. Take every offer\u2019s channel from `fixture.makeChannel`, so the kit can see which ports the parent closes."),e}frame(t){let e=t??[...new Set([...nt,...this.options.allowedEmbedders??[]])],n=[],o=new q({allowedEmbedders:e,expectedSource:it,selfOrigin:this.frameOrigin,clock:this.frameClock,onRefused:i(s=>n.push(s),"onRefused")});return o.kitRefusals=n,this.frames.push(o),o}deliver(t,e){let n=this.transferred(e);return t.handleWindowMessage({data:e.data,origin:this.options.embedderOrigin,source:it,ports:[n.port2]})}answer(t,e,n=[]){this.transferred(t).port2.postMessage({gem:c,type:"gem.handshake.accept",v:e,capabilities:[...n]});}advertised(){let t=this.latest().data.capabilities;return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}async raw(t){let e=this.latest(),n=this.transferred(e),o=[];n.port2.onmessage=a=>{a.data!==null&&typeof a.data=="object"&&o.push(a.data);};let s=Math.min(W(this).max,R.max);this.answer(e,s,t),await this.flush(),this.state()!=="connected"&&this.fail(`a hand-written frame accepted at v${s}, and the parent reports '${this.state()}'.`);let d=0;return {received:o,ask:i(async(a,u,y)=>{let w=`conformance-${++d}`;return this.activation=y==="missing"?void 0:{isActive:y==="pressed"},n.port2.postMessage({gem:c,type:a,v:s,requestId:w,...u}),await this.flush(),this.activation={isActive:false},w},"ask"),answers:i(a=>o.filter(u=>u.requestId===a),"answers")}}surfaces(t){let e=this.parent.surfacesShown;return typeof e!="number"&&this.fail(`${t}, so the kit must see what it shows: expose \`surfacesShown\` on the adapter (every checkout, card, QR code, toast or tab the parent opened for a game).`),e}offersInAnyMinute(){let t=0;for(let e=0;e<this.offers.length;e++){let n=this.offers[e].at,o=0;for(let s=e;s<this.offers.length&&this.offers[s].at<n+ee;s++)o++;t=Math.max(t,o);}return t}state(){return this.parent.state}dispose(){try{this.parent.close();}catch{}for(let t of this.frames)t.close();for(let t of this.minted)try{t.port1.close(),t.port2.close();}catch{}}};function ot(r){return r instanceof Error?r.message:String(r)}i(ot,"describe");function Yn(r){return r.kitRefusals??[]}i(Yn,"refusalsOf");function W(r){let t=r.latest().data.versions,e=t?.min,n=t?.max;return (typeof e!="number"||typeof n!="number")&&r.fail("the handshake carries no numeric `versions.min` / `versions.max`"),{min:e,max:n}}i(W,"offeredRange");async function h(r,t,e,n){let o=new se(r,t,e);try{await n(o);}finally{o.dispose();}}i(h,"withRig");async function Z(r){let t=r.frame();return r.deliver(t,r.latest())||r.fail(`a game built with this SDK refused the handshake from ${r.options.embedderOrigin}: ${Yn(t).join("; ")||"no reason recorded"}. A production origin the SDK does not carry needs an SDK that lists it; a non-production one goes in \`allowedEmbedders\` here, as a game would name it.`),await r.flush(),r.state()!=="connected"&&r.fail(`the frame accepted, but the parent reports '${r.state()}' rather than 'connected'.`),t}i(Z,"connect");var x=["appLinks","openGame","store","storeOfferSheet"],Qn="00000000-0000-4000-8000-00000000c0de",le="conformance-unknown-sku",xe={appLinks:{type:"gem.requestOpenApp",fields:{target:"arcade"}},openGame:{type:"gem.requestOpenGame",fields:{ref:Qn}},store:{type:"gem.requestStorePurchase",fields:{skuId:le}},storeOfferSheet:{type:"gem.requestStoreOfferSheet",fields:{skuId:le}}};function $(r,t,e,n,o){let s=t.answers(e);s.length!==1&&r.fail(`${o}: the parent sent ${s.length} answers; it owes exactly one.`);let[d]=s;(d.outcome!=="declined"||d.reason!=="busy")&&r.fail(`${o}: answered ${JSON.stringify(d.outcome)} / ${JSON.stringify(d.reason)}, not 'declined' / 'busy'.`);let a=r.surfaces(o);a!==n&&r.fail(`${o}: the parent showed ${a-n} surface(s); it must show nothing.`);}i($,"expectBusy");async function Pe(r,t,e="gem.requestStorePurchase"){let n=await t.ask("gem.requestStoreOffers",{},"unpressed"),[o]=t.answers(n),d=(o?.offers??[])[0]?.skuId;(o?.outcome!=="ok"||typeof d!="string")&&r.fail(`the parent advertises \`store\` but listed no offer to a signed-in player (${JSON.stringify(o?.outcome)} / ${JSON.stringify(o?.reason)}). Give the adapter's store a signed-in player and at least one SKU, so the kit can open a checkout.`);let a=r.surfaces("the parent advertises `store`"),u=await t.ask(e,{skuId:d},"pressed"),y=r.surfaces("the parent advertises `store`");return y!==a+1&&r.fail(`a pressed ${e} of the listed SKU ${JSON.stringify(d)} showed ${y-a} surface(s); it must open exactly one.`),t.answers(u).length>0&&r.fail(`${e} was answered while what it opened was still open. Answer once the player has acted, so the kit can hold it open.`),{requestId:u,skuId:d,shown:y}}i(Pe,"openStoreCheckout");function Ee(r){typeof r.parent.dismiss!="function"&&r.fail("the parent advertises `store`, so the kit must be able to close its checkout: expose `dismiss()` on the adapter (the player closing the checkout without paying)."),r.parent.dismiss();}i(Ee,"dismiss");var Zn=["*","null","","https://game.conformance.invalid/","https://game.conformance.invalid/play"],er=[{id:"origin.refuses-imprecise-target",title:"refuses a frame origin that is not one exact origin, before anything is posted",async run(r,t){for(let e of Zn){let n=JSON.stringify(e),o,s=false;try{o=new se(this.id,r,{...t,frameOrigin:e},e),o.parent.start();}catch(d){if(d instanceof V)throw d;s=true;}try{if((o?.offers??[]).some(a=>a.targetOrigin==="*"||a.targetOrigin===e))throw new V(this.id,`with frame origin ${n} the parent posted the handshake anyway \u2014 the offer transfers the session port, so an imprecise target hands the session to whatever the frame has navigated to.`);if(!s)throw new V(this.id,`a frame origin of ${n} was accepted. Refuse it at construction or at start(), by throwing.`)}finally{o?.dispose();}}}},{id:"handshake.targets-frame-origin",title:"posts a valid handshake to exactly the frame origin, naming the page\u2019s own origin",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(P);for(let s of e.offers){s.targetOrigin!==e.frameOrigin&&e.fail(`an offer was posted with targetOrigin ${JSON.stringify(s.targetOrigin)}, not the frame origin ${e.frameOrigin}.`),e.transferred(s);let d=D["gem.handshake"]?.(s.data);d&&e.fail(`the handshake does not validate against the schema: ${d}`);let a=s.data;a.gem!==c&&e.fail(`the handshake's \`gem\` is not ${c}`),a.embedder!==t.embedderOrigin&&e.fail(`the handshake names embedder ${JSON.stringify(a.embedder)}, but the page is served from ${t.embedderOrigin}. Every game compares the two and refuses a mismatch: name the origin the page is actually served from.`);}let{min:n,max:o}=W(e);n>o&&e.fail(`the offered range is empty: min ${n} > max ${o}`),(o<R.min||n>R.max)&&e.fail(`the offered range v${n}\u2013v${o} shares no version with this SDK (v${R.min}\u2013v${R.max}).`);});}},{id:"handshake.default-game-connects",title:"connects to a game that names no embedders, at the highest common version",async run(r,t){await h(this.id,r,t,async e=>{e.start();let n=await Z(e),{max:o}=W(e),s=Math.min(o,R.max);n.protocolVersion!==s&&e.fail(`the frame chose v${String(n.protocolVersion)}, expected v${s}`),e.parent.protocolVersion!==n.protocolVersion&&e.fail(`the parent reports v${String(e.parent.protocolVersion)} while the frame chose v${String(n.protocolVersion)}. Adopt the version on the accept.`);});}},{id:"handshake.refusal-is-not-a-verdict",title:"keeps offering, still handshaking, when a game refuses this page",async run(r,t){await h(this.id,r,t,async e=>{e.start();let n=e.frame([Xn]);e.deliver(n,e.latest())&&e.fail("a frame that does not allow this page accepted it \u2014 the kit is misconfigured"),await e.flush();let o=e.offers.length;e.advance(P*3),await e.flush(),e.state()!=="handshaking"&&e.fail(`after a refusal and ${P*3}ms the parent reports '${e.state()}'. A refusing game answers nothing, by design, so silence is not evidence: stay handshaking until the page closes the bridge.`),e.offers.length<=o&&e.fail("the parent stopped offering after a refusal; keep offering on the cadence.");});}},{id:"version.adopts-frame-choice",title:"adopts the version the frame chose from inside the offered range",async run(r,t){await h(this.id,r,t,async e=>{e.start();let{min:n}=W(e);e.answer(e.latest(),n),await e.flush(),(e.state()!=="connected"||e.parent.protocolVersion!==n)&&e.fail(`answered with v${n}, the bottom of the offered range, the parent reports '${e.state()}' at v${String(e.parent.protocolVersion)}.`);});}},{id:"version.refuses-out-of-range",title:"never connects at a version outside the range it offered",async run(r,t){for(let e of ["above","below"])await h(this.id,r,t,async n=>{n.start();let{min:o,max:s}=W(n),d=e==="above"?s+1:o-1;n.answer(n.latest(),d),await n.flush(),(n.state()==="connected"||n.parent.protocolVersion===d)&&n.fail(`answered with v${d}, outside the offered v${o}\u2013v${s}, the parent connected. A frame choosing a version this parent never offered is not a session.`);});}},{id:"lifecycle.reoffers-on-fresh-ports",title:"re-offers on a fresh port while unanswered, inside the contract\u2019s budget",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(P*2),e.offers.length<2&&e.fail(`one offer in ${P*2}ms. A message to a document that is not listening yet is lost, not queued: re-offer until something answers.`);let n=e.offers.map(s=>e.transferred(s).port2);new Set(n).size!==n.length&&e.fail("an offer re-used a port; each offer needs its own channel."),e.advance(ee*2);let o=e.offersInAnyMinute();o>Ae&&e.fail(`${o} offers inside one minute; the contract budgets ${Ae} \`gem.handshake\` per minute.`);});}},{id:"lifecycle.late-answer-adopts-and-closes-the-rest",title:"adopts the offer a late frame answers, and closes every other offered port",async run(r,t){await h(this.id,r,t,async e=>{e.start(),e.advance(P*2);let n=e.transferred(e.latest());await Z(e),n.closed&&e.fail("the parent closed the port the frame answered on.");let o=e.minted.filter(s=>s!==n&&!s.closed).length;o>0&&e.fail(`${o} unanswered offer port(s) still open after connecting. Close every other offer when one is answered.`);});}},{id:"lifecycle.no-offers-after-connect",title:"stops offering once connected",async run(r,t){await h(this.id,r,t,async e=>{e.start(),await Z(e);let n=e.offers.length;e.advance(ee*5,1e3),e.offers.length!==n&&e.fail(`${e.offers.length-n} offer(s) after the session connected.`);});}},{id:"lifecycle.duplicate-accept-keeps-session",title:"keeps the session when a second accept arrives, even an out-of-range one",async run(r,t){await h(this.id,r,t,async e=>{e.start(),await Z(e);let n=e.parent.protocolVersion;e.answer(e.latest(),W(e).max+1),await e.flush(),(e.state()!=="connected"||e.parent.protocolVersion!==n)&&e.fail(`a second accept moved the parent to '${e.state()}' at v${String(e.parent.protocolVersion)}. One answer settles the handshake.`);});}},{id:"lifecycle.close-releases-everything",title:"on close, releases every port and timer and offers nothing more",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(P),e.parent.close(),e.state()!=="closed"&&e.fail(`after close() the parent reports '${e.state()}'`);let n=e.minted.filter(s=>!s.closed).length;n>0&&e.fail(`${n} offer port(s) left open after close().`),e.clock.pendingTimers>0&&e.fail(`${e.clock.pendingTimers} timer(s) still armed after close().`);let o=e.offers.length;e.advance(ee*2,1e3),e.offers.length!==o&&e.fail("the parent offered again after close().");}),await h(this.id,r,t,async e=>{e.start(),await Z(e),e.parent.close(),e.state()!=="closed"&&e.fail(`after close() the parent reports '${e.state()}'`),e.minted.filter(o=>!o.closed).length>0&&e.fail("the session port is still open after close().");});}},{id:"lifecycle.unanswered-offers-are-bounded",title:"holds at most about a minute of unanswered offer ports, however long it runs",async run(r,t){await h(this.id,r,t,e=>{e.start(),e.advance(ee*10,1e3);let n=e.minted.filter(o=>!o.closed).length;n>Ae+1&&e.fail(`${n} unanswered offer ports open after ten minutes. Close offers nothing answered within a grace (a minute is the reference), or the loop leaks a channel per tick for as long as the page holds the frame.`);});}},{id:"capability.unadvertised-is-refused",title:"refuses an arcade-feature or store ask under a capability either side did not advertise, answering nothing and showing nothing",async run(r,t){for(let e of x)await h(this.id,r,t,async n=>{n.start();let o=n.advertised().includes(e),s=x.filter(O=>!o||O!==e),d=await n.raw(s),a=n.parent.surfacesShown,{type:u,fields:y}=xe[e],w=await d.ask(u,y,"pressed"),b=o?"the frame":"the parent";d.answers(w).length>0&&n.fail(`${u} under '${e}', which ${b} did not advertise, was answered. Refuse it before anything else and send nothing back.`),n.parent.surfacesShown!==a&&n.fail(`${u} under unadvertised '${e}' showed a surface.`),n.errors.some(O=>O.code==="unsupported_capability")||n.fail(`${u} under '${e}', which ${b} did not advertise, was not reported as 'unsupported_capability'. Report it through \`fixture.reportError\` (what GemHost calls onError).`);});}},{id:"acting.refuses-without-activation",title:"answers an acting ask with no press \u2014 or no activation API \u2014 declined / busy, showing nothing",async run(r,t){for(let e of x)for(let n of ["unpressed","missing"])await h(this.id,r,t,async o=>{if(o.start(),!o.advertised().includes(e))return;let s=await o.raw(x),d=o.surfaces(`the parent advertises '${e}'`),{type:a,fields:u}=xe[e],y=await s.ask(a,u,n);$(o,s,y,d,n==="missing"?`${a} with no userActivation API (it must fail closed)`:`${a} with no press`);});}},{id:"store.unknown-sku-is-unavailable",title:"answers a store purchase of a SKU it does not list unavailable, showing nothing",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("store"))return;let n=await e.raw(x),o=e.surfaces("the parent advertises `store`"),s=await n.ask("gem.requestStorePurchase",{skuId:le},"pressed"),d=n.answers(s);(d.length!==1||d[0].outcome!=="unavailable")&&e.fail(`a pressed purchase of an unknown SKU was answered ${JSON.stringify(d)}; it owes exactly one 'unavailable'.`),e.surfaces("the parent advertises `store`")!==o&&e.fail("a purchase of an unknown SKU showed a surface; it must show nothing.");});}},{id:"acting.second-ask-is-busy",title:"answers any acting ask declined / busy, showing nothing, while a store checkout is open",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("store"))return;let n=await e.raw(x),o=await Pe(e,n),s=await n.ask("gem.requestStorePurchase",{skuId:o.skuId},"pressed");$(e,n,s,o.shown,"a second store purchase while one is open");for(let a of ["appLinks","openGame","storeOfferSheet"]){if(!e.advertised().includes(a))continue;let{type:u,fields:y}=xe[a],w=await n.ask(u,y,"pressed");$(e,n,w,o.shown,`${u} while a store checkout is open`);}Ee(e),await e.flush();let[d]=n.answers(o.requestId);(d?.outcome!=="declined"||d.reason!=="cancelled")&&e.fail(`the closed checkout was answered ${JSON.stringify(d?.outcome)} / ${JSON.stringify(d?.reason)}, not 'declined' / 'cancelled'.`);});}},{id:"store.backs-off-after-cancel",title:"answers the next store purchase declined / busy, showing nothing, after a dismissal",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("store"))return;let n=await e.raw(x),o=await Pe(e,n);Ee(e),await e.flush();let s=e.surfaces("the parent advertises `store`"),d=await n.ask("gem.requestStorePurchase",{skuId:o.skuId},"pressed");if($(e,n,d,s,"a store purchase straight after the player dismissed one"),!e.advertised().includes("storeOfferSheet"))return;let a=await n.ask("gem.requestStoreOfferSheet",{skuId:o.skuId},"pressed");$(e,n,a,s,"an offer sheet straight after the player dismissed a checkout");});}},{id:"sheet.needs-store",title:"advertises storeOfferSheet only beside store",async run(r,t){await h(this.id,r,t,async e=>{e.start();let n=e.advertised();n.includes("storeOfferSheet")&&!n.includes("store")&&e.fail("the parent advertises `storeOfferSheet` without `store`. A game takes its SKU from gem.storeOffersResult and hears a pending purchase settle through gem.storePurchaseSettled, and both are gated on `store`.");});}},{id:"sheet.unknown-sku-is-unavailable",title:"answers an offer sheet for a SKU it does not list unavailable, showing nothing",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("storeOfferSheet"))return;let n=await e.raw(x),o=e.surfaces("the parent advertises `storeOfferSheet`"),s=await n.ask("gem.requestStoreOfferSheet",{skuId:le},"pressed"),d=n.answers(s);(d.length!==1||d[0].outcome!=="unavailable")&&e.fail(`a pressed offer sheet for an unknown SKU was answered ${JSON.stringify(d)}; it owes exactly one 'unavailable'.`),e.surfaces("the parent advertises `storeOfferSheet`")!==o&&e.fail("an offer sheet for an unknown SKU showed a surface; it must show nothing.");});}},{id:"sheet.shares-the-slot-and-the-back-off",title:"holds the acting slot while an offer sheet is open, and backs both store asks off after the player leaves it",async run(r,t){await h(this.id,r,t,async e=>{if(e.start(),!e.advertised().includes("storeOfferSheet"))return;let n=await e.raw(x),o=await Pe(e,n,"gem.requestStoreOfferSheet"),s=await n.ask("gem.requestStorePurchase",{skuId:o.skuId},"pressed");$(e,n,s,o.shown,"a store purchase while an offer sheet is open"),Ee(e),await e.flush();let[d]=n.answers(o.requestId);(d?.outcome!=="declined"||d.reason!=="cancelled")&&e.fail(`the closed offer sheet was answered ${JSON.stringify(d?.outcome)} / ${JSON.stringify(d?.reason)}, not 'declined' / 'cancelled'.`);let a=e.surfaces("the parent advertises `storeOfferSheet`");for(let u of ["gem.requestStoreOfferSheet","gem.requestStorePurchase"]){let y=await n.ask(u,{skuId:o.skuId},"pressed");$(e,n,y,a,`${u} straight after the player left a sheet`);}});}}],tr=Object.freeze(er);async function Gv(r,t){if(!H(t.embedderOrigin))throw new Error(`runParentConformance: embedderOrigin must be one exact origin, got ${JSON.stringify(t.embedderOrigin)}`);let e=[];for(let n of tr)try{await n.run(r,t),e.push({id:n.id,title:n.title,ok:!0});}catch(o){e.push({id:n.id,title:n.title,ok:false,message:ot(o)});}return e}i(Gv,"runParentConformance");
3
3
  export{tr as PARENT_CONFORMANCE_CASES,Mv as PARENT_CONFORMANCE_VERSION,V as ParentConformanceError,Gv as runParentConformance};//# sourceMappingURL=gem-web-sdk.conformance.js.map
4
4
  //# sourceMappingURL=gem-web-sdk.conformance.js.map