@indiegems/gem-web-sdk 15.0.0 → 15.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.
@@ -1,3 +1,3 @@
1
- var Ee=Object.defineProperty;var Ie=(n,e)=>{for(var t in e)Ee(n,t,{get:e[t],enumerable:true});};globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:15.0.0]";var Me={initialDelayMs:2e3,maxDelayMs:6e4};function Le(n,e){return e!==void 0?!e:n==="chain_exhausted"}var B=class{constructor(e){this.options=e;}options;grant=null;state="pending";failure=null;generation=0;inFlight=null;rejected=false;retry=null;failures=0;notBefore=0;disposed=false;get currentState(){return this.state}get lastFailure(){return this.failure}async get(){if(this.state==="lost")throw new b(this.failure??"unavailable","the session cannot be renewed");let e=this.seconds();if(this.grant&&e<this.grant.expiresAt-this.renewBefore())return this.grant.token;if(this.pastChainCeiling())throw new b("chain_exhausted","the token chain reached its ceiling");if(this.inFlight===null&&this.options.clock.now()<this.notBefore)throw new b(this.failure??"unavailable","the embedder asked for no new token yet; one is asked for when that wait is over");return this.refresh(this.nextReason())}pastChainCeiling(){return this.grant===null||this.seconds()<this.grant.chainEndsAt?false:(this.enter("lost","chain_exhausted"),true)}nextReason(){return this.rejected?"rejected":this.grant?"expiring":"initial"}invalidate(e){e!==void 0&&this.grant!==null&&this.grant.token!==e||this.rejected&&this.grant===null||(this.grant=null,this.generation+=1,this.inFlight=null,this.rejected=true,this.state!=="lost"&&this.enter("pending"));}accept(e){this.state!=="lost"&&(this.grant=e,this.rejected=false,this.enter("ready"));}close(e="unavailable"){this.grant=null,this.generation+=1,this.inFlight=null,this.enter("lost",e);}dispose(){this.disposed=true,this.cancelRetry();}scheduleRetry(e){if(this.cancelRetry(),this.failures+=1,this.notBefore=e===void 0?0:this.options.clock.now()+e,this.disposed)return;let{initialDelayMs:t,maxDelayMs:r}=Me,o=Math.min(r,t*2**(this.failures-1)),a=Math.max(o/2,e??0)+Math.random()*(o/2);this.retry={handle:this.options.clock.setTimeout(()=>this.retryNow(),a)};}retryNow(){this.retry=null,this.notBefore=0,!(this.state==="lost"||this.pastChainCeiling())&&this.refresh(this.nextReason()).catch(()=>{});}cancelRetry(){this.retry!==null&&(this.options.clock.clearTimeout(this.retry.handle),this.retry=null);}refresh(e){if(this.inFlight)return this.inFlight;this.cancelRetry();let t=this.generation;this.enter("pending");let r=this.options.request(e).then(o=>{if(t!==this.generation)throw new b("internal","token arrived for a superseded generation");return this.grant=o,this.rejected=false,this.enter("ready"),o.token}).catch(o=>{let a=qe(o),{retryable:s,retryAfterMs:l}=o??{};throw t===this.generation&&(Le(a,typeof s=="boolean"?s:void 0)?this.enter("lost",a):(this.scheduleRetry(typeof l=="number"&&Number.isFinite(l)&&l>0?l:void 0),this.enter("degraded",a))),o instanceof b?o:new b(a,String(o))}).finally(()=>{this.inFlight===r&&(this.inFlight=null);});return this.inFlight=r,r}enter(e,t){(e==="ready"||e==="lost")&&this.cancelRetry(),e==="ready"&&(this.failures=0,this.notBefore=0),this.failure=t??(e==="ready"?null:this.failure),this.state!==e&&(this.state=e,this.options.onStateChange?.(e,t));}renewBefore(){return this.options.renewBeforeSeconds??60}seconds(){return Math.floor(this.options.clock.now()/1e3)}},b=class n extends Error{constructor(t,r){super(`auth ${t}: ${r}`);this.reason=t;this.name="AuthError";}reason;static isAuthError(t){if(t instanceof n)return true;let r=t;return r?.name==="AuthError"&&typeof r.reason=="string"}};function qe(n){if(n instanceof b)return n.reason;let e=n?.reason;return typeof e=="string"&&["chain_exhausted","unavailable","timeout","internal"].includes(e)?e:"internal"}var f=class n extends Error{retryable=false;constructor(e){super(e),this.name="TransportRefusal";}static isRefusal(e){if(e instanceof n)return true;let t=e;return t?.name==="TransportRefusal"&&t.retryable===false}},U=["if-match","if-none-match"],X=4096;function $e(n){if(!n)return;let e={};for(let[t,r]of Object.entries(n)){let o=t.toLowerCase();if(!U.includes(o)||r===void 0)continue;let a=typeof r=="string"?je(r):r;if(typeof a!="string"||a.length>X||!Fe(a))throw new f(`the ${o} header value cannot be sent \u2014 it is not a string, is longer than ${X} characters, or contains a character outside the range an HTTP header value may carry. This is a malformed request, not a server failure; the value is withheld here rather than written to logs.`);a!==""&&(e[o]=a);}return Object.freeze(e)}function je(n){let e=o=>o===32||o===9||o===10||o===13,t=0,r=n.length;for(;t<r&&e(n.charCodeAt(t));)t++;for(;r>t&&e(n.charCodeAt(r-1));)r--;return n.slice(t,r)}function Fe(n){for(let e=0;e<n.length;e++){let t=n.charCodeAt(e);if(t!==9&&(t<32||t===127||t>255))return false}return true}function Be(n){let{bytes:e}=n;if(e!==void 0){if(Object.prototype.toString.call(e)!=="[object Uint8Array]")throw new f("the raw body is not a Uint8Array, so it cannot be sent as bytes. This is a malformed request, not a server failure.");if(n.body!==void 0)throw new f("the request carries both a JSON body and a raw body, and only one can be sent. This is a malformed request, not a server failure.");return new Uint8Array(e)}}function Q(n,e){let t=e.replayUnauthorizedOnce??true;return {async request(r){let o={...r},a=$e(o.headers),s=Be(o),l=Object.freeze({...o,headers:a,...s===void 0?{}:{bytes:s}}),u=await e.tokenSource.get(),c=await n(l,u);if(c.status!==401||!t)return c;e.tokenSource.invalidate(u);let g=await e.tokenSource.get(),_=await n(l,g);if(_.status===401)throw e.tokenSource.close("unavailable"),new b("unavailable","the server refused a freshly minted token");return _}}}function Z(n){let e=typeof n=="string"?new TextEncoder().encode(n):n;return btoa(Ue(e))}function Ue(n){let t="";for(let r=0;r<n.length;r+=32768)t+=String.fromCharCode(...n.subarray(r,r+32768));return t}var m=class extends Error{constructor(t,r,o,a,s){super(`${t} (${r}): ${o}`);this.reason=t;this.status=r;this.detail=o;this.retryAfterSeconds=a;this.code=s;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}},ne=new WeakMap;function te(n){return ne.get(n)??{}}var Ne=new Set([408,429,500,502,503,504]),De=6e4;function He(n){if(n===void 0)return 2;if(!Number.isInteger(n)||n<0||n>10)throw new f(`maxRetries must be a whole number between 0 and 10; received ${JSON.stringify(n)}. This is a configuration fault, not a server failure.`);return n}var E=class{constructor(e){this.options=e;}options;async get(e,t){return this.send({method:"GET",path:e,signal:t},true)}async getWithEtag(e,t={}){let r=await this.sendRaw({method:"GET",path:e,...t.signal===void 0?{}:{signal:t.signal},...t.ifNoneMatch===void 0?{}:{headers:{"if-none-match":t.ifNoneMatch}}},true),o=r.etag===void 0?{}:{etag:r.etag};return r.status===304?{modified:false,...o}:{modified:true,body:r.body,...o}}async put(e,t,r,o={}){return this.send({method:"PUT",path:e,body:t,signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false)}async post(e,t,r,o={}){return this.send({method:"POST",path:e,body:t,signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false)}async postBytes(e,t,r,o={}){let a=await this.sendRaw({method:"POST",path:e,bytes:t,responseType:"bytes",signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false);return {bytes:Ve(a.body),...a.invocationId===void 0?{}:{invocationId:a.invocationId}}}async patch(e,t,r,o={}){return this.send({method:"PATCH",path:e,body:t,signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false)}async delete(e,t,r={}){return this.send({method:"DELETE",path:e,signal:t,...q(r)},r.replaySafe===true,r.retryRateLimited!==false)}async send(e,t,r=true){return (await this.sendRaw(e,t,r)).body}async sendRaw(e,t,r=true){let o=t?He(this.options.maxRetries)+1:1,a=null;if(e.signal?.aborted)throw ee(e.signal);for(let s=0;s<o;s++){if(s>0&&await this.backoff(s,a,e.signal),e.signal?.aborted)throw ee(e.signal);let l;try{l=await this.options.transport.request(e);}catch(c){if(b.isAuthError(c)||f.isRefusal(c)||e.signal?.aborted||We(c))throw c;a=new m("unavailable",0,String(c));continue}if(l.status>=200&&l.status<300||l.status===304)return l;let u=ze(l);if(!Ne.has(l.status)||l.status===429&&!r)throw u;a=u;}throw a??new m("unavailable",0,"no attempt was made")}backoff(e,t,r){if(r?.aborted)return Promise.resolve();let o=t?.retryAfterSeconds,a=o!==void 0?o*1e3:2**(e-1)*250,s=2**(e-1)*250,l=Math.min(Math.max(a,s),De);return new Promise(u=>{let c=this.options.clock,g=false,_=false,v,h=()=>{g||(g=true,_&&c.clearTimeout(v),r?.removeEventListener("abort",h),u());};r?.addEventListener("abort",h,{once:true}),v=c.setTimeout(h,l),_=true,g&&c.clearTimeout(v);})}};function Ve(n){if(n instanceof Uint8Array)return n;if(n==null)return new Uint8Array(0);let e=typeof n=="string"?n:JSON.stringify(n);return new TextEncoder().encode(e)}function q(n){return {...n.keepalive===void 0?{}:{keepalive:n.keepalive},...n.ifMatch===void 0?{}:{headers:{"if-match":n.ifMatch}}}}function ee(n){return n.reason!==void 0?n.reason:new DOMException("The operation was aborted.","AbortError")}function We(n){let e=n?.name;return e==="AbortError"||e==="TimeoutError"}function ze(n){let e=Ye(n),t=Xe(n.body),r={...t===void 0?{}:{details:t},...n.invocationId===void 0?{}:{invocationId:n.invocationId}};return Object.keys(r).length>0&&ne.set(e,r),e}var Ke=new Map([["insufficient_funds","insufficient_funds"]]);function Ye(n){let{status:e,body:t}=n,r=Je(t),o=n.retryAfterSeconds??Ze(t),a=Qe(t),s=a===void 0?void 0:Ke.get(a);if(s!==void 0)return new m(s,e,r,void 0,a);switch(e){case 304:return new m("not_modified",e,r);case 400:case 422:return new m("invalid",e,r,void 0,a);case 403:return new m("forbidden",e,r,void 0,a);case 404:return new m("not_found",e,r,void 0,a);case 402:return new m("insufficient_funds",e,r,void 0,a);case 409:return new m("conflict",e,r,void 0,a);case 412:return new m("precondition_failed",e,r,void 0,a);case 413:return new m("too_large",e,r,void 0,a);case 429:return new m("rate_limited",e,r,o,a);default:return new m("unavailable",e,r,o,a)}}function Je(n){if(typeof n=="string")return n;let e=n?.message;if(typeof e=="string")return e;let t=n?.error;if(typeof t=="string")return t;let r=t?.message;return typeof r=="string"?r:"no detail"}function Xe(n){let t=n?.error?.details;return t===null?void 0:t}function Qe(n){let t=n?.error?.code;return typeof t=="string"&&t.length>0?t:void 0}function Ze(n){let e=n?.retryAfterSeconds;return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:void 0}function en(n){let e=n.lastIndexOf("@");return (e===-1?n:`***@${n.slice(e+1)}`).slice(0,200)}var nn=new Set(U);function tn(n){if(!n)return {};let e={};for(let[t,r]of Object.entries(n))nn.has(t.toLowerCase())&&(e[t.toLowerCase()]=r);return e}function re(n){let e=n.fetch??globalThis.fetch.bind(globalThis);return Q(async(t,r)=>{let o;try{o=new URL(n.baseUrl.replace(/\/+$/,""));}catch{throw new f("the configured baseUrl is not a valid URL. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure.")}let a;try{a=new URL(o.href.replace(/\/+$/,"")+t.path);}catch{throw new f(`${JSON.stringify(typeof t.path=="string"?en(t.path):"(a non-string path)")} does not form a valid URL against ${o.protocol}//${o.host}. This is a malformed request, not a server failure.`)}if(a.protocol!==o.protocol||a.host!==o.host)throw new f(`${JSON.stringify(a.pathname)} resolves to ${a.protocol}//${a.host}, not ${o.protocol}//${o.host}. A request path may not change the request's origin \u2014 it carries the play token.`);if(a.username!==""||a.password!==""){let h=o.username!==""||o.password!=="",y=a.username!==o.username||a.password!==o.password,S=h?y?"baseUrl (and possibly the request path)":"baseUrl":"request path";throw new f(`the ${S} puts credentials in the URL, which cannot be fetched (${t.method} ${a.pathname}). The value is withheld here rather than written to logs. This is a malformed request, not a server failure.`)}for(let[h,y]of Object.entries(n.defaultQuery??{}))a.searchParams.has(h)||a.searchParams.set(h,y);let s=t.bytes,l=t.bytes===void 0?void 0:"application/octet-stream";try{t.body!==void 0&&(s=JSON.stringify(t.body),l="application/json");}catch(h){throw new f(`the body of ${t.method} ${JSON.stringify(a.pathname)} could not be serialised to JSON: ${String(h)}. This is a bug in the request, not a server failure \u2014 retrying sends the same unserialisable value.`)}let u=await e(a.toString(),{method:t.method,headers:{authorization:`Bearer ${r}`,...l===void 0?{}:{"content-type":l},...tn(t.headers)},body:s,signal:t.signal,...t.keepalive===void 0?{}:{keepalive:t.keepalive},credentials:"omit",mode:"cors",redirect:"error",cache:"no-store",referrerPolicy:"no-referrer"}),c=null;if(t.responseType==="bytes"&&u.status>=200&&u.status<300)c=new Uint8Array(await u.arrayBuffer());else {let h=await u.text();if(h.length>0)try{c=JSON.parse(h);}catch{c=h;}}let g=u.headers.get("etag"),_=rn(u.headers.get("retry-after")),v=u.headers.get("x-gem-invocation-id");return {status:u.status,body:c,...g===null?{}:{etag:g},..._===void 0?{}:{retryAfterSeconds:_},...v===null||v===""?{}:{invocationId:v}}},n)}function rn(n){if(n===null)return;let e=n.trim();if(!/^\d+$/.test(e))return;let t=Number(e);return Number.isFinite(t)?t:void 0}var on="gem-web-bridge";var I=65536,oe=8,N=5e3,ae=1024;var D=Object.create(null);var ie=Object.create(null);function an(n){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(n);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function sn(n){let e;try{e=new URL(n);}catch{return false}return e.username!==""||e.password!==""?false:e.protocol==="https:"?true:e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1")}function ln(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function dn(n){return /^[a-z_]+$/.test(n)}function V(n,e,t){if(e>oe)return `nesting deeper than ${oe}`;if(++t.nodes>N)return `more than ${N} values`;if(t.bytes>I)return `over the ${I} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let o=n;if(t.seen.has(o))return "a repeated or cyclic object reference";if(t.seen.add(o),Array.isArray(n)){if(n.length>ae)return `an array of ${n.length} entries, over the ${ae} limit`;t.bytes+=2;for(let l of n){let u=V(l,e+1,t);if(u)return u}return null}let a=Object.getPrototypeOf(n);if(a!==Object.prototype&&a!==null)return "a value that is not a plain object";t.bytes+=2;let s=n;for(let l in s){if(!Object.prototype.hasOwnProperty.call(s,l))continue;if(++t.nodes>N)return `more than ${N} values`;if(t.bytes+=l.length*3+4,t.bytes>I)return `over the ${I} byte limit`;let u=V(s[l],e+1,t);if(u)return u}return null}function un(n){let e={nodes:0,bytes:0,seen:new Set},t=V(n,0,e);return t?`message contains ${t}`:e.bytes>I?`message is over the ${I} byte limit`:null}var cn={origin:[an,"not a well-formed origin"],url:[sn,"not a safe absolute http(s) URL"],bcp47:[ln,"not a well-formed BCP 47 tag"],code:[dn,"not a code of lower-case letters and underscores"]};function se(n,e,t){for(let r of e){let o=t===""?r.n:`${t}.${r.n}`,a=n[r.n];if(a==null){if(r.o)continue;return `${o}: required`}let s=W(a,r,o);if(s)return s}return null}function W(n,e,t){switch(e.k){case "u":return typeof n=="boolean"?null:W(n,{...e,k:typeof n=="string"?"s":"n"},t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e.max)return `${t}: exceeds ${e.max} characters`;if(e.f){let[r,o]=cn[e.f];if(!r(n))return `${t}: ${o}`}return null}case "n":case "i":return typeof n!="number"||!(e.k=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${e.k=="i"?"integer":"number"}`:n<e.lo||n>e.hi?`${t}: out of range ${e.lo}..${e.hi}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let r=e.v;return r.includes(n)?null:`${t}: not one of ${r.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:se(n,e.fs,t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e.max)return `${t}: exceeds ${e.max} items`;let r=e.it,o=`${t}[]`;for(let a of n){if(a==null){if(r.o)continue;return `${o}: required`}let s=W(a,r,o);if(s)return s}return null}default:{let r=e.k;return `${t}: unknown kind ${String(r)}`}}}var le=Object.create(null);function d(n,e,t,r){return D[n]=t,r&&(ie[n]=r),le[n]=o=>{let a=un(o);if(a)return a;if(typeof o!="object"||o===null||Array.isArray(o))return "message: expected object";let s=o;return s.gem!==on?"gem: wrong protocol marker":s.type!==n?`type: expected ${n}`:se(s,e,"")}}var p={n:"requestId",k:"s",max:64};d("gem.handshake",[{n:"versions",k:"o",fs:[{n:"min",k:"i",lo:1,hi:1e3},{n:"max",k:"i",lo:1,hi:1e3}]},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}},{n:"embedder",k:"s",max:2048,f:"origin"},{n:"unbidden",k:"b",o:1}],30);d("gem.handshake.accept",[{n:"v",k:"i",lo:1,hi:1e3},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}}],5);d("gem.token",[{n:"token",k:"s",max:4096},{n:"requestId",k:"s",o:1,max:64},{n:"generation",k:"i",lo:0,hi:2147483647},{n:"apiBaseUrl",k:"s",o:1,max:512,f:"url"},{n:"playerId",k:"s",max:64},{n:"gameId",k:"s",max:64},{n:"channel",k:"e",v:["dev","test","release"]},{n:"gameVersion",k:"s",o:1,max:64},{n:"expiresAt",k:"i",lo:0,hi:4102444800},{n:"chainEndsAt",k:"i",lo:0,hi:4102444800},{n:"allowDevTools",k:"b",o:1}],30);d("gem.token.denied",[p,{n:"retryable",k:"b"},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:36e5},{n:"reason",k:"e",v:["chain_exhausted","unavailable"]},{n:"correlationId",k:"s",max:64}],30);d("gem.player",[{n:"untrusted",k:"o",fs:[{n:"displayName",k:"s",max:64},{n:"displayTag",k:"s",o:1,max:80},{n:"displayHandle",k:"s",max:80}]},{n:"playerId",k:"s",o:1,max:64},{n:"discriminator",k:"s",o:1,max:8},{n:"avatarIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatarIconId",k:"s",o:1,max:64},{n:"avatar3dId",k:"s",o:1,max:64},{n:"avatar3dModelUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatar3dIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatars3d",k:"a",o:1,max:8,it:{n:"",k:"o",fs:[{n:"family",k:"s",max:32},{n:"id",k:"s",max:64},{n:"modelUrl",k:"s",max:256,f:"url"},{n:"iconUrl",k:"s",max:256,f:"url"}]}},{n:"badges",k:"a",o:1,max:2,it:{n:"",k:"o",fs:[{n:"itemId",k:"s",max:64},{n:"iconUrl",k:"s",max:2048,f:"url"}]}},{n:"displayTag",k:"s",max:8},{n:"avatarUrl",k:"s",o:1,max:2048,f:"url"},{n:"boost",k:"o",o:1,fs:[{n:"multipliers",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"currencyId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"multiplierBp",k:"i",lo:1e4,hi:1e6}]}},{n:"expiresInDays",k:"i",o:1,lo:1,hi:7}]}],12);d("gem.pause",[{n:"paused",k:"b"}],120);d("gem.prefs",[{n:"reducedMotion",k:"b"},{n:"muted",k:"b"},{n:"showFrameStats",k:"b",o:1},{n:"music",k:"b",o:1},{n:"sounds",k:"b",o:1},{n:"locale",k:"s",max:35,f:"bcp47"},{n:"recommended",k:"o",o:1,fs:[{n:"volume",k:"i",lo:0,hi:100},{n:"batterySaver",k:"b"},{n:"keyboardLayout",k:"e",v:["wasd","arrows"]},{n:"gamepadConfirm",k:"e",v:["south","east"]},{n:"vibration",k:"b"},{n:"lookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"gamepadLookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"invertLookY",k:"b",o:1},{n:"touchMovement",k:"s",o:1,max:16,f:"code"},{n:"graphicsQuality",k:"s",o:1,max:16,f:"code"},{n:"graphicsQualityAuto",k:"b",o:1}]}],60);d("gem.cohorts",[{n:"cohorts",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"experimentId",k:"s",max:36},{n:"variantKey",k:"s",max:32}]}}],12,"cohorts");d("gem.earnAllowanceChanged",[],30,"earnAllowance");d("gem.viewport",[{n:"insets",k:"o",fs:[{n:"top",k:"i",lo:0,hi:4096},{n:"right",k:"i",lo:0,hi:4096},{n:"bottom",k:"i",lo:0,hi:4096},{n:"left",k:"i",lo:0,hi:4096}]},{n:"reserved",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384},{n:"control",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384}]}]}}],60,"viewport");d("gem.deviceSettings",[{n:"settings",k:"j",o:1}],12,"deviceSettings");d("gem.willTerminate",[{n:"reason",k:"e",v:["grant_expiring","session_ended","navigating","kill_switch"]},{n:"graceMs",k:"i",lo:0,hi:6e4}],10);d("gem.roomEvent",[{n:"event",k:"e",v:["updated","match_started","match_ready","match_failed","version_stale"]},{n:"roomId",k:"s",max:64},{n:"matchId",k:"s",o:1,max:64},{n:"version",k:"i",o:1,lo:0,hi:2147483647},{n:"reason",k:"s",o:1,max:64},{n:"platform",k:"e",o:1,v:["universal","mobile"]}],120,"rooms");d("gem.socketState",[{n:"connected",k:"b"},{n:"reason",k:"e",o:1,v:["connecting","dropped","watchdog","terminal"]}],12,"socketState");d("gem.seatedEvent",[{n:"roomId",k:"s",max:64}],60,"partySeating");d("gem.autoStartFailedEvent",[{n:"roomId",k:"s",max:64},{n:"code",k:"s",max:64}],30,"autoStartFailure");d("gem.ticketEvent",[{n:"event",k:"e",v:["queued","matched","expired","cancelled","failed"]},{n:"ticketId",k:"s",max:64},{n:"partySize",k:"i",o:1,lo:1,hi:64},{n:"queuedAt",k:"i",o:1,lo:0,hi:4102444800},{n:"expiresAt",k:"i",o:1,lo:0,hi:4102444800},{n:"reason",k:"s",o:1,max:64},{n:"roomId",k:"s",o:1,max:64}],60,"ticketEvents");d("gem.signalEvent",[{n:"event",k:"e",v:["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1800,"p2p");d("gem.dedicatedEvent",[{n:"event",k:"e",v:["answer","error","matchEnded"]},{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",o:1,max:10240},{n:"sessionId",k:"s",o:1,max:64},{n:"code",k:"s",o:1,max:64},{n:"message",k:"s",o:1,max:256},{n:"reason",k:"s",o:1,max:64}],120,"dedicated");d("gem.partyResult",[p,{n:"partyId",k:"s",o:1,max:64}],30,"parties");d("gem.clusterTarget",[{n:"clusterTargetId",k:"s",max:64},{n:"clusterTargetIds",k:"a",o:1,max:3,it:{n:"",k:"s",max:64}}],12,"clusterTarget");d("gem.devtools",[{n:"action",k:"e",v:["open","close","toggle"]}],30,"devtools");d("gem.screenshot",[{n:"maxWidth",k:"i",lo:1,hi:7680},{n:"maxHeight",k:"i",lo:1,hi:4320}],6,"screenshot");d("gem.screenshot.denied",[{n:"reason",k:"e",v:["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]},{n:"retryable",k:"b"}],12,"screenshot");d("gem.requestContext",[],12,"feedbackContext");d("gem.gameContext",[{n:"context",k:"j"}],12,"feedbackContext");d("gem.signal",[{n:"signal",k:"e",v:["join","offer","answer","ice","migrationAck","hostUnreachable"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1200,"p2p");d("gem.dedicatedOffer",[{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",max:10240},{n:"intent",k:"e",v:["initial","ice_restart","full_reconnect"]}],60,"dedicated");d("gem.partyRequest",[p],12,"parties");d("gem.ready",[],5);d("gem.engaged",[],5,"engagement");d("gem.progress",[{n:"loaded",k:"i",lo:0,hi:1099511627776},{n:"total",k:"i",lo:0,hi:1099511627776}],600);d("gem.requestToken",[p,{n:"reason",k:"e",v:["initial","expiring","rejected"]}],12);d("gem.requestLocalServer",[p,{n:"gameRoomId",k:"s",max:64}],6,"localHost");d("gem.localServer",[p,{n:"serverId",k:"s",o:1,max:128}],30,"localHost");d("gem.localServer.denied",[p,{n:"retryable",k:"b"},{n:"reason",k:"e",v:["refused","unavailable"]}],30,"localHost");d("gem.exit",[{n:"reason",k:"s",o:1,max:64}],5);d("gem.openExternal",[{n:"url",k:"s",max:2048,f:"url"}],6);d("gem.requestFullscreen",[{n:"enabled",k:"b"}],30);d("gem.audioState",[{n:"muted",k:"b"}],120,"audioState");d("gem.rewardCue",[{n:"currencySlug",k:"s",max:32},{n:"expected",k:"i",o:1,lo:1,hi:1e9},{n:"origin",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384}]}],60,"rewardCue");d("gem.walletDisplay",[{n:"visible",k:"b"},{n:"currencies",k:"a",o:1,max:8,it:{n:"",k:"s",max:64}},{n:"score",k:"b",o:1}],60,"walletDisplay");d("gem.controlsDeclare",[{n:"uses",k:"o",fs:[{n:"lookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"gamepadLookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"invertLookY",k:"s",o:1,max:16,f:"code"},{n:"touchMovement",k:"s",o:1,max:16,f:"code"}]}],60,"controls");d("gem.menu",[{n:"settings",k:"b"},{n:"items",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:32},{n:"label",k:"s",max:40}]}}],60,"gameMenu");d("gem.menuSelect",[{n:"entry",k:"e",v:["settings","item"]},{n:"id",k:"s",o:1,max:32}],60,"gameMenu");d("gem.gemCorner",[{n:"corner",k:"e",v:["top-left","top-right","bottom-left","bottom-right"]}],12,"gemCorner");d("gem.settingsDeclare",[{n:"settings",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:40},{n:"label",k:"s",max:40},{n:"kind",k:"e",v:["toggle","slider","choice"]},{n:"value",k:"u",max:40,lo:-1e6,hi:1e6},{n:"quick",k:"b",o:1},{n:"icon",k:"s",o:1,max:16,f:"code"},{n:"min",k:"n",o:1,lo:-1e6,hi:1e6},{n:"max",k:"n",o:1,lo:-1e6,hi:1e6},{n:"step",k:"n",o:1,lo:0,hi:1e6},{n:"options",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:40},{n:"label",k:"s",max:40}]}}]}}],120,"gameSettings");d("gem.settingChange",[{n:"id",k:"s",max:40},{n:"value",k:"u",max:40,lo:-1e6,hi:1e6}],120,"gameSettings");d("gem.requestShare",[{n:"params",k:"a",max:16,it:{n:"",k:"o",fs:[{n:"key",k:"s",max:64},{n:"value",k:"s",max:512}]}}],6,"share");d("gem.requestAvatarPicker",[{n:"tab",k:"e",o:1,v:["2d","3d"]}],6,"avatarPicker");d("gem.vibrate",[{n:"pattern",k:"a",max:99,it:{n:"",k:"i",lo:0,hi:1e4}}],600,"vibration");d("gem.frameStats",[{n:"fps",k:"i",lo:0,hi:1e3},{n:"frameTimeP95Ms",k:"i",lo:0,hi:1e5},{n:"counts",k:"e",o:1,v:["pageFrames","gameDraws"]}],120,"frameStats");d("gem.portraitBorders",[{n:"name",k:"s",o:1,max:40}],60,"portraitBorders");d("gem.error",[{n:"code",k:"e",v:["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]},{n:"message",k:"s",o:1,max:256}],60);d("gem.requestPurchase",[p,{n:"purchaseKind",k:"e",v:["item","recipe"]},{n:"targetId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"quantity",k:"i",o:1,lo:1,hi:10},{n:"catalogId",k:"s",o:1,max:64}],12,"purchases");d("gem.requestAvatar3dPurchase",[p,{n:"itemId",k:"s",max:64},{n:"currencySlug",k:"s",max:32}],12,"avatar3dPurchases");d("gem.purchaseResult",[p,{n:"outcome",k:"e",v:["ok","declined","unavailable"]},{n:"reason",k:"s",o:1,max:64,f:"code"},{n:"purchaseId",k:"s",o:1,max:64}],30,"purchases");d("gem.deviceSettings.put",[p,{n:"settings",k:"j"}],30,"deviceSettings");d("gem.deviceSettings.result",[p,{n:"outcome",k:"e",v:["stored","too_large","unavailable"]}],30,"deviceSettings");d("gem.requestAd",[p,{n:"placement",k:"s",max:32},{n:"format",k:"e",v:["interstitial","rewarded"]}],6,"ads");d("gem.adResult",[p,{n:"outcome",k:"e",v:["shown","no_ad","unavailable"]}],12,"ads");d("gem.signalMoment",[p,{n:"kind",k:"e",v:["level_end","run_end","match_end","menu_return","other_break"]},{n:"placement",k:"s",o:1,max:32}],6,"moments");d("gem.momentResult",[p,{n:"shown",k:"b"}],12,"moments");d("gem.requestSignIn",[p],6,"signIn");d("gem.signInResult",[p,{n:"outcome",k:"e",v:["signed_in","declined","unavailable"]}],12,"signIn");d("gem.requestFriendChallenge",[p,{n:"playerId",k:"s",max:64},{n:"template",k:"s",max:64},{n:"params",k:"j",o:1}],12,"friendChallenges");d("gem.friendChallengeResult",[p,{n:"outcome",k:"e",v:["accepted","cancelled","refused","unavailable","rate_limited","invalid"]},{n:"code",k:"s",o:1,max:64},{n:"param",k:"s",o:1,max:64},{n:"channel",k:"s",o:1,max:16},{n:"configVersion",k:"i",o:1,lo:0,hi:9007199254740991},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:864e5},{n:"detail",k:"s",o:1,max:300}],30,"friendChallenges");Object.freeze(le);D.__malformed=60;Object.freeze(D);Object.freeze(ie);var M={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)};function de(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}var mn=1e4;function ue(n,e){return z(n,e)}function z(n,e){if(typeof n.serverToken!="function"||typeof n.onServerToken!="function")throw new Error("this runtime delivers no game-server token: host.serverToken/onServerToken are missing. The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let t=s=>({token:s.token,expiresAt:s.expiresAt,chainEndsAt:Number.POSITIVE_INFINITY}),r=null,o=new B({clock:e.clock,...e.onStateChange===void 0?{}:{onStateChange:e.onStateChange},request:()=>new Promise((s,l)=>{let u=e.clock.setTimeout(()=>{r=null,l({reason:"unavailable",retryable:true});},e.timeoutMs??mn);r={resolve:c=>{e.clock.clearTimeout(u),s(c);},reject:l,timer:u};})}),a=n.onServerToken(s=>{let l=t(s),u=r;if(u){r=null,u.resolve(l);return}o.accept(l);});return {source:o,dispose(){a(),o.dispose(),r&&(e.clock.clearTimeout(r.timer),r.reject({reason:"unavailable",retryable:false}),r=null);}}}var Y={};Ie(Y,{acceptTransfer:()=>gn,addEntityProgression:()=>pn,adminAction:()=>yn,cancelInventoryTransfer:()=>fn,claimEntityProgressionRewards:()=>hn,closeGameRoom:()=>_n,createEntityBatchOrder:()=>bn,createEntityOrder:()=>vn,createSession:()=>Rn,deleteEntityGameSetting:()=>xn,deleteEntitySave:()=>kn,deleteSession:()=>Sn,deleteShowcaseValue:()=>wn,executeEntityRecipe:()=>An,finalizeSaveUpload:()=>Pn,getChannelAssignment:()=>On,getDropRates:()=>Tn,getEntityCurrency:()=>Gn,getEntityGameSetting:()=>Cn,getEntityInventory:()=>En,getEntityInventoryItem:()=>In,getEntityLeaderboard:()=>Mn,getEntityOrder:()=>Ln,getEntityProgression:()=>qn,getEntityRecipeAvailability:()=>$n,getEntityRecipeUsage:()=>jn,getEntitySave:()=>Fn,getEvent:()=>Bn,getGameRoomV2:()=>Un,getLeaderboard:()=>Nn,getPlayerGameProfile:()=>Dn,getPurchaseCatalog:()=>Hn,getResolvedTemplate:()=>Vn,getRoomArgs:()=>Wn,getShowcaseValue:()=>zn,getTransferDetails:()=>Kn,ingestAnalyticsEvents:()=>K,initiateInventoryTransfer:()=>Yn,invokeFunction:()=>Jn,kickPlayer:()=>Xn,listChallenges:()=>Qn,listEntityGameSettings:()=>Zn,listEntityOrders:()=>et,listEntityProgressions:()=>nt,listEntitySaves:()=>tt,listEvents:()=>rt,listGameRooms:()=>ot,listResolvedTemplates:()=>at,listTransfers:()=>it,maxEntityProgression:()=>st,putEntityGameSetting:()=>lt,putRoomArgs:()=>dt,putShowcaseValue:()=>ut,rejectTransfer:()=>ct,reportMatchPlayers:()=>mt,requestSaveUploadUrl:()=>gt,resetEntityProgression:()=>pt,setMatchBackfill:()=>yt,simulateEntityOrder:()=>ft,transferHostV2:()=>ht,updateGameRoomV2:()=>_t});function i(n){let e=String(n);if(!/^[A-Za-z0-9_.-]{1,128}$/.test(e)||e==="."||e==="..")throw new TypeError(`${JSON.stringify(e)} is not a usable path segment \u2014 it is interpolated into a URL`);return encodeURIComponent(e)}async function gn(n,e,t){let r=`/v1/inventory/transfers/${i(e)}/accept`;return n.client.post(r,void 0,t?.signal)}async function pn(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/add`;return n.client.post(s,o,a?.signal)}async function yn(n,e,t,r){let o=`/v1/voice/sessions/${i(e)}/admin`;return n.client.post(o,t,r?.signal)}async function fn(n,e,t){let r=`/v1/inventory/transfers/${i(e)}/cancel`;return n.client.post(r,void 0,t?.signal)}async function hn(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/claim`;return n.client.post(s,o,a?.signal)}async function _n(n,e,t){let r=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}`;return n.client.delete(r,t?.signal)}async function bn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders/batch`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function vn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Rn(n,e,t){return n.client.post("/v1/voice/sessions",e,t?.signal)}async function xn(n,e,t,r,o){let a=`/v1/settings/${i(e)}/${i(t)}/${i(r)}`;return n.client.delete(a,o?.signal)}async function kn(n,e,t,r,o){let a=`/v1/saves/${i(e)}/${i(t)}/${i(r)}`;return n.client.delete(a,o?.signal)}async function Sn(n,e,t){let r=`/v1/voice/sessions/${i(e)}`;return n.client.delete(r,t?.signal)}async function wn(n,e,t,r){let o=`/v1/showcase/players/${i(e)}/${i(t)}`;return n.client.delete(o,r?.signal)}async function An(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/recipes/execute`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Pn(n,e,t,r,o,a){let s=`/v1/saves/${i(e)}/${i(t)}/${i(r)}/finalize`;return n.client.post(s,o,a?.signal)}async function On(n,e,t){let r=`/v1/games/${i(n.gameId())}/config/channels/${i(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Tn(n,e,t){let r=`/v1/inventory/loot-tables/${i(e)}/drop-rates`;return n.client.get(r,t?.signal)}async function Gn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/currencies/${i(r)}`;return n.client.get(a,o?.signal)}async function Cn(n,e,t,r,o){let a=`/v1/settings/${i(e)}/${i(t)}/${i(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function En(n,e,t,r){let o=new URLSearchParams;r?.include_buckets!==void 0&&o.set("include_buckets",String(r?.include_buckets)),r?.include_custom_data!==void 0&&o.set("include_custom_data",String(r?.include_custom_data)),r?.currency_limit!==void 0&&o.set("currency_limit",String(r?.currency_limit)),r?.currency_cursor!==void 0&&o.set("currency_cursor",String(r?.currency_cursor)),r?.item_limit!==void 0&&o.set("item_limit",String(r?.item_limit)),r?.item_cursor!==void 0&&o.set("item_cursor",String(r?.item_cursor)),r?.progression_limit!==void 0&&o.set("progression_limit",String(r?.progression_limit)),r?.progression_cursor!==void 0&&o.set("progression_cursor",String(r?.progression_cursor));let a=o.toString(),s=`/v1/inventory/${i(e)}/${i(t)}`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function In(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/items/${i(r)}`;return n.client.get(a,o?.signal)}async function Mn(n,e,t,r,o){let a=new URLSearchParams;o?.bucket_1!==void 0&&a.set("bucket_1",String(o?.bucket_1)),o?.bucket_2!==void 0&&a.set("bucket_2",String(o?.bucket_2)),o?.limit!==void 0&&a.set("limit",String(o?.limit)),o?.periods_back!==void 0&&a.set("periods_back",String(o?.periods_back)),o?.period!==void 0&&a.set("period",String(o?.period)),o?.player_id!==void 0&&a.set("player_id",String(o?.player_id));let s=a.toString(),l=`/v1/leaderboards/${i(e)}/${i(t)}/${i(r)}`+(s?`?${s}`:"");return n.client.get(l,o?.signal)}async function Ln(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders/${i(r)}`;return n.client.get(a,o?.signal)}async function qn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}`;return n.client.get(a,o?.signal)}async function $n(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/catalogs/${i(r)}/recipes/${i(o)}/availability`;return n.client.get(s,a?.signal)}async function jn(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit));let a=o.toString(),s=`/v1/inventory/${i(e)}/${i(t)}/recipe-usage`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function Fn(n,e,t,r,o){let a=`/v1/saves/${i(e)}/${i(t)}/${i(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function Bn(n,e,t){let r=`/v1/events/${i(e)}`;return n.client.get(r,t?.signal)}async function Un(n,e,t){let r=`/v2/games/${i(n.gameId())}/game-rooms/${i(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Nn(n,e,t){let r=new URLSearchParams;t?.bucket_1!==void 0&&r.set("bucket_1",String(t?.bucket_1)),t?.bucket_2!==void 0&&r.set("bucket_2",String(t?.bucket_2)),t?.periods_back!==void 0&&r.set("periods_back",String(t?.periods_back)),t?.period!==void 0&&r.set("period",String(t?.period)),t?.player_id!==void 0&&r.set("player_id",String(t?.player_id));let o=r.toString(),a=`/v1/leaderboards/${i(e)}`+(o?`?${o}`:"");return n.client.get(a,t?.signal)}async function Dn(n,e,t){let r=`/v1/profiles/players/${i(e)}`;return n.client.get(r,t?.signal)}async function Hn(n,e){let t=new URLSearchParams;e?.config_version!==void 0&&t.set("config_version",String(e?.config_version));let r=t.toString(),o="/v1/inventory/platform-funded-game-inventory-purchase-catalog"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Vn(n,e,t){let r=`/v1/game-room-templates/${i(e)}`;return n.client.get(r,t?.signal)}async function Wn(n,e,t){let r=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}/room-args`;return n.client.get(r,t?.signal)}async function zn(n,e,t,r){let o=`/v1/showcase/players/${i(e)}/${i(t)}`;return n.client.get(o,r?.signal)}async function Kn(n,e,t){let r=`/v1/inventory/transfers/${i(e)}`;return n.client.get(r,t?.signal)}async function K(n,e,t){let r=`/v1/games/${i(n.gameId())}/analytics/events`;return n.client.post(r,e,t?.signal,{replaySafe:true,retryRateLimited:false})}async function Yn(n,e,t){return n.client.post("/v1/inventory/transfers",e,t?.signal)}async function Jn(n,e,t){return n.client.postBytes("/v1/functions/invoke",e,t?.signal)}async function Xn(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}/kick/${i(t)}`;return n.client.post(o,void 0,r?.signal)}async function Qn(n,e){let t=new URLSearchParams;e?.periods_back!==void 0&&t.set("periods_back",String(e?.periods_back)),e?.player_id!==void 0&&t.set("player_id",String(e?.player_id));let r=t.toString(),o="/v1/challenges"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Zn(n,e,t,r){let o=`/v1/settings/${i(e)}/${i(t)}`;return n.client.get(o,r?.signal)}async function et(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),s=`/v1/inventory/${i(e)}/${i(t)}/orders`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function nt(n,e,t,r){let o=`/v1/inventory/${i(e)}/${i(t)}/progressions`;return n.client.get(o,r?.signal)}async function tt(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),s=`/v1/saves/${i(e)}/${i(t)}`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function rt(n,e){let t=new URLSearchParams;e?.include_inactive!==void 0&&t.set("include_inactive",String(e?.include_inactive)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.offset!==void 0&&t.set("offset",String(e?.offset));let r=t.toString(),o="/v1/events"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function ot(n,e){let t=new URLSearchParams;e?.status!==void 0&&t.set("status",String(e?.status)),e?.game_channel!==void 0&&t.set("game_channel",String(e?.game_channel)),e?.game_room_type!==void 0&&t.set("game_room_type",String(e?.game_room_type)),e?.template_id!==void 0&&t.set("template_id",String(e?.template_id)),e?.template_slug!==void 0&&t.set("template_slug",String(e?.template_slug)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor));let r=t.toString(),o=`/v1/games/${i(n.gameId())}/game-rooms`+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function at(n,e){return n.client.get("/v1/game-room-templates",e?.signal)}async function it(n,e){let t=new URLSearchParams;e.entity_type!==void 0&&t.set("entity_type",String(e.entity_type)),e.entity_id!==void 0&&t.set("entity_id",String(e.entity_id)),e?.direction!==void 0&&t.set("direction",String(e?.direction)),e?.status!==void 0&&t.set("status",String(e?.status)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor)),e?.limit!==void 0&&t.set("limit",String(e?.limit));let r=t.toString(),o="/v1/inventory/transfers"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function st(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/max`;return n.client.post(s,o,a?.signal)}async function lt(n,e,t,r,o,a){let s=`/v1/settings/${i(e)}/${i(t)}/${i(r)}`;return n.client.put(s,o,a?.signal,{...a?.ifMatch===void 0?{}:{ifMatch:a.ifMatch}})}async function dt(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}/room-args`;return n.client.put(o,t,r?.signal)}async function ut(n,e,t,r,o){let a=`/v1/showcase/players/${i(e)}/${i(t)}`;return n.client.put(a,r,o?.signal)}async function ct(n,e,t){let r=`/v1/inventory/transfers/${i(e)}/reject`;return n.client.post(r,void 0,t?.signal)}async function mt(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/matches/${i(e)}/players/report`;return n.client.post(o,t,r?.signal)}async function gt(n,e,t,r,o,a){let s=`/v1/saves/${i(e)}/${i(t)}/${i(r)}/upload-url`;return n.client.post(s,o,a?.signal,{replaySafe:true})}async function pt(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/reset`;return n.client.post(s,o,a?.signal)}async function yt(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/matches/${i(e)}/backfill`;return n.client.put(o,t,r?.signal)}async function ft(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders/simulate`;return n.client.post(a,r,o?.signal)}async function ht(n,e,t,r){let o=`/v2/games/${i(n.gameId())}/game-rooms/${i(e)}/transfer/${i(t)}`;return n.client.post(o,void 0,r?.signal)}async function _t(n,e,t,r){let o=`/v2/games/${i(n.gameId())}/game-rooms/${i(e)}`;return n.client.patch(o,t,r?.signal,{...r?.ifMatch===void 0?{}:{ifMatch:r.ifMatch}})}var ce="gem_";function me(n,e){let t=e?.retryAfterSeconds,r=typeof t=="number"&&Number.isFinite(t)&&t>0?t*1e3:0,o=1e4*2**Math.min(Math.max(0,n-1),16);return Math.min(Math.max(r,o),3e5)}var Rt=1e4,xt=50,kt=1e3,St=65536-4*1024,ge=n=>typeof n=="number"?n:0,wt=n=>typeof n=="string"?n:"";function pe(){let n=globalThis.crypto;return typeof n?.randomUUID=="function"?n.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.floor(Math.random()*16);return (e==="x"?t:t&3|8).toString(16)})}function At(n){return n?.retryable===true}function Pt(n,e){let t=n??{},r=Array.isArray(t.rejected)?t.rejected.map(o=>{let a=o??{};return {index:ge(a.index),code:wt(a.code)}}):[];return {accepted:ge(t.accepted),rejected:r,dropped:e,duplicate:t.duplicate===true}}function ye(n,e){return {event_uid:n.uid,name:n.name,offset_ms:Math.round(n.atMs)-e,player_id:n.playerId,dimensions:n.dimensions,measures:n.measures}}function fe(n){let e=Math.max(1,Math.floor(n.maxQueue??kt)),t={client:n.client,gameId:()=>n.gameId},r=[],o=0,a=null,s=false,l=null,u=0,c=0,g=false,_=false,v=R=>{n.log?.(R);},h=()=>{a!==null&&(n.clock.clearTimeout(a),a=null);},y=()=>Math.max(0,c-n.clock.now()),S=()=>{a!==null||s||l!==null||(a=n.clock.setTimeout(()=>{a=null,O().catch(()=>{});},Math.max(Rt,y())));},A=()=>{let R=Math.min(r.length,100),x=128;for(let T=0;T<R;T+=1){let G=r[T];if(G===void 0)break;if(x+=JSON.stringify(ye(G,0)).length+1,x>St)return Math.max(1,T)}return R};async function O(R){for(;l!==null;)await l.catch(()=>{});if(r.length===0)return {accepted:0,rejected:[],dropped:0,duplicate:false};let x=Ge(R);l=x;try{return await x}finally{l===x&&(l=null),r.length>0&&S();}}async function Ge(R){h();let x=r.splice(0,A()),T=o;o=0;let G=Math.round(n.clock.now()),F={batch_id:pe(),sent_at_ms:G,events:x.map(w=>ye(w,G))};try{let w=await K(t,F,R===void 0?void 0:{signal:R}),C=Pt(w,T);u=0,c=0;for(let Ce of C.rejected)v(`[gem] analytics: an event was refused (${Ce.code}). See the analytics_events section of your game config.`);return C}catch(w){if(At(w)){for(r=[...x,...r],o+=T;r.length>e;)r.shift(),o+=1;u+=1,c=n.clock.now()+me(u,w);}else v(`[gem] analytics: ${x.length} event(s) were discarded \u2014 the platform refused the batch and will refuse it again: ${String(w)}`);throw w}}return {track(R,x,T={}){if(s)return;if(typeof R!="string"||R===""){_||(_=true,v(`[gem] analytics: '${x}' was not recorded \u2014 an event from a server has to name the player it is about. A game-server token names no player, so the platform cannot infer one; pass the id from join() or host.players().`));return}if(x.startsWith(ce)){g||(g=true,v(`[gem] analytics: '${x}' was not recorded \u2014 the gem_ prefix is reserved, and the platform's own events are client-sourced, so a server may not emit one at all. Name your event something else.`));return}let G={},F={};for(let[w,C]of Object.entries(T))typeof C=="string"?G[w]=C:typeof C=="number"&&Number.isFinite(C)&&(F[w]=C);for(;r.length>=e;)r.shift(),o+=1;r.push({uid:pe(),playerId:R,name:x,atMs:n.clock.now(),dimensions:G,measures:F}),r.length>=xt&&l===null&&y()===0?O().catch(()=>{}):S();},flush:R=>O(R),get pending(){return r.length},dispose(){s=true,h(),r=[],o=0;}}}var Ot=new Map([["function_error",false],["function_failed",false],["function_timeout",false],["function_not_published",false],["function_input_too_large",false],["functions_not_configured",false],["forbidden",false],["function_capacity_unavailable",true],["function_host_unreachable",true],["function_host_error",true],["rate_limited",true],["service_unavailable",true]]),$=class extends m{constructor(e,t){super(e.reason,e.status,e.detail,e.retryAfterSeconds,e.code),this.name="GemFunctionError",Object.assign(this,t);}get retryable(){return (this.code===void 0?void 0:Ot.get(this.code))??super.retryable}},L=class{#e;constructor(e){this.#e=e;}async invoke(e,t={}){let r=Tt(e),o;try{o=await this.#e(r,t.signal===void 0?{}:{signal:t.signal});}catch(a){throw a instanceof m?Ct(a):a}return Gt(o)}};function Tt(n){if(n instanceof ArrayBuffer)return new Uint8Array(n);if(ArrayBuffer.isView(n))return new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let e;try{e=JSON.stringify(n);}catch(t){throw new f(`the function input could not be serialised to JSON: ${String(t)}. Pass bytes, or a value JSON can carry. This is a bug in the call, not a server failure.`)}if(e===void 0)throw new f(`the function input is ${typeof n}, which JSON cannot carry. Pass bytes \u2014 an empty Uint8Array for no input \u2014 or a JSON value. This is a bug in the call, not a server failure.`);return new TextEncoder().encode(e)}function Gt(n){let{bytes:e}=n,t=()=>new TextDecoder("utf-8",{fatal:true}).decode(e);return {bytes:e,...n.invocationId===void 0?{}:{invocationId:n.invocationId},text:t,json:()=>JSON.parse(t())}}function Ct(n){let{details:e,invocationId:t}=te(n),o=e?.untrusted?.message,a=e?.reason;return new $(n,{...t===void 0?{}:{invocationId:t},...n.code==="function_error"&&typeof o=="string"?{functionMessage:o}:{},...n.code==="function_failed"&&typeof a=="string"?{failure:a}:{}})}var P=n=>typeof n=="string"?n:"",Et=/(?:Z|[+-]\d{2}:?\d{2})$/i;function he(n){let e=P(n);if(!Et.test(e))return 0;let t=Date.parse(e);return Number.isFinite(t)?Math.floor(t/1e3):0}var It=/^[0-9a-f]{1,64}$/,Mt=/^[0-9a-f]{16}$/,Lt=/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/,qt=/^(?:[01]\d|2[0-3]):[0-5]\d$/;function $t(n){let e=P(n),t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!t)return;let[r,o,a]=[Number(t[1]),Number(t[2]),Number(t[3])],s=new Date(Date.UTC(r,o-1,a));return s.getUTCFullYear()===r&&s.getUTCMonth()===o-1&&s.getUTCDate()===a?e:void 0}var jt=4096,Ft=new TextEncoder;function Bt(n){let e=P(n.challenge_data),t=P(n.challenge_data_hash);return e===""||!Mt.test(t)?{}:Ft.encode(e).length>jt?{}:{data:e,dataHash:t}}function Ut(n){let e=n??{},t=he(e.period_start_at),r=he(e.period_ends_at);if(t===0||r===0)return;let o=P(e.slug),a=P(e.seed),s=$t(e.period_local_date),l=P(e.boundary),u=P(e.reset_time);return {id:P(e.progression_definition_id),...o===""?{}:{slug:o},window:P(e.window),periodStartAt:t,periodEndsAt:r,...s===void 0?{}:{periodLocalDate:s},boundary:Lt.test(l)?l:"",resetTime:qt.test(u)?u:"",seed:It.test(a)?a:"",...Bt(e)}}function _e(n){if(n!==void 0&&(!Number.isInteger(n)||n<0||n>400))throw new TypeError(`periodsBack must be an integer between 0 and 400, got ${String(n)}`)}function be(n){let e=n??{};return Array.isArray(e.challenges)?e.challenges.map(Ut).filter(t=>t!==void 0):[]}function ve(n){return {async list(e){let{playerId:t,periodsBack:r,signal:o}=e??{};if(typeof t!="string"||t==="")throw new TypeError("gem.challenges.list needs { playerId }: a game-server token names no player, so the platform cannot tell whose period you mean. Pass the id from join() or gem.players().");_e(r);let a=await n({player_id:t,...r===void 0?{}:{periods_back:r},...o===void 0?{}:{signal:o}});return be(a)}}}function Nt(n){let e={};for(let[t,r]of Object.entries(Y))if(typeof r=="function"){let o=r;e[t]=(...a)=>o(n,...a);}return e}function H(n,e={}){let t=e.clock??M,r=e.baseUrl??n.apiBaseUrl;if(!r)throw new Error("this runtime names no gem-api host (host.apiBaseUrl is missing). The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let o=z(n,{clock:t,...e.onAuthStateChange===void 0?{}:{onStateChange:e.onAuthStateChange}}),a=re({baseUrl:r,tokenSource:o.source,defaultQuery:{game_id:n.gameId,channel:n.channel},...e.fetch===void 0?{}:{fetch:e.fetch}}),s=new E({transport:a,clock:t}),l={gameId:n.gameId,channel:n.channel,matchId:n.matchId,...n.gameRoomId===void 0?{}:{gameRoomId:n.gameRoomId}},u=Nt({client:s,gameId:()=>l.gameId}),c=l.gameRoomId,g=n.endMatch,_=n.log,v=fe({client:s,clock:t,gameId:l.gameId,...typeof _=="function"?{log:y=>_.call(n,y)}:{}});return {identity:l,client:s,api:u,match:{end:async()=>{if(g===void 0)throw new Error("this runtime cannot end the match: the server image running this bundle predates host.endMatch, and only the platform half holds the allocation proof an end needs.");await g.call(n);},setBackfill:async(y,S)=>{let A=await u.setMatchBackfill(l.matchId,{open:y.open,...y.seats===void 0?{}:{seats:y.seats}},S===void 0?{}:{signal:S});return {open:A.open,seats:A.seats??null}},reportPlayers:async(y,S)=>(await u.reportMatchPlayers(l.matchId,{players:y.map(O=>({player_id:O.playerId,state:O.state}))},S===void 0?{}:{signal:S})).players.map(O=>({playerId:O.player_id,outcome:O.outcome}))},analytics:v,functions:new L(u.invokeFunction),challenges:ve(u.listChallenges),...c===void 0?{}:{room:{get:y=>u.getGameRoomV2(c,y),args:async y=>{let S=await u.getGameRoomV2(c,y?.signal===void 0?{}:{signal:y.signal}),A=S.modified?S.body:void 0;if(A?.room_args==null||A.room_args_template==null)throw new m("not_found",404,"this room's template declares no room args",void 0,"room_args_not_defined");return {room_args:A.room_args,room_args_template:A.room_args_template}}}},dispose:()=>{v.dispose(),o.dispose();}}}var Dt=["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"],Ht=new Set(Dt);function Re(n){return typeof n=="string"&&Ht.has(n)?n:"unknown"}var Se="[gem: use gem.text.toPlain()]",xe=new Set,ke;function Vt(n){try{if(xe.has(n))return;let e=globalThis.location;if(ke??=!!e?.port&&de(e.hostname??""),!ke)return;xe.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Se}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
1
+ var Ee=Object.defineProperty;var Ie=(n,e)=>{for(var t in e)Ee(n,t,{get:e[t],enumerable:true});};globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:15.1.1]";var Me={initialDelayMs:2e3,maxDelayMs:6e4};function Le(n,e){return e!==void 0?!e:n==="chain_exhausted"}var B=class{constructor(e){this.options=e;}options;grant=null;state="pending";failure=null;generation=0;inFlight=null;rejected=false;retry=null;failures=0;notBefore=0;disposed=false;get currentState(){return this.state}get lastFailure(){return this.failure}async get(){if(this.state==="lost")throw new b(this.failure??"unavailable","the session cannot be renewed");let e=this.seconds();if(this.grant&&e<this.grant.expiresAt-this.renewBefore())return this.grant.token;if(this.pastChainCeiling())throw new b("chain_exhausted","the token chain reached its ceiling");if(this.inFlight===null&&this.options.clock.now()<this.notBefore)throw new b(this.failure??"unavailable","the embedder asked for no new token yet; one is asked for when that wait is over");return this.refresh(this.nextReason())}pastChainCeiling(){return this.grant===null||this.seconds()<this.grant.chainEndsAt?false:(this.enter("lost","chain_exhausted"),true)}nextReason(){return this.rejected?"rejected":this.grant?"expiring":"initial"}invalidate(e){e!==void 0&&this.grant!==null&&this.grant.token!==e||this.rejected&&this.grant===null||(this.grant=null,this.generation+=1,this.inFlight=null,this.rejected=true,this.state!=="lost"&&this.enter("pending"));}accept(e){this.state!=="lost"&&(this.grant=e,this.rejected=false,this.enter("ready"));}close(e="unavailable"){this.grant=null,this.generation+=1,this.inFlight=null,this.enter("lost",e);}dispose(){this.disposed=true,this.cancelRetry();}scheduleRetry(e){if(this.cancelRetry(),this.failures+=1,this.notBefore=e===void 0?0:this.options.clock.now()+e,this.disposed)return;let{initialDelayMs:t,maxDelayMs:r}=Me,o=Math.min(r,t*2**(this.failures-1)),a=Math.max(o/2,e??0)+Math.random()*(o/2);this.retry={handle:this.options.clock.setTimeout(()=>this.retryNow(),a)};}retryNow(){this.retry=null,this.notBefore=0,!(this.state==="lost"||this.pastChainCeiling())&&this.refresh(this.nextReason()).catch(()=>{});}cancelRetry(){this.retry!==null&&(this.options.clock.clearTimeout(this.retry.handle),this.retry=null);}refresh(e){if(this.inFlight)return this.inFlight;this.cancelRetry();let t=this.generation;this.enter("pending");let r=this.options.request(e).then(o=>{if(t!==this.generation)throw new b("internal","token arrived for a superseded generation");return this.grant=o,this.rejected=false,this.enter("ready"),o.token}).catch(o=>{let a=qe(o),{retryable:s,retryAfterMs:l}=o??{};throw t===this.generation&&(Le(a,typeof s=="boolean"?s:void 0)?this.enter("lost",a):(this.scheduleRetry(typeof l=="number"&&Number.isFinite(l)&&l>0?l:void 0),this.enter("degraded",a))),o instanceof b?o:new b(a,String(o))}).finally(()=>{this.inFlight===r&&(this.inFlight=null);});return this.inFlight=r,r}enter(e,t){(e==="ready"||e==="lost")&&this.cancelRetry(),e==="ready"&&(this.failures=0,this.notBefore=0),this.failure=t??(e==="ready"?null:this.failure),this.state!==e&&(this.state=e,this.options.onStateChange?.(e,t));}renewBefore(){return this.options.renewBeforeSeconds??60}seconds(){return Math.floor(this.options.clock.now()/1e3)}},b=class n extends Error{constructor(t,r){super(`auth ${t}: ${r}`);this.reason=t;this.name="AuthError";}reason;static isAuthError(t){if(t instanceof n)return true;let r=t;return r?.name==="AuthError"&&typeof r.reason=="string"}};function qe(n){if(n instanceof b)return n.reason;let e=n?.reason;return typeof e=="string"&&["chain_exhausted","unavailable","timeout","internal"].includes(e)?e:"internal"}var f=class n extends Error{retryable=false;constructor(e){super(e),this.name="TransportRefusal";}static isRefusal(e){if(e instanceof n)return true;let t=e;return t?.name==="TransportRefusal"&&t.retryable===false}},U=["if-match","if-none-match"],X=4096;function $e(n){if(!n)return;let e={};for(let[t,r]of Object.entries(n)){let o=t.toLowerCase();if(!U.includes(o)||r===void 0)continue;let a=typeof r=="string"?je(r):r;if(typeof a!="string"||a.length>X||!Fe(a))throw new f(`the ${o} header value cannot be sent \u2014 it is not a string, is longer than ${X} characters, or contains a character outside the range an HTTP header value may carry. This is a malformed request, not a server failure; the value is withheld here rather than written to logs.`);a!==""&&(e[o]=a);}return Object.freeze(e)}function je(n){let e=o=>o===32||o===9||o===10||o===13,t=0,r=n.length;for(;t<r&&e(n.charCodeAt(t));)t++;for(;r>t&&e(n.charCodeAt(r-1));)r--;return n.slice(t,r)}function Fe(n){for(let e=0;e<n.length;e++){let t=n.charCodeAt(e);if(t!==9&&(t<32||t===127||t>255))return false}return true}function Be(n){let{bytes:e}=n;if(e!==void 0){if(Object.prototype.toString.call(e)!=="[object Uint8Array]")throw new f("the raw body is not a Uint8Array, so it cannot be sent as bytes. This is a malformed request, not a server failure.");if(n.body!==void 0)throw new f("the request carries both a JSON body and a raw body, and only one can be sent. This is a malformed request, not a server failure.");return new Uint8Array(e)}}function Q(n,e){let t=e.replayUnauthorizedOnce??true;return {async request(r){let o={...r},a=$e(o.headers),s=Be(o),l=Object.freeze({...o,headers:a,...s===void 0?{}:{bytes:s}}),u=await e.tokenSource.get(),c=await n(l,u);if(c.status!==401||!t)return c;e.tokenSource.invalidate(u);let g=await e.tokenSource.get(),_=await n(l,g);if(_.status===401)throw e.tokenSource.close("unavailable"),new b("unavailable","the server refused a freshly minted token");return _}}}function Z(n){let e=typeof n=="string"?new TextEncoder().encode(n):n;return btoa(Ue(e))}function Ue(n){let t="";for(let r=0;r<n.length;r+=32768)t+=String.fromCharCode(...n.subarray(r,r+32768));return t}var m=class extends Error{constructor(t,r,o,a,s){super(`${t} (${r}): ${o}`);this.reason=t;this.status=r;this.detail=o;this.retryAfterSeconds=a;this.code=s;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}},ne=new WeakMap;function te(n){return ne.get(n)??{}}var Ne=new Set([408,429,500,502,503,504]),De=6e4;function He(n){if(n===void 0)return 2;if(!Number.isInteger(n)||n<0||n>10)throw new f(`maxRetries must be a whole number between 0 and 10; received ${JSON.stringify(n)}. This is a configuration fault, not a server failure.`);return n}var E=class{constructor(e){this.options=e;}options;async get(e,t){return this.send({method:"GET",path:e,signal:t},true)}async getWithEtag(e,t={}){let r=await this.sendRaw({method:"GET",path:e,...t.signal===void 0?{}:{signal:t.signal},...t.ifNoneMatch===void 0?{}:{headers:{"if-none-match":t.ifNoneMatch}}},true),o=r.etag===void 0?{}:{etag:r.etag};return r.status===304?{modified:false,...o}:{modified:true,body:r.body,...o}}async put(e,t,r,o={}){return this.send({method:"PUT",path:e,body:t,signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false)}async post(e,t,r,o={}){return this.send({method:"POST",path:e,body:t,signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false)}async postBytes(e,t,r,o={}){let a=await this.sendRaw({method:"POST",path:e,bytes:t,responseType:"bytes",signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false);return {bytes:Ve(a.body),...a.invocationId===void 0?{}:{invocationId:a.invocationId}}}async patch(e,t,r,o={}){return this.send({method:"PATCH",path:e,body:t,signal:r,...q(o)},o.replaySafe===true,o.retryRateLimited!==false)}async delete(e,t,r={}){return this.send({method:"DELETE",path:e,signal:t,...q(r)},r.replaySafe===true,r.retryRateLimited!==false)}async send(e,t,r=true){return (await this.sendRaw(e,t,r)).body}async sendRaw(e,t,r=true){let o=t?He(this.options.maxRetries)+1:1,a=null;if(e.signal?.aborted)throw ee(e.signal);for(let s=0;s<o;s++){if(s>0&&await this.backoff(s,a,e.signal),e.signal?.aborted)throw ee(e.signal);let l;try{l=await this.options.transport.request(e);}catch(c){if(b.isAuthError(c)||f.isRefusal(c)||e.signal?.aborted||We(c))throw c;a=new m("unavailable",0,String(c));continue}if(l.status>=200&&l.status<300||l.status===304)return l;let u=ze(l);if(!Ne.has(l.status)||l.status===429&&!r)throw u;a=u;}throw a??new m("unavailable",0,"no attempt was made")}backoff(e,t,r){if(r?.aborted)return Promise.resolve();let o=t?.retryAfterSeconds,a=o!==void 0?o*1e3:2**(e-1)*250,s=2**(e-1)*250,l=Math.min(Math.max(a,s),De);return new Promise(u=>{let c=this.options.clock,g=false,_=false,v,h=()=>{g||(g=true,_&&c.clearTimeout(v),r?.removeEventListener("abort",h),u());};r?.addEventListener("abort",h,{once:true}),v=c.setTimeout(h,l),_=true,g&&c.clearTimeout(v);})}};function Ve(n){if(n instanceof Uint8Array)return n;if(n==null)return new Uint8Array(0);let e=typeof n=="string"?n:JSON.stringify(n);return new TextEncoder().encode(e)}function q(n){return {...n.keepalive===void 0?{}:{keepalive:n.keepalive},...n.ifMatch===void 0?{}:{headers:{"if-match":n.ifMatch}}}}function ee(n){return n.reason!==void 0?n.reason:new DOMException("The operation was aborted.","AbortError")}function We(n){let e=n?.name;return e==="AbortError"||e==="TimeoutError"}function ze(n){let e=Ye(n),t=Xe(n.body),r={...t===void 0?{}:{details:t},...n.invocationId===void 0?{}:{invocationId:n.invocationId}};return Object.keys(r).length>0&&ne.set(e,r),e}var Ke=new Map([["insufficient_funds","insufficient_funds"]]);function Ye(n){let{status:e,body:t}=n,r=Je(t),o=n.retryAfterSeconds??Ze(t),a=Qe(t),s=a===void 0?void 0:Ke.get(a);if(s!==void 0)return new m(s,e,r,void 0,a);switch(e){case 304:return new m("not_modified",e,r);case 400:case 422:return new m("invalid",e,r,void 0,a);case 403:return new m("forbidden",e,r,void 0,a);case 404:return new m("not_found",e,r,void 0,a);case 402:return new m("insufficient_funds",e,r,void 0,a);case 409:return new m("conflict",e,r,void 0,a);case 412:return new m("precondition_failed",e,r,void 0,a);case 413:return new m("too_large",e,r,void 0,a);case 429:return new m("rate_limited",e,r,o,a);default:return new m("unavailable",e,r,o,a)}}function Je(n){if(typeof n=="string")return n;let e=n?.message;if(typeof e=="string")return e;let t=n?.error;if(typeof t=="string")return t;let r=t?.message;return typeof r=="string"?r:"no detail"}function Xe(n){let t=n?.error?.details;return t===null?void 0:t}function Qe(n){let t=n?.error?.code;return typeof t=="string"&&t.length>0?t:void 0}function Ze(n){let e=n?.retryAfterSeconds;return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:void 0}function en(n){let e=n.lastIndexOf("@");return (e===-1?n:`***@${n.slice(e+1)}`).slice(0,200)}var nn=new Set(U);function tn(n){if(!n)return {};let e={};for(let[t,r]of Object.entries(n))nn.has(t.toLowerCase())&&(e[t.toLowerCase()]=r);return e}function re(n){let e=n.fetch??globalThis.fetch.bind(globalThis);return Q(async(t,r)=>{let o;try{o=new URL(n.baseUrl.replace(/\/+$/,""));}catch{throw new f("the configured baseUrl is not a valid URL. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure.")}let a;try{a=new URL(o.href.replace(/\/+$/,"")+t.path);}catch{throw new f(`${JSON.stringify(typeof t.path=="string"?en(t.path):"(a non-string path)")} does not form a valid URL against ${o.protocol}//${o.host}. This is a malformed request, not a server failure.`)}if(a.protocol!==o.protocol||a.host!==o.host)throw new f(`${JSON.stringify(a.pathname)} resolves to ${a.protocol}//${a.host}, not ${o.protocol}//${o.host}. A request path may not change the request's origin \u2014 it carries the play token.`);if(a.username!==""||a.password!==""){let h=o.username!==""||o.password!=="",y=a.username!==o.username||a.password!==o.password,S=h?y?"baseUrl (and possibly the request path)":"baseUrl":"request path";throw new f(`the ${S} puts credentials in the URL, which cannot be fetched (${t.method} ${a.pathname}). The value is withheld here rather than written to logs. This is a malformed request, not a server failure.`)}for(let[h,y]of Object.entries(n.defaultQuery??{}))a.searchParams.has(h)||a.searchParams.set(h,y);let s=t.bytes,l=t.bytes===void 0?void 0:"application/octet-stream";try{t.body!==void 0&&(s=JSON.stringify(t.body),l="application/json");}catch(h){throw new f(`the body of ${t.method} ${JSON.stringify(a.pathname)} could not be serialised to JSON: ${String(h)}. This is a bug in the request, not a server failure \u2014 retrying sends the same unserialisable value.`)}let u=await e(a.toString(),{method:t.method,headers:{authorization:`Bearer ${r}`,...l===void 0?{}:{"content-type":l},...tn(t.headers)},body:s,signal:t.signal,...t.keepalive===void 0?{}:{keepalive:t.keepalive},credentials:"omit",mode:"cors",redirect:"error",cache:"no-store",referrerPolicy:"no-referrer"}),c=null;if(t.responseType==="bytes"&&u.status>=200&&u.status<300)c=new Uint8Array(await u.arrayBuffer());else {let h=await u.text();if(h.length>0)try{c=JSON.parse(h);}catch{c=h;}}let g=u.headers.get("etag"),_=rn(u.headers.get("retry-after")),v=u.headers.get("x-gem-invocation-id");return {status:u.status,body:c,...g===null?{}:{etag:g},..._===void 0?{}:{retryAfterSeconds:_},...v===null||v===""?{}:{invocationId:v}}},n)}function rn(n){if(n===null)return;let e=n.trim();if(!/^\d+$/.test(e))return;let t=Number(e);return Number.isFinite(t)?t:void 0}var on="gem-web-bridge";var I=65536,oe=8,N=5e3,ae=1024;var D=Object.create(null);var ie=Object.create(null);function an(n){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(n);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function sn(n){let e;try{e=new URL(n);}catch{return false}return e.username!==""||e.password!==""?false:e.protocol==="https:"?true:e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1")}function ln(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function dn(n){return /^[a-z_]+$/.test(n)}function V(n,e,t){if(e>oe)return `nesting deeper than ${oe}`;if(++t.nodes>N)return `more than ${N} values`;if(t.bytes>I)return `over the ${I} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let o=n;if(t.seen.has(o))return "a repeated or cyclic object reference";if(t.seen.add(o),Array.isArray(n)){if(n.length>ae)return `an array of ${n.length} entries, over the ${ae} limit`;t.bytes+=2;for(let l of n){let u=V(l,e+1,t);if(u)return u}return null}let a=Object.getPrototypeOf(n);if(a!==Object.prototype&&a!==null)return "a value that is not a plain object";t.bytes+=2;let s=n;for(let l in s){if(!Object.prototype.hasOwnProperty.call(s,l))continue;if(++t.nodes>N)return `more than ${N} values`;if(t.bytes+=l.length*3+4,t.bytes>I)return `over the ${I} byte limit`;let u=V(s[l],e+1,t);if(u)return u}return null}function un(n){let e={nodes:0,bytes:0,seen:new Set},t=V(n,0,e);return t?`message contains ${t}`:e.bytes>I?`message is over the ${I} byte limit`:null}var cn={origin:[an,"not a well-formed origin"],url:[sn,"not a safe absolute http(s) URL"],bcp47:[ln,"not a well-formed BCP 47 tag"],code:[dn,"not a code of lower-case letters and underscores"]};function se(n,e,t){for(let r of e){let o=t===""?r.n:`${t}.${r.n}`,a=n[r.n];if(a==null){if(r.o)continue;return `${o}: required`}let s=W(a,r,o);if(s)return s}return null}function W(n,e,t){switch(e.k){case "u":return typeof n=="boolean"?null:W(n,{...e,k:typeof n=="string"?"s":"n"},t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e.max)return `${t}: exceeds ${e.max} characters`;if(e.f){let[r,o]=cn[e.f];if(!r(n))return `${t}: ${o}`}return null}case "n":case "i":return typeof n!="number"||!(e.k=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${e.k=="i"?"integer":"number"}`:n<e.lo||n>e.hi?`${t}: out of range ${e.lo}..${e.hi}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let r=e.v;return r.includes(n)?null:`${t}: not one of ${r.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:se(n,e.fs,t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e.max)return `${t}: exceeds ${e.max} items`;let r=e.it,o=`${t}[]`;for(let a of n){if(a==null){if(r.o)continue;return `${o}: required`}let s=W(a,r,o);if(s)return s}return null}default:{let r=e.k;return `${t}: unknown kind ${String(r)}`}}}var le=Object.create(null);function d(n,e,t,r){return D[n]=t,r&&(ie[n]=r),le[n]=o=>{let a=un(o);if(a)return a;if(typeof o!="object"||o===null||Array.isArray(o))return "message: expected object";let s=o;return s.gem!==on?"gem: wrong protocol marker":s.type!==n?`type: expected ${n}`:se(s,e,"")}}var p={n:"requestId",k:"s",max:64};d("gem.handshake",[{n:"versions",k:"o",fs:[{n:"min",k:"i",lo:1,hi:1e3},{n:"max",k:"i",lo:1,hi:1e3}]},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}},{n:"embedder",k:"s",max:2048,f:"origin"},{n:"unbidden",k:"b",o:1}],30);d("gem.handshake.accept",[{n:"v",k:"i",lo:1,hi:1e3},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}}],5);d("gem.token",[{n:"token",k:"s",max:4096},{n:"requestId",k:"s",o:1,max:64},{n:"generation",k:"i",lo:0,hi:2147483647},{n:"apiBaseUrl",k:"s",o:1,max:512,f:"url"},{n:"playerId",k:"s",max:64},{n:"gameId",k:"s",max:64},{n:"channel",k:"e",v:["dev","test","release"]},{n:"gameVersion",k:"s",o:1,max:64},{n:"expiresAt",k:"i",lo:0,hi:4102444800},{n:"chainEndsAt",k:"i",lo:0,hi:4102444800},{n:"allowDevTools",k:"b",o:1}],30);d("gem.token.denied",[p,{n:"retryable",k:"b"},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:36e5},{n:"reason",k:"e",v:["chain_exhausted","unavailable"]},{n:"correlationId",k:"s",max:64}],30);d("gem.player",[{n:"untrusted",k:"o",fs:[{n:"displayName",k:"s",max:64},{n:"displayTag",k:"s",o:1,max:80},{n:"displayHandle",k:"s",max:80}]},{n:"playerId",k:"s",o:1,max:64},{n:"discriminator",k:"s",o:1,max:8},{n:"avatarIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatarIconId",k:"s",o:1,max:64},{n:"avatar3dId",k:"s",o:1,max:64},{n:"avatar3dModelUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatar3dIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatars3d",k:"a",o:1,max:8,it:{n:"",k:"o",fs:[{n:"family",k:"s",max:32},{n:"id",k:"s",max:64},{n:"modelUrl",k:"s",max:256,f:"url"},{n:"iconUrl",k:"s",max:256,f:"url"}]}},{n:"badges",k:"a",o:1,max:2,it:{n:"",k:"o",fs:[{n:"itemId",k:"s",max:64},{n:"iconUrl",k:"s",max:2048,f:"url"}]}},{n:"displayTag",k:"s",max:8},{n:"avatarUrl",k:"s",o:1,max:2048,f:"url"},{n:"boost",k:"o",o:1,fs:[{n:"multipliers",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"currencyId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"multiplierBp",k:"i",lo:1e4,hi:1e6}]}},{n:"expiresInDays",k:"i",o:1,lo:1,hi:7}]}],12);d("gem.pause",[{n:"paused",k:"b"}],120);d("gem.prefs",[{n:"reducedMotion",k:"b"},{n:"muted",k:"b"},{n:"showFrameStats",k:"b",o:1},{n:"music",k:"b",o:1},{n:"sounds",k:"b",o:1},{n:"locale",k:"s",max:35,f:"bcp47"},{n:"recommended",k:"o",o:1,fs:[{n:"volume",k:"i",lo:0,hi:100},{n:"batterySaver",k:"b"},{n:"keyboardLayout",k:"e",v:["wasd","arrows"]},{n:"gamepadConfirm",k:"e",v:["south","east"]},{n:"vibration",k:"b"},{n:"lookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"gamepadLookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"invertLookY",k:"b",o:1},{n:"touchMovement",k:"s",o:1,max:16,f:"code"},{n:"graphicsQuality",k:"s",o:1,max:16,f:"code"},{n:"graphicsQualityAuto",k:"b",o:1}]}],60);d("gem.cohorts",[{n:"cohorts",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"experimentId",k:"s",max:36},{n:"variantKey",k:"s",max:32}]}}],12,"cohorts");d("gem.earnAllowanceChanged",[],30,"earnAllowance");d("gem.viewport",[{n:"insets",k:"o",fs:[{n:"top",k:"i",lo:0,hi:4096},{n:"right",k:"i",lo:0,hi:4096},{n:"bottom",k:"i",lo:0,hi:4096},{n:"left",k:"i",lo:0,hi:4096}]},{n:"reserved",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384},{n:"control",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384}]}]}}],60,"viewport");d("gem.deviceSettings",[{n:"settings",k:"j",o:1}],12,"deviceSettings");d("gem.willTerminate",[{n:"reason",k:"e",v:["grant_expiring","session_ended","navigating","kill_switch"]},{n:"graceMs",k:"i",lo:0,hi:6e4}],10);d("gem.roomEvent",[{n:"event",k:"e",v:["updated","match_started","match_ready","match_failed","version_stale"]},{n:"roomId",k:"s",max:64},{n:"matchId",k:"s",o:1,max:64},{n:"version",k:"i",o:1,lo:0,hi:2147483647},{n:"reason",k:"s",o:1,max:64},{n:"platform",k:"e",o:1,v:["universal","mobile"]}],120,"rooms");d("gem.socketState",[{n:"connected",k:"b"},{n:"reason",k:"e",o:1,v:["connecting","dropped","watchdog","terminal"]}],12,"socketState");d("gem.seatedEvent",[{n:"roomId",k:"s",max:64}],60,"partySeating");d("gem.autoStartFailedEvent",[{n:"roomId",k:"s",max:64},{n:"code",k:"s",max:64}],30,"autoStartFailure");d("gem.ticketEvent",[{n:"event",k:"e",v:["queued","matched","expired","cancelled","failed"]},{n:"ticketId",k:"s",max:64},{n:"partySize",k:"i",o:1,lo:1,hi:64},{n:"queuedAt",k:"i",o:1,lo:0,hi:4102444800},{n:"expiresAt",k:"i",o:1,lo:0,hi:4102444800},{n:"reason",k:"s",o:1,max:64},{n:"roomId",k:"s",o:1,max:64}],60,"ticketEvents");d("gem.signalEvent",[{n:"event",k:"e",v:["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1800,"p2p");d("gem.dedicatedEvent",[{n:"event",k:"e",v:["answer","error","matchEnded"]},{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",o:1,max:10240},{n:"sessionId",k:"s",o:1,max:64},{n:"code",k:"s",o:1,max:64},{n:"message",k:"s",o:1,max:256},{n:"reason",k:"s",o:1,max:64}],120,"dedicated");d("gem.partyResult",[p,{n:"partyId",k:"s",o:1,max:64}],30,"parties");d("gem.clusterTarget",[{n:"clusterTargetId",k:"s",max:64},{n:"clusterTargetIds",k:"a",o:1,max:3,it:{n:"",k:"s",max:64}}],12,"clusterTarget");d("gem.devtools",[{n:"action",k:"e",v:["open","close","toggle"]}],30,"devtools");d("gem.screenshot",[{n:"maxWidth",k:"i",lo:1,hi:7680},{n:"maxHeight",k:"i",lo:1,hi:4320}],6,"screenshot");d("gem.screenshot.denied",[{n:"reason",k:"e",v:["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]},{n:"retryable",k:"b"}],12,"screenshot");d("gem.requestContext",[],12,"feedbackContext");d("gem.gameContext",[{n:"context",k:"j"}],12,"feedbackContext");d("gem.signal",[{n:"signal",k:"e",v:["join","offer","answer","ice","migrationAck","hostUnreachable"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1200,"p2p");d("gem.dedicatedOffer",[{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",max:10240},{n:"intent",k:"e",v:["initial","ice_restart","full_reconnect"]}],60,"dedicated");d("gem.partyRequest",[p],12,"parties");d("gem.ready",[],5);d("gem.engaged",[],5,"engagement");d("gem.progress",[{n:"loaded",k:"i",lo:0,hi:1099511627776},{n:"total",k:"i",lo:0,hi:1099511627776}],600);d("gem.requestToken",[p,{n:"reason",k:"e",v:["initial","expiring","rejected"]}],12);d("gem.requestLocalServer",[p,{n:"gameRoomId",k:"s",max:64}],6,"localHost");d("gem.localServer",[p,{n:"serverId",k:"s",o:1,max:128}],30,"localHost");d("gem.localServer.denied",[p,{n:"retryable",k:"b"},{n:"reason",k:"e",v:["refused","unavailable"]}],30,"localHost");d("gem.exit",[{n:"reason",k:"s",o:1,max:64}],5);d("gem.openExternal",[{n:"url",k:"s",max:2048,f:"url"}],6);d("gem.requestFullscreen",[{n:"enabled",k:"b"}],30);d("gem.audioState",[{n:"muted",k:"b"}],120,"audioState");d("gem.rewardCue",[{n:"currencySlug",k:"s",max:32},{n:"expected",k:"i",o:1,lo:1,hi:1e9},{n:"origin",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384}]}],60,"rewardCue");d("gem.walletDisplay",[{n:"visible",k:"b"},{n:"currencies",k:"a",o:1,max:8,it:{n:"",k:"s",max:64}},{n:"score",k:"b",o:1}],60,"walletDisplay");d("gem.controlsDeclare",[{n:"uses",k:"o",fs:[{n:"lookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"gamepadLookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"invertLookY",k:"s",o:1,max:16,f:"code"},{n:"touchMovement",k:"s",o:1,max:16,f:"code"}]}],60,"controls");d("gem.menu",[{n:"settings",k:"b"},{n:"items",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:32},{n:"label",k:"s",max:40}]}}],60,"gameMenu");d("gem.menuSelect",[{n:"entry",k:"e",v:["settings","item"]},{n:"id",k:"s",o:1,max:32}],60,"gameMenu");d("gem.gemCorner",[{n:"corner",k:"e",v:["top-left","top-right","bottom-left","bottom-right"]}],12,"gemCorner");d("gem.settingsDeclare",[{n:"settings",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:40},{n:"label",k:"s",max:40},{n:"kind",k:"e",v:["toggle","slider","choice"]},{n:"value",k:"u",max:40,lo:-1e6,hi:1e6},{n:"quick",k:"b",o:1},{n:"icon",k:"s",o:1,max:16,f:"code"},{n:"min",k:"n",o:1,lo:-1e6,hi:1e6},{n:"max",k:"n",o:1,lo:-1e6,hi:1e6},{n:"step",k:"n",o:1,lo:0,hi:1e6},{n:"options",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:40},{n:"label",k:"s",max:40}]}}]}}],120,"gameSettings");d("gem.settingChange",[{n:"id",k:"s",max:40},{n:"value",k:"u",max:40,lo:-1e6,hi:1e6}],120,"gameSettings");d("gem.requestShare",[{n:"params",k:"a",max:16,it:{n:"",k:"o",fs:[{n:"key",k:"s",max:64},{n:"value",k:"s",max:512}]}}],6,"share");d("gem.requestAvatarPicker",[{n:"tab",k:"e",o:1,v:["2d","3d"]}],6,"avatarPicker");d("gem.vibrate",[{n:"pattern",k:"a",max:99,it:{n:"",k:"i",lo:0,hi:1e4}}],600,"vibration");d("gem.frameStats",[{n:"fps",k:"i",lo:0,hi:1e3},{n:"frameTimeP95Ms",k:"i",lo:0,hi:1e5},{n:"counts",k:"e",o:1,v:["pageFrames","gameDraws"]}],120,"frameStats");d("gem.portraitBorders",[{n:"name",k:"s",o:1,max:40}],60,"portraitBorders");d("gem.error",[{n:"code",k:"e",v:["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]},{n:"message",k:"s",o:1,max:256}],60);d("gem.requestPurchase",[p,{n:"purchaseKind",k:"e",v:["item","recipe"]},{n:"targetId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"quantity",k:"i",o:1,lo:1,hi:10},{n:"catalogId",k:"s",o:1,max:64}],12,"purchases");d("gem.requestAvatar3dPurchase",[p,{n:"itemId",k:"s",max:64},{n:"currencySlug",k:"s",max:32}],12,"avatar3dPurchases");d("gem.purchaseResult",[p,{n:"outcome",k:"e",v:["ok","declined","unavailable"]},{n:"reason",k:"s",o:1,max:64,f:"code"},{n:"purchaseId",k:"s",o:1,max:64}],30,"purchases");d("gem.deviceSettings.put",[p,{n:"settings",k:"j"}],30,"deviceSettings");d("gem.deviceSettings.result",[p,{n:"outcome",k:"e",v:["stored","too_large","unavailable"]}],30,"deviceSettings");d("gem.requestAd",[p,{n:"placement",k:"s",max:32},{n:"format",k:"e",v:["interstitial","rewarded"]}],6,"ads");d("gem.adResult",[p,{n:"outcome",k:"e",v:["shown","no_ad","unavailable"]}],12,"ads");d("gem.signalMoment",[p,{n:"kind",k:"e",v:["level_end","run_end","match_end","menu_return","other_break"]},{n:"placement",k:"s",o:1,max:32}],6,"moments");d("gem.momentResult",[p,{n:"shown",k:"b"}],12,"moments");d("gem.requestSignIn",[p],6,"signIn");d("gem.signInResult",[p,{n:"outcome",k:"e",v:["signed_in","declined","unavailable"]}],12,"signIn");d("gem.requestFriendChallenge",[p,{n:"playerId",k:"s",max:64},{n:"template",k:"s",max:64},{n:"params",k:"j",o:1}],12,"friendChallenges");d("gem.friendChallengeResult",[p,{n:"outcome",k:"e",v:["accepted","cancelled","refused","unavailable","rate_limited","invalid"]},{n:"code",k:"s",o:1,max:64},{n:"param",k:"s",o:1,max:64},{n:"channel",k:"s",o:1,max:16},{n:"configVersion",k:"i",o:1,lo:0,hi:9007199254740991},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:864e5},{n:"detail",k:"s",o:1,max:300}],30,"friendChallenges");Object.freeze(le);D.__malformed=60;Object.freeze(D);Object.freeze(ie);var M={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)};function de(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}var mn=1e4;function ue(n,e){return z(n,e)}function z(n,e){if(typeof n.serverToken!="function"||typeof n.onServerToken!="function")throw new Error("this runtime delivers no game-server token: host.serverToken/onServerToken are missing. The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let t=s=>({token:s.token,expiresAt:s.expiresAt,chainEndsAt:Number.POSITIVE_INFINITY}),r=null,o=new B({clock:e.clock,...e.onStateChange===void 0?{}:{onStateChange:e.onStateChange},request:()=>new Promise((s,l)=>{let u=e.clock.setTimeout(()=>{r=null,l({reason:"unavailable",retryable:true});},e.timeoutMs??mn);r={resolve:c=>{e.clock.clearTimeout(u),s(c);},reject:l,timer:u};})}),a=n.onServerToken(s=>{let l=t(s),u=r;if(u){r=null,u.resolve(l);return}o.accept(l);});return {source:o,dispose(){a(),o.dispose(),r&&(e.clock.clearTimeout(r.timer),r.reject({reason:"unavailable",retryable:false}),r=null);}}}var Y={};Ie(Y,{acceptTransfer:()=>gn,addEntityProgression:()=>pn,adminAction:()=>yn,cancelInventoryTransfer:()=>fn,claimEntityProgressionRewards:()=>hn,closeGameRoom:()=>_n,createEntityBatchOrder:()=>bn,createEntityOrder:()=>vn,createSession:()=>Rn,deleteEntityGameSetting:()=>xn,deleteEntitySave:()=>kn,deleteSession:()=>Sn,deleteShowcaseValue:()=>wn,executeEntityRecipe:()=>An,finalizeSaveUpload:()=>Pn,getChannelAssignment:()=>On,getDropRates:()=>Tn,getEntityCurrency:()=>Gn,getEntityGameSetting:()=>Cn,getEntityInventory:()=>En,getEntityInventoryItem:()=>In,getEntityLeaderboard:()=>Mn,getEntityOrder:()=>Ln,getEntityProgression:()=>qn,getEntityRecipeAvailability:()=>$n,getEntityRecipeUsage:()=>jn,getEntitySave:()=>Fn,getEvent:()=>Bn,getGameRoomV2:()=>Un,getLeaderboard:()=>Nn,getPlayerGameProfile:()=>Dn,getPurchaseCatalog:()=>Hn,getResolvedTemplate:()=>Vn,getRoomArgs:()=>Wn,getShowcaseValue:()=>zn,getTransferDetails:()=>Kn,ingestAnalyticsEvents:()=>K,initiateInventoryTransfer:()=>Yn,invokeFunction:()=>Jn,kickPlayer:()=>Xn,listChallenges:()=>Qn,listEntityGameSettings:()=>Zn,listEntityOrders:()=>et,listEntityProgressions:()=>nt,listEntitySaves:()=>tt,listEvents:()=>rt,listGameRooms:()=>ot,listResolvedTemplates:()=>at,listTransfers:()=>it,maxEntityProgression:()=>st,putEntityGameSetting:()=>lt,putRoomArgs:()=>dt,putShowcaseValue:()=>ut,rejectTransfer:()=>ct,reportMatchPlayers:()=>mt,requestSaveUploadUrl:()=>gt,resetEntityProgression:()=>pt,setMatchBackfill:()=>yt,simulateEntityOrder:()=>ft,transferHostV2:()=>ht,updateGameRoomV2:()=>_t});function i(n){let e=String(n);if(!/^[A-Za-z0-9_.-]{1,128}$/.test(e)||e==="."||e==="..")throw new TypeError(`${JSON.stringify(e)} is not a usable path segment \u2014 it is interpolated into a URL`);return encodeURIComponent(e)}async function gn(n,e,t){let r=`/v1/inventory/transfers/${i(e)}/accept`;return n.client.post(r,void 0,t?.signal)}async function pn(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/add`;return n.client.post(s,o,a?.signal)}async function yn(n,e,t,r){let o=`/v1/voice/sessions/${i(e)}/admin`;return n.client.post(o,t,r?.signal)}async function fn(n,e,t){let r=`/v1/inventory/transfers/${i(e)}/cancel`;return n.client.post(r,void 0,t?.signal)}async function hn(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/claim`;return n.client.post(s,o,a?.signal)}async function _n(n,e,t){let r=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}`;return n.client.delete(r,t?.signal)}async function bn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders/batch`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function vn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Rn(n,e,t){return n.client.post("/v1/voice/sessions",e,t?.signal)}async function xn(n,e,t,r,o){let a=`/v1/settings/${i(e)}/${i(t)}/${i(r)}`;return n.client.delete(a,o?.signal)}async function kn(n,e,t,r,o){let a=`/v1/saves/${i(e)}/${i(t)}/${i(r)}`;return n.client.delete(a,o?.signal)}async function Sn(n,e,t){let r=`/v1/voice/sessions/${i(e)}`;return n.client.delete(r,t?.signal)}async function wn(n,e,t,r){let o=`/v1/showcase/players/${i(e)}/${i(t)}`;return n.client.delete(o,r?.signal)}async function An(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/recipes/execute`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Pn(n,e,t,r,o,a){let s=`/v1/saves/${i(e)}/${i(t)}/${i(r)}/finalize`;return n.client.post(s,o,a?.signal)}async function On(n,e,t){let r=`/v1/games/${i(n.gameId())}/config/channels/${i(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Tn(n,e,t){let r=`/v1/inventory/loot-tables/${i(e)}/drop-rates`;return n.client.get(r,t?.signal)}async function Gn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/currencies/${i(r)}`;return n.client.get(a,o?.signal)}async function Cn(n,e,t,r,o){let a=`/v1/settings/${i(e)}/${i(t)}/${i(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function En(n,e,t,r){let o=new URLSearchParams;r?.include_buckets!==void 0&&o.set("include_buckets",String(r?.include_buckets)),r?.include_custom_data!==void 0&&o.set("include_custom_data",String(r?.include_custom_data)),r?.currency_limit!==void 0&&o.set("currency_limit",String(r?.currency_limit)),r?.currency_cursor!==void 0&&o.set("currency_cursor",String(r?.currency_cursor)),r?.item_limit!==void 0&&o.set("item_limit",String(r?.item_limit)),r?.item_cursor!==void 0&&o.set("item_cursor",String(r?.item_cursor)),r?.progression_limit!==void 0&&o.set("progression_limit",String(r?.progression_limit)),r?.progression_cursor!==void 0&&o.set("progression_cursor",String(r?.progression_cursor));let a=o.toString(),s=`/v1/inventory/${i(e)}/${i(t)}`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function In(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/items/${i(r)}`;return n.client.get(a,o?.signal)}async function Mn(n,e,t,r,o){let a=new URLSearchParams;o?.bucket_1!==void 0&&a.set("bucket_1",String(o?.bucket_1)),o?.bucket_2!==void 0&&a.set("bucket_2",String(o?.bucket_2)),o?.limit!==void 0&&a.set("limit",String(o?.limit)),o?.periods_back!==void 0&&a.set("periods_back",String(o?.periods_back)),o?.period!==void 0&&a.set("period",String(o?.period)),o?.player_id!==void 0&&a.set("player_id",String(o?.player_id));let s=a.toString(),l=`/v1/leaderboards/${i(e)}/${i(t)}/${i(r)}`+(s?`?${s}`:"");return n.client.get(l,o?.signal)}async function Ln(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders/${i(r)}`;return n.client.get(a,o?.signal)}async function qn(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}`;return n.client.get(a,o?.signal)}async function $n(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/catalogs/${i(r)}/recipes/${i(o)}/availability`;return n.client.get(s,a?.signal)}async function jn(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit));let a=o.toString(),s=`/v1/inventory/${i(e)}/${i(t)}/recipe-usage`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function Fn(n,e,t,r,o){let a=`/v1/saves/${i(e)}/${i(t)}/${i(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function Bn(n,e,t){let r=`/v1/events/${i(e)}`;return n.client.get(r,t?.signal)}async function Un(n,e,t){let r=`/v2/games/${i(n.gameId())}/game-rooms/${i(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Nn(n,e,t){let r=new URLSearchParams;t?.bucket_1!==void 0&&r.set("bucket_1",String(t?.bucket_1)),t?.bucket_2!==void 0&&r.set("bucket_2",String(t?.bucket_2)),t?.periods_back!==void 0&&r.set("periods_back",String(t?.periods_back)),t?.period!==void 0&&r.set("period",String(t?.period)),t?.player_id!==void 0&&r.set("player_id",String(t?.player_id));let o=r.toString(),a=`/v1/leaderboards/${i(e)}`+(o?`?${o}`:"");return n.client.get(a,t?.signal)}async function Dn(n,e,t){let r=`/v1/profiles/players/${i(e)}`;return n.client.get(r,t?.signal)}async function Hn(n,e){let t=new URLSearchParams;e?.config_version!==void 0&&t.set("config_version",String(e?.config_version));let r=t.toString(),o="/v1/inventory/platform-funded-game-inventory-purchase-catalog"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Vn(n,e,t){let r=`/v1/game-room-templates/${i(e)}`;return n.client.get(r,t?.signal)}async function Wn(n,e,t){let r=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}/room-args`;return n.client.get(r,t?.signal)}async function zn(n,e,t,r){let o=`/v1/showcase/players/${i(e)}/${i(t)}`;return n.client.get(o,r?.signal)}async function Kn(n,e,t){let r=`/v1/inventory/transfers/${i(e)}`;return n.client.get(r,t?.signal)}async function K(n,e,t){let r=`/v1/games/${i(n.gameId())}/analytics/events`;return n.client.post(r,e,t?.signal,{replaySafe:true,retryRateLimited:false})}async function Yn(n,e,t){return n.client.post("/v1/inventory/transfers",e,t?.signal)}async function Jn(n,e,t){return n.client.postBytes("/v1/functions/invoke",e,t?.signal)}async function Xn(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}/kick/${i(t)}`;return n.client.post(o,void 0,r?.signal)}async function Qn(n,e){let t=new URLSearchParams;e?.periods_back!==void 0&&t.set("periods_back",String(e?.periods_back)),e?.player_id!==void 0&&t.set("player_id",String(e?.player_id));let r=t.toString(),o="/v1/challenges"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Zn(n,e,t,r){let o=`/v1/settings/${i(e)}/${i(t)}`;return n.client.get(o,r?.signal)}async function et(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),s=`/v1/inventory/${i(e)}/${i(t)}/orders`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function nt(n,e,t,r){let o=`/v1/inventory/${i(e)}/${i(t)}/progressions`;return n.client.get(o,r?.signal)}async function tt(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),s=`/v1/saves/${i(e)}/${i(t)}`+(a?`?${a}`:"");return n.client.get(s,r?.signal)}async function rt(n,e){let t=new URLSearchParams;e?.include_inactive!==void 0&&t.set("include_inactive",String(e?.include_inactive)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.offset!==void 0&&t.set("offset",String(e?.offset));let r=t.toString(),o="/v1/events"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function ot(n,e){let t=new URLSearchParams;e?.status!==void 0&&t.set("status",String(e?.status)),e?.game_channel!==void 0&&t.set("game_channel",String(e?.game_channel)),e?.game_room_type!==void 0&&t.set("game_room_type",String(e?.game_room_type)),e?.template_id!==void 0&&t.set("template_id",String(e?.template_id)),e?.template_slug!==void 0&&t.set("template_slug",String(e?.template_slug)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor));let r=t.toString(),o=`/v1/games/${i(n.gameId())}/game-rooms`+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function at(n,e){return n.client.get("/v1/game-room-templates",e?.signal)}async function it(n,e){let t=new URLSearchParams;e.entity_type!==void 0&&t.set("entity_type",String(e.entity_type)),e.entity_id!==void 0&&t.set("entity_id",String(e.entity_id)),e?.direction!==void 0&&t.set("direction",String(e?.direction)),e?.status!==void 0&&t.set("status",String(e?.status)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor)),e?.limit!==void 0&&t.set("limit",String(e?.limit));let r=t.toString(),o="/v1/inventory/transfers"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function st(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/max`;return n.client.post(s,o,a?.signal)}async function lt(n,e,t,r,o,a){let s=`/v1/settings/${i(e)}/${i(t)}/${i(r)}`;return n.client.put(s,o,a?.signal,{...a?.ifMatch===void 0?{}:{ifMatch:a.ifMatch}})}async function dt(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/game-rooms/${i(e)}/room-args`;return n.client.put(o,t,r?.signal)}async function ut(n,e,t,r,o){let a=`/v1/showcase/players/${i(e)}/${i(t)}`;return n.client.put(a,r,o?.signal)}async function ct(n,e,t){let r=`/v1/inventory/transfers/${i(e)}/reject`;return n.client.post(r,void 0,t?.signal)}async function mt(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/matches/${i(e)}/players/report`;return n.client.post(o,t,r?.signal)}async function gt(n,e,t,r,o,a){let s=`/v1/saves/${i(e)}/${i(t)}/${i(r)}/upload-url`;return n.client.post(s,o,a?.signal,{replaySafe:true})}async function pt(n,e,t,r,o,a){let s=`/v1/inventory/${i(e)}/${i(t)}/progressions/${i(r)}/reset`;return n.client.post(s,o,a?.signal)}async function yt(n,e,t,r){let o=`/v1/games/${i(n.gameId())}/matches/${i(e)}/backfill`;return n.client.put(o,t,r?.signal)}async function ft(n,e,t,r,o){let a=`/v1/inventory/${i(e)}/${i(t)}/orders/simulate`;return n.client.post(a,r,o?.signal)}async function ht(n,e,t,r){let o=`/v2/games/${i(n.gameId())}/game-rooms/${i(e)}/transfer/${i(t)}`;return n.client.post(o,void 0,r?.signal)}async function _t(n,e,t,r){let o=`/v2/games/${i(n.gameId())}/game-rooms/${i(e)}`;return n.client.patch(o,t,r?.signal,{...r?.ifMatch===void 0?{}:{ifMatch:r.ifMatch}})}var ce="gem_";function me(n,e){let t=e?.retryAfterSeconds,r=typeof t=="number"&&Number.isFinite(t)&&t>0?t*1e3:0,o=1e4*2**Math.min(Math.max(0,n-1),16);return Math.min(Math.max(r,o),3e5)}var Rt=1e4,xt=50,kt=1e3,St=65536-4*1024,ge=n=>typeof n=="number"?n:0,wt=n=>typeof n=="string"?n:"";function pe(){let n=globalThis.crypto;return typeof n?.randomUUID=="function"?n.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.floor(Math.random()*16);return (e==="x"?t:t&3|8).toString(16)})}function At(n){return n?.retryable===true}function Pt(n,e){let t=n??{},r=Array.isArray(t.rejected)?t.rejected.map(o=>{let a=o??{};return {index:ge(a.index),code:wt(a.code)}}):[];return {accepted:ge(t.accepted),rejected:r,dropped:e,duplicate:t.duplicate===true}}function ye(n,e){return {event_uid:n.uid,name:n.name,offset_ms:Math.round(n.atMs)-e,player_id:n.playerId,dimensions:n.dimensions,measures:n.measures}}function fe(n){let e=Math.max(1,Math.floor(n.maxQueue??kt)),t={client:n.client,gameId:()=>n.gameId},r=[],o=0,a=null,s=false,l=null,u=0,c=0,g=false,_=false,v=R=>{n.log?.(R);},h=()=>{a!==null&&(n.clock.clearTimeout(a),a=null);},y=()=>Math.max(0,c-n.clock.now()),S=()=>{a!==null||s||l!==null||(a=n.clock.setTimeout(()=>{a=null,O().catch(()=>{});},Math.max(Rt,y())));},A=()=>{let R=Math.min(r.length,100),x=128;for(let T=0;T<R;T+=1){let G=r[T];if(G===void 0)break;if(x+=JSON.stringify(ye(G,0)).length+1,x>St)return Math.max(1,T)}return R};async function O(R){for(;l!==null;)await l.catch(()=>{});if(r.length===0)return {accepted:0,rejected:[],dropped:0,duplicate:false};let x=Ge(R);l=x;try{return await x}finally{l===x&&(l=null),r.length>0&&S();}}async function Ge(R){h();let x=r.splice(0,A()),T=o;o=0;let G=Math.round(n.clock.now()),F={batch_id:pe(),sent_at_ms:G,events:x.map(w=>ye(w,G))};try{let w=await K(t,F,R===void 0?void 0:{signal:R}),C=Pt(w,T);u=0,c=0;for(let Ce of C.rejected)v(`[gem] analytics: an event was refused (${Ce.code}). See the analytics_events section of your game config.`);return C}catch(w){if(At(w)){for(r=[...x,...r],o+=T;r.length>e;)r.shift(),o+=1;u+=1,c=n.clock.now()+me(u,w);}else v(`[gem] analytics: ${x.length} event(s) were discarded \u2014 the platform refused the batch and will refuse it again: ${String(w)}`);throw w}}return {track(R,x,T={}){if(s)return;if(typeof R!="string"||R===""){_||(_=true,v(`[gem] analytics: '${x}' was not recorded \u2014 an event from a server has to name the player it is about. A game-server token names no player, so the platform cannot infer one; pass the id from join() or host.players().`));return}if(x.startsWith(ce)){g||(g=true,v(`[gem] analytics: '${x}' was not recorded \u2014 the gem_ prefix is reserved, and the platform's own events are client-sourced, so a server may not emit one at all. Name your event something else.`));return}let G={},F={};for(let[w,C]of Object.entries(T))typeof C=="string"?G[w]=C:typeof C=="number"&&Number.isFinite(C)&&(F[w]=C);for(;r.length>=e;)r.shift(),o+=1;r.push({uid:pe(),playerId:R,name:x,atMs:n.clock.now(),dimensions:G,measures:F}),r.length>=xt&&l===null&&y()===0?O().catch(()=>{}):S();},flush:R=>O(R),get pending(){return r.length},dispose(){s=true,h(),r=[],o=0;}}}var Ot=new Map([["function_error",false],["function_failed",false],["function_timeout",false],["function_not_published",false],["function_input_too_large",false],["functions_not_configured",false],["forbidden",false],["function_capacity_unavailable",true],["function_host_unreachable",true],["function_host_error",true],["rate_limited",true],["service_unavailable",true]]),$=class extends m{constructor(e,t){super(e.reason,e.status,e.detail,e.retryAfterSeconds,e.code),this.name="GemFunctionError",Object.assign(this,t);}get retryable(){return (this.code===void 0?void 0:Ot.get(this.code))??super.retryable}},L=class{#e;constructor(e){this.#e=e;}async invoke(e,t={}){let r=Tt(e),o;try{o=await this.#e(r,t.signal===void 0?{}:{signal:t.signal});}catch(a){throw a instanceof m?Ct(a):a}return Gt(o)}};function Tt(n){if(n instanceof ArrayBuffer)return new Uint8Array(n);if(ArrayBuffer.isView(n))return new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let e;try{e=JSON.stringify(n);}catch(t){throw new f(`the function input could not be serialised to JSON: ${String(t)}. Pass bytes, or a value JSON can carry. This is a bug in the call, not a server failure.`)}if(e===void 0)throw new f(`the function input is ${typeof n}, which JSON cannot carry. Pass bytes \u2014 an empty Uint8Array for no input \u2014 or a JSON value. This is a bug in the call, not a server failure.`);return new TextEncoder().encode(e)}function Gt(n){let{bytes:e}=n,t=()=>new TextDecoder("utf-8",{fatal:true}).decode(e);return {bytes:e,...n.invocationId===void 0?{}:{invocationId:n.invocationId},text:t,json:()=>JSON.parse(t())}}function Ct(n){let{details:e,invocationId:t}=te(n),o=e?.untrusted?.message,a=e?.reason;return new $(n,{...t===void 0?{}:{invocationId:t},...n.code==="function_error"&&typeof o=="string"?{functionMessage:o}:{},...n.code==="function_failed"&&typeof a=="string"?{failure:a}:{}})}var P=n=>typeof n=="string"?n:"",Et=/(?:Z|[+-]\d{2}:?\d{2})$/i;function he(n){let e=P(n);if(!Et.test(e))return 0;let t=Date.parse(e);return Number.isFinite(t)?Math.floor(t/1e3):0}var It=/^[0-9a-f]{1,64}$/,Mt=/^[0-9a-f]{16}$/,Lt=/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/,qt=/^(?:[01]\d|2[0-3]):[0-5]\d$/;function $t(n){let e=P(n),t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!t)return;let[r,o,a]=[Number(t[1]),Number(t[2]),Number(t[3])],s=new Date(Date.UTC(r,o-1,a));return s.getUTCFullYear()===r&&s.getUTCMonth()===o-1&&s.getUTCDate()===a?e:void 0}var jt=4096,Ft=new TextEncoder;function Bt(n){let e=P(n.challenge_data),t=P(n.challenge_data_hash);return e===""||!Mt.test(t)?{}:Ft.encode(e).length>jt?{}:{data:e,dataHash:t}}function Ut(n){let e=n??{},t=he(e.period_start_at),r=he(e.period_ends_at);if(t===0||r===0)return;let o=P(e.slug),a=P(e.seed),s=$t(e.period_local_date),l=P(e.boundary),u=P(e.reset_time);return {id:P(e.progression_definition_id),...o===""?{}:{slug:o},window:P(e.window),periodStartAt:t,periodEndsAt:r,...s===void 0?{}:{periodLocalDate:s},boundary:Lt.test(l)?l:"",resetTime:qt.test(u)?u:"",seed:It.test(a)?a:"",...Bt(e)}}function _e(n){if(n!==void 0&&(!Number.isInteger(n)||n<0||n>400))throw new TypeError(`periodsBack must be an integer between 0 and 400, got ${String(n)}`)}function be(n){let e=n??{};return Array.isArray(e.challenges)?e.challenges.map(Ut).filter(t=>t!==void 0):[]}function ve(n){return {async list(e){let{playerId:t,periodsBack:r,signal:o}=e??{};if(typeof t!="string"||t==="")throw new TypeError("gem.challenges.list needs { playerId }: a game-server token names no player, so the platform cannot tell whose period you mean. Pass the id from join() or gem.players().");_e(r);let a=await n({player_id:t,...r===void 0?{}:{periods_back:r},...o===void 0?{}:{signal:o}});return be(a)}}}function Nt(n){let e={};for(let[t,r]of Object.entries(Y))if(typeof r=="function"){let o=r;e[t]=(...a)=>o(n,...a);}return e}function H(n,e={}){let t=e.clock??M,r=e.baseUrl??n.apiBaseUrl;if(!r)throw new Error("this runtime names no gem-api host (host.apiBaseUrl is missing). The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let o=z(n,{clock:t,...e.onAuthStateChange===void 0?{}:{onStateChange:e.onAuthStateChange}}),a=re({baseUrl:r,tokenSource:o.source,defaultQuery:{game_id:n.gameId,channel:n.channel},...e.fetch===void 0?{}:{fetch:e.fetch}}),s=new E({transport:a,clock:t}),l={gameId:n.gameId,channel:n.channel,matchId:n.matchId,...n.gameRoomId===void 0?{}:{gameRoomId:n.gameRoomId}},u=Nt({client:s,gameId:()=>l.gameId}),c=l.gameRoomId,g=n.endMatch,_=n.log,v=fe({client:s,clock:t,gameId:l.gameId,...typeof _=="function"?{log:y=>_.call(n,y)}:{}});return {identity:l,client:s,api:u,match:{end:async()=>{if(g===void 0)throw new Error("this runtime cannot end the match: the server image running this bundle predates host.endMatch, and only the platform half holds the allocation proof an end needs.");await g.call(n);},setBackfill:async(y,S)=>{let A=await u.setMatchBackfill(l.matchId,{open:y.open,...y.seats===void 0?{}:{seats:y.seats}},S===void 0?{}:{signal:S});return {open:A.open,seats:A.seats??null}},reportPlayers:async(y,S)=>(await u.reportMatchPlayers(l.matchId,{players:y.map(O=>({player_id:O.playerId,state:O.state}))},S===void 0?{}:{signal:S})).players.map(O=>({playerId:O.player_id,outcome:O.outcome}))},analytics:v,functions:new L(u.invokeFunction),challenges:ve(u.listChallenges),...c===void 0?{}:{room:{get:y=>u.getGameRoomV2(c,y),args:async y=>{let S=await u.getGameRoomV2(c,y?.signal===void 0?{}:{signal:y.signal}),A=S.modified?S.body:void 0;if(A?.room_args==null||A.room_args_template==null)throw new m("not_found",404,"this room's template declares no room args",void 0,"room_args_not_defined");return {room_args:A.room_args,room_args_template:A.room_args_template}}}},dispose:()=>{v.dispose(),o.dispose();}}}var Dt=["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"],Ht=new Set(Dt);function Re(n){return typeof n=="string"&&Ht.has(n)?n:"unknown"}var Se="[gem: use gem.text.toPlain()]",xe=new Set,ke;function Vt(n){try{if(xe.has(n))return;let e=globalThis.location;if(ke??=!!e?.port&&de(e.hostname??""),!ke)return;xe.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Se}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
2
2
  ${new Error().stack}`);}catch{}}var k=class{#e;#n;constructor(e,t="player text"){this.#e=e,this.#n=t,Object.freeze(this);}toString(){return Vt(this.#n),Se}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function j(n,e){return new k(n,e)}var we={toPlain(n){return k.unwrap(n)},compare(n,e,t){let r=n instanceof k?k.unwrap(n):n,o=e instanceof k?k.unwrap(e):e;if(typeof Intl>"u"||typeof Intl.Collator!="function")return r<o?-1:r>o?1:0;let a;try{a=new Intl.Collator(t);}catch{a=new Intl.Collator;}return a.compare(r,o)},equals(n,e){let t=n instanceof k?k.unwrap(n):n,r=e instanceof k?k.unwrap(e):e;return t.normalize("NFC")===r.normalize("NFC")}};var Ae="the match has not started yet: the runtime hands over the host at `init`, and this was read before that. Read it from `init` or later \u2014 a bundle that needs a value at module scope is reading it one call too early.";function J(n,e={}){let t=e.clock??M,r=()=>{let l=n();if(l===null)throw new Error(Ae);return l},o=null,a=()=>{if(o===null)throw new Error(Ae);return o};return {server:{get identity(){return a().identity},get client(){return a().client},get api(){return a().api},get match(){return a().match},get analytics(){return a().analytics},get functions(){return a().functions},get challenges(){return a().challenges},get room(){return a().room},dispose(){o?.dispose();},get sizeClass(){return r().sizeClass},get roomArgs(){return r().roomArgs},get roster(){return r().roster},players(){return r().players()},profile(l){let c=r().profile?.(l);if(!c)return null;let g=c.displayName;return typeof g!="string"||g===""?null:{displayName:j(g,"profile.displayName"),...typeof c.displayTag=="string"&&c.displayTag!==""?{displayTag:j(c.displayTag,"profile.displayTag")}:{},...typeof c.discriminator=="string"?{discriminator:c.discriminator}:{}}},playerNumber(l){return r().playerNumber?.(l)},send(l,u,c){c===void 0?r().send(l,u):r().send(l,u,c);},broadcast(l,u){u===void 0?r().broadcast(l):r().broadcast(l,u);},kick(l,u){u===void 0?r().kick(l):r().kick(l,u);},log(l){r().log(l);},warn(l){r().warn(l);}},attach:()=>{o=H(r(),{...e,clock:t});}}}function Pe(n,e={}){let{server:t,attach:r}=J(()=>n,e);return r(),t}function Wt(n){if(n===void 0||typeof n.reason!="string"||n.reason==="")return;let e=n.platform_reason??n.endedReason;return {reason:n.reason,...e===void 0?{}:{endedReason:Re(e)}}}var zt=1e4;function Kt(n){let e=false;return t=>{if(e)return;let r;try{r=n.match.reportPlayers([{playerId:t,state:"connected"}],AbortSignal.timeout(zt));}catch{return}r.catch(o=>{o instanceof m&&o.status>=400&&o.status<500&&(e=o.status!==429);});}}function Oe(n,e={}){return ()=>{let t=null,{server:r,attach:o}=J(()=>t,e),a=n(r),s={async init(u){t=u,o(),await a.init?.();},async shutdown(u){let c=Wt(u);try{await a.shutdown?.(c);}finally{r.dispose();}}},l=Kt(r);return s.join=(u,c)=>(l(u),a.join?.(u,c)),a.leave&&(s.leave=(u,c)=>a.leave?.(u,c)),a.message&&(s.message=(u,c,g,_)=>a.message?.(u,c,g,_)),s}}var Yt=new Set(["unlocked","already_unlocked","suppressed"]);function Te(n){let e=n?.changes;if(!Array.isArray(e))return [];let t=[];for(let r of e){let o=r??{};if(o.type!=="achievement_unlocked"||typeof o.outcome!="string"||!Yt.has(o.outcome)||typeof o.achievement_id!="string"||o.achievement_id==="")continue;let a=o.outcome;t.push({achievementId:o.achievement_id,progressionDefinitionId:typeof o.progression_definition_id=="string"?o.progression_definition_id:"",points:a==="unlocked"&&typeof o.points=="number"&&Number.isFinite(o.points)?o.points:0,outcome:a,...typeof o.reason=="string"?{reason:o.reason}:{}});}return t}export{b as AuthError,U as CONDITIONAL_HEADERS,L as FunctionsApi,m as GemApiError,E as GemClient,$ as GemFunctionError,k as PlayerText,f as TransportRefusal,Te as achievementReceipts,j as asPlayerText,H as createGemServer,ue as createHostTokenSource,Pe as createMatchServer,Oe as defineServer,M as systemClock,we as text,Z as toBase64};//# sourceMappingURL=gem-web-sdk.server.js.map
3
3
  //# sourceMappingURL=gem-web-sdk.server.js.map
package/dist/sdk.d.ts CHANGED
@@ -1501,6 +1501,7 @@ interface AnalyticsApiOptions {
1501
1501
  readonly clock: Clock;
1502
1502
  readonly gameId: () => string | null;
1503
1503
  readonly playerId: () => string | null;
1504
+ readonly awaitPlayerId?: () => Promise<string>;
1504
1505
  readonly options?: AnalyticsOptions;
1505
1506
  readonly log?: ((message: string) => void) | null;
1506
1507
  }
@@ -1509,6 +1510,8 @@ declare class AnalyticsApi {
1509
1510
  constructor(options: AnalyticsApiOptions);
1510
1511
  track(name: string, props?: Readonly<Record<string, AnalyticsValue>>): void;
1511
1512
  setContext(props: Readonly<Record<string, AnalyticsValue>>): void;
1513
+ experiment(key: string, count: number): Promise<number>;
1514
+ experiment<T extends string>(key: string, options: readonly T[]): Promise<T>;
1512
1515
  get environment(): Readonly<Record<string, string>>;
1513
1516
  flush(options?: AnalyticsFlushOptions): Promise<AnalyticsFlushResult>;
1514
1517
  trackPlatform(name: PlatformEventName, props?: Readonly<Record<string, AnalyticsValue>>): void;
@@ -4766,6 +4769,8 @@ interface DeviceSettingsApi {
4766
4769
  declare class GemUnframedError extends Error {
4767
4770
  constructor();
4768
4771
  }
4772
+ declare function experimentSelection(key: string, playerId: string, count: number): number;
4773
+ declare function experimentSelection<T extends string>(key: string, playerId: string, options: readonly T[]): T;
4769
4774
  interface AvatarRigNode {
4770
4775
  readonly node: string;
4771
4776
  readonly parent: string | null;
@@ -4962,4 +4967,4 @@ interface ExitAnalytics {
4962
4967
  }
4963
4968
  declare function leaveAndExit(rooms: ExitRooms, bridge: ExitBridge | null, reason?: string, analytics?: ExitAnalytics): Promise<void>;
4964
4969
  declare function createGem(options?: GemOptions): Gem;
4965
- export { ARCADE_EMBEDDERS, AVATAR_RIGS, type AccountApi, type AdFormat, type AdOutcome, type AdOutcomeKind, type AdRequest, type AdsApi, AnalyticsApi, type AnalyticsFlushOptions, type AnalyticsFlushResult, type AnalyticsOptions, type AnalyticsRejection$1 as AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Avatar3dFamily, type Avatar3dOffer, type Avatar3dPrice, type Avatar3dPurchaseRequest, type AvatarRigNode, type AvatarRigs, type Avatars3dApi, type Base64String, type BootMark, type BootTimeline, type BroadcastOptions, type BytesResponse, CONDITIONAL_HEADERS, type Challenge, type ChallengeWindow, ChallengesApi, type ClientOptions, type Clock, type ConditionalRequestHeaders, type ConfiguredPurchasePayment, type ConfiguredPurchaseRequest, type ConfiguredPurchaseResult, type ControlsApi, DEVICE_SETTINGS_MAX_BYTES, DedicatedServerError, type DedicatedServerFailure, type DedicatedServerSession, type DevToolsOpener, type DeviceSettings, type DeviceSettingsApi, type DeviceSettingsPutOutcome, type DirectPurchaseRequest, type DirectPurchaseResult, type EarnAllowance, type EarnAllowanceEvent, type EarnAllowancePeriod, type EarnAllowanceReadOptions, type EntityLeaderboard, type EntityLeaderboardEntry, type EntityLeaderboardOptions, type EntityType$1 as EntityType, type ExitAnalytics, type ExitBridge, type ExitRooms, type FrameState, type Friend, type FriendChallenge, FriendChallengeError, type FriendChallengeErrorCode, type FriendChallengeOutcome, type FriendChallengeOutcomeKind, type FriendChallengeRequest, type FriendChallengesPage, FriendsApi, type FriendsPage, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameApi, type GameConfig, type GameMenu, type GameMenuItem, type GameServerLink, type GameSettingsApi, type Gem, GemApiError, type GemAudioChannels, GemClient, type GemCohort, type GemCohorts, GemConfigError, type GemControlBox, type GemCorner, type GemDevTools, type GemErrorReason, GemFrame, GemFunctionError, type GemGameChoiceSetting, type GemGameSetting, type GemGameSettingBase, type GemGameSettingChange, type GemGameSettingIcon, type GemGameSettingOption, type GemGameSettingValue, type GemGameSliderSetting, type GemGameToggleSetting, type GemGraphicsQuality, type GemIdentity, type GemInsets, type GemLook, type GemLookBase, type GemLookDelta, type GemLookOptions, type GemLookSpeed, type GemMatchApi, type GemMatchServer, type GemMatchServerModule, type GemOptions, type GemPlayer, type GemPrefs, type GemRecommendedSettings, type GemReservedArea, GemSaveError, type GemServer, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerIdentity, type GemServerLifecycle, type GemServerOptions, type GemServerProfile, type GemTerminationNotice, GemUnframedError, type GemViewport, type GetLeaderboardOptions, type HostTokenSource, type HostTokenSourceOptions, type JoinWelcome, type Leaderboard, type LeaderboardEntry, type LeaderboardPeriod, LeaderboardsApi, type ListChallengesOptions, type ListFriendChallengesOptions, type ListFriendsOptions, MAX_PERIODS_BACK, type MatchBackfillState, type MatchConnectOptions, type MatchConnection, type MatchHosting, type MatchHostingMode, type MatchPlayerReport, type MatchPlayerReportResult, type MatchServeOptions, type MatchTransport, type MessageMeta, type MomentKind, type MomentResult, type MomentSignal, type MomentsApi, type MultiTransportSession, PLATFORM_EVENTS, type PeriodOptions, PlatformApi, PlatformCurrenciesApi, type PlatformCurrency, type PlatformCurrencyAccess, type PlatformCurrencyPurchasePayment, type PlatformCurrencyPurchaseRequest, type PlatformCurrencyPurchaseResult, type PlatformCurrencyReadOptions, PlatformEarnAllowanceApi, type PlatformEventName, type PlayerBadge$1 as PlayerBadge, type PlayerBoost, type PlayerBoostMultiplier, type PlayerIdentity, type PlayerProfile, PlayerText, type ProfileCallOptions, type ProfileShowcaseEntry$1 as ProfileShowcaseEntry, ProfilesApi, type PurchaseApi, type PurchaseOptions, type PurchaseOutcome, PurchaseRefusal, type PurchaseRefusalCode, type PurchaseRequest, type PutBytesOptions, type PutSaveOptions, type RankedRow, type RecipeAvailability, type RecipeLock, type RequestOptions, type RewardCue, type RewardedStatus, type RoomArgValue, type SavePage, type SaveRecord, type SendOptions$1 as SendOptions, type ServerApi, type ServerBundleHost, type ServerTokenGrant, type SessionEndReason, type ShareApi, type ShareRequest, ShowcaseApi, type ShowcaseCallOptions, type ShowcaseFriendEntry, type ShowcaseFriends, type ShowcaseValue, type SignInOutcome, type SignInOutcomeKind, type SnapshotCurrency, type SnapshotItem, type TokenGrant, type Transport, type TransportAdvert, type TransportKind, TransportRefusal, type TransportRequest, type TransportResponse, UNRELIABLE_CHANNEL_LABEL, type VersionedGetOptions, type VersionedResponse, type VibrationApi, type WalletVisibleOptions, type WornAvatar3d$1 as WornAvatar3d, asPlayerText, createBootTimeline, createGem, createGemServer, createHostTokenSource, createMatchServer, defineServer, leaveAndExit, mapIdentity, markOnResolve, platformIconUrl, presentJoinCredential, readTemplateTransports, readTransportAdvert, reportOnResolve, systemClock, text, toBase64, webTransportAvailable };
4970
+ export { ARCADE_EMBEDDERS, AVATAR_RIGS, type AccountApi, type AdFormat, type AdOutcome, type AdOutcomeKind, type AdRequest, type AdsApi, AnalyticsApi, type AnalyticsFlushOptions, type AnalyticsFlushResult, type AnalyticsOptions, type AnalyticsRejection$1 as AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Avatar3dFamily, type Avatar3dOffer, type Avatar3dPrice, type Avatar3dPurchaseRequest, type AvatarRigNode, type AvatarRigs, type Avatars3dApi, type Base64String, type BootMark, type BootTimeline, type BroadcastOptions, type BytesResponse, CONDITIONAL_HEADERS, type Challenge, type ChallengeWindow, ChallengesApi, type ClientOptions, type Clock, type ConditionalRequestHeaders, type ConfiguredPurchasePayment, type ConfiguredPurchaseRequest, type ConfiguredPurchaseResult, type ControlsApi, DEVICE_SETTINGS_MAX_BYTES, DedicatedServerError, type DedicatedServerFailure, type DedicatedServerSession, type DevToolsOpener, type DeviceSettings, type DeviceSettingsApi, type DeviceSettingsPutOutcome, type DirectPurchaseRequest, type DirectPurchaseResult, type EarnAllowance, type EarnAllowanceEvent, type EarnAllowancePeriod, type EarnAllowanceReadOptions, type EntityLeaderboard, type EntityLeaderboardEntry, type EntityLeaderboardOptions, type EntityType$1 as EntityType, type ExitAnalytics, type ExitBridge, type ExitRooms, type FrameState, type Friend, type FriendChallenge, FriendChallengeError, type FriendChallengeErrorCode, type FriendChallengeOutcome, type FriendChallengeOutcomeKind, type FriendChallengeRequest, type FriendChallengesPage, FriendsApi, type FriendsPage, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameApi, type GameConfig, type GameMenu, type GameMenuItem, type GameServerLink, type GameSettingsApi, type Gem, GemApiError, type GemAudioChannels, GemClient, type GemCohort, type GemCohorts, GemConfigError, type GemControlBox, type GemCorner, type GemDevTools, type GemErrorReason, GemFrame, GemFunctionError, type GemGameChoiceSetting, type GemGameSetting, type GemGameSettingBase, type GemGameSettingChange, type GemGameSettingIcon, type GemGameSettingOption, type GemGameSettingValue, type GemGameSliderSetting, type GemGameToggleSetting, type GemGraphicsQuality, type GemIdentity, type GemInsets, type GemLook, type GemLookBase, type GemLookDelta, type GemLookOptions, type GemLookSpeed, type GemMatchApi, type GemMatchServer, type GemMatchServerModule, type GemOptions, type GemPlayer, type GemPrefs, type GemRecommendedSettings, type GemReservedArea, GemSaveError, type GemServer, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerIdentity, type GemServerLifecycle, type GemServerOptions, type GemServerProfile, type GemTerminationNotice, GemUnframedError, type GemViewport, type GetLeaderboardOptions, type HostTokenSource, type HostTokenSourceOptions, type JoinWelcome, type Leaderboard, type LeaderboardEntry, type LeaderboardPeriod, LeaderboardsApi, type ListChallengesOptions, type ListFriendChallengesOptions, type ListFriendsOptions, MAX_PERIODS_BACK, type MatchBackfillState, type MatchConnectOptions, type MatchConnection, type MatchHosting, type MatchHostingMode, type MatchPlayerReport, type MatchPlayerReportResult, type MatchServeOptions, type MatchTransport, type MessageMeta, type MomentKind, type MomentResult, type MomentSignal, type MomentsApi, type MultiTransportSession, PLATFORM_EVENTS, type PeriodOptions, PlatformApi, PlatformCurrenciesApi, type PlatformCurrency, type PlatformCurrencyAccess, type PlatformCurrencyPurchasePayment, type PlatformCurrencyPurchaseRequest, type PlatformCurrencyPurchaseResult, type PlatformCurrencyReadOptions, PlatformEarnAllowanceApi, type PlatformEventName, type PlayerBadge$1 as PlayerBadge, type PlayerBoost, type PlayerBoostMultiplier, type PlayerIdentity, type PlayerProfile, PlayerText, type ProfileCallOptions, type ProfileShowcaseEntry$1 as ProfileShowcaseEntry, ProfilesApi, type PurchaseApi, type PurchaseOptions, type PurchaseOutcome, PurchaseRefusal, type PurchaseRefusalCode, type PurchaseRequest, type PutBytesOptions, type PutSaveOptions, type RankedRow, type RecipeAvailability, type RecipeLock, type RequestOptions, type RewardCue, type RewardedStatus, type RoomArgValue, type SavePage, type SaveRecord, type SendOptions$1 as SendOptions, type ServerApi, type ServerBundleHost, type ServerTokenGrant, type SessionEndReason, type ShareApi, type ShareRequest, ShowcaseApi, type ShowcaseCallOptions, type ShowcaseFriendEntry, type ShowcaseFriends, type ShowcaseValue, type SignInOutcome, type SignInOutcomeKind, type SnapshotCurrency, type SnapshotItem, type TokenGrant, type Transport, type TransportAdvert, type TransportKind, TransportRefusal, type TransportRequest, type TransportResponse, UNRELIABLE_CHANNEL_LABEL, type VersionedGetOptions, type VersionedResponse, type VibrationApi, type WalletVisibleOptions, type WornAvatar3d$1 as WornAvatar3d, asPlayerText, createBootTimeline, createGem, createGemServer, createHostTokenSource, createMatchServer, defineServer, experimentSelection, leaveAndExit, mapIdentity, markOnResolve, platformIconUrl, presentJoinCredential, readTemplateTransports, readTransportAdvert, reportOnResolve, systemClock, text, toBase64, webTransportAvailable };
package/docs/analytics.md CHANGED
@@ -16,8 +16,8 @@ you design around them:
16
16
  only ask about the last two months.
17
17
 
18
18
  What you record here is **yours**. It feeds your own reporting and nothing the platform treats as
19
- authoritative — see [section 8](#8-what-analytics-is-not) before you attach a reward to it, and
20
- [section 9](#9-where-to-see-what-you-recorded) for where to read it back.
19
+ authoritative — see [section 9](#9-what-analytics-is-not) before you attach a reward to it, and
20
+ [section 10](#10-where-to-see-what-you-recorded) for where to read it back.
21
21
 
22
22
  ## 1. Two roles, and they are not the same person
23
23
 
@@ -226,9 +226,73 @@ only reason to call it by hand is to look at the answer.
226
226
  **`enabled: false` turns your own `track` calls into no-ops and leaves the platform's `gem_*`
227
227
  events alone** — it is narrower than it looks.
228
228
 
229
- ## 6. What your dedicated server records, and why it differs
229
+ ## 6. Run your own experiment
230
230
 
231
- A server bundle reaches the same surface as `gem.analytics`, with one signature difference:
231
+ To try two versions of something — a short tutorial against a long one, two shop layouts — each
232
+ player needs an arm that stays theirs. **Never pick it with `Math.random()`**: a player gets a new
233
+ arm on every refresh, plays both, and the test cannot be read. Ask the SDK instead:
234
+
235
+ ```js
236
+ const tutorial = await gem.analytics.experiment('tutorial_length', ['short', 'long']);
237
+ const layout = await gem.analytics.experiment('shop_layout', 3); // 0, 1 or 2
238
+ ```
239
+
240
+ Pass a key and either **a list of strings**, to get one of them, or **a count**, to get an index
241
+ from `0` to `count - 1`. The answer is the same on every call, every refresh and every device for
242
+ that player. A string listed twice is twice as likely, which is how to weight an arm. A key must be
243
+ a non-empty string, and a count or a list holds 1 to 2048 arms; anything else rejects with a
244
+ `TypeError` or `RangeError` before the call waits for anything.
245
+
246
+ **Keep the key and the arms fixed for the life of the experiment.** Changing the count or the list
247
+ re-deals every player. To start over on purpose, use a new key.
248
+
249
+ **It waits for the player.** The selection is made from the signed-in player's id, which arrives
250
+ with the first play token, so a call during boot waits for it — and rejects the way
251
+ `gem.awaitIdentity()` does when none arrives, which outside the Arcade, the Gem Editor and the dev
252
+ host it never does. There is no random fallback; catch the rejection and play your control arm:
253
+
254
+ ```js
255
+ const tutorial = await gem.analytics
256
+ .experiment('tutorial_length', ['short', 'long'])
257
+ .catch(() => 'short');
258
+ ```
259
+
260
+ A guest is selected by their guest id, which is stable for as long as their guest account lasts.
261
+ Once they sign in to an account they are that account's player, and its selection applies.
262
+
263
+ **Nothing is recorded for you.** The selection is not sent anywhere and the SDK does not add it to
264
+ your events — you choose which events carry it. Declare a dimension for the experiment on those
265
+ events, with its arms as the values (a count's index is a `range` from `0` to `count - 1`), and
266
+ pass the selection yourself:
267
+
268
+ ```yaml
269
+ analytics_events:
270
+ - id: tutorial_finished
271
+ dimensions:
272
+ tutorial_length: { values: [short, long] }
273
+ ```
274
+
275
+ ```js
276
+ gem.analytics.track('tutorial_finished', { tutorial_length: tutorial });
277
+ ```
278
+
279
+ It counts against the event's three dimensions and its cardinality cap like any other, so put it
280
+ on the events that answer the question rather than on all of them. Name the dimension after your
281
+ experiment, never `variant`: the platform fills that one in itself, for store-page experiments.
282
+
283
+ **The selection can be reproduced anywhere** — a dedicated server, an analytics query — because
284
+ the hash is fixed and will not change: FNV-1a 32-bit over the UTF-8 bytes of
285
+ `"<key>:<playerId>"`, then MurmurHash3's 32-bit finalizer (`fmix32`), as an unsigned integer, mod
286
+ the number of arms; with a list, that is the index into it. `experimentSelection(key, playerId,
287
+ countOrOptions)`, exported by `@indiegems/gem-web-sdk/game` and `/sdk`, computes it for an id you
288
+ hold. Hashing the key in is what keeps experiments independent: the players in one experiment's
289
+ first arm are not the same players in every other experiment's first arm.
290
+
291
+ ## 7. What your dedicated server records, and why it differs
292
+
293
+ A server bundle reaches the same surface as `gem.analytics`, except `experiment` — a server
294
+ reproduces a player's selection with `experimentSelection` from `/sdk`, given their id — and with one
295
+ signature difference:
232
296
 
233
297
  ```js
234
298
  export default defineServer((gem) => ({
@@ -251,7 +315,7 @@ the queue. See [server-bundles.md](server-bundles.md) for the rest of a bundle's
251
315
  **A server cannot emit `gem_*` events.** Those are declared client-sourced, so a server-side
252
316
  `gem_match_started` is refused; record your own event instead.
253
317
 
254
- ## 7. When something is refused
318
+ ## 8. When something is refused
255
319
 
256
320
  **Refusals arrive inside a successful response, one per event.** A batch carrying one bad name is
257
321
  not rejected — its other events are written and the bad one comes back at its index. The SDK
@@ -285,7 +349,7 @@ refusal in a row, up to five minutes; the send on the way out of the page goes r
285
349
  discards its batch instead, because a queue that can never empty fills up and starts dropping the
286
350
  events that would explain the problem.
287
351
 
288
- ## 8. What analytics is not
352
+ ## 9. What analytics is not
289
353
 
290
354
  **Client events are game-asserted browser facts.** Anything a browser can record, a modified
291
355
  browser can record differently — the same boundary that keeps a leaderboard score off the client
@@ -308,7 +372,7 @@ that is.
308
372
  **Nothing per-player comes back.** The reads answer with counts and with how many distinct players
309
373
  reached a step; there is no drill-down to an individual, on any surface.
310
374
 
311
- ## 9. Where to see what you recorded
375
+ ## 10. Where to see what you recorded
312
376
 
313
377
  You do not need to build anything to read your events back: the platform charts them. Your game's
314
378
  **Analytics** page in the developer portal (Developer, then your game, then **Analytics**;
@@ -1,6 +1,6 @@
1
1
  # The function surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/function` v15.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
3
+ Generated from `@indiegems/gem-web-sdk/function` v15.1.1, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
4
4
 
5
5
  `@indiegems/gem-web-sdk/function` exports exactly the symbols on this page and nothing else.
6
6
 
@@ -310,6 +310,72 @@ them, whatever you set here.
310
310
  | --- | --- |
311
311
  | `props` | `Readonly<Record<string, AnalyticsValue>>` |
312
312
 
313
+ <a id="analyticsapi-experiment"></a>
314
+
315
+ ## AnalyticsApi.experiment
316
+
317
+ ```ts
318
+ experiment(key: string, count: number): Promise<number>
319
+ ```
320
+
321
+ Which arm of one of your experiments this player gets — the same answer on
322
+ every call, every refresh and every device. Use it wherever you would have
323
+ picked an arm with `Math.random()`, which deals a player a new arm on each
324
+ visit and makes the test unreadable.
325
+
326
+ Pass a COUNT and get an index, `0` to `count - 1`; pass a LIST of strings
327
+ and get one of them. A string listed twice is twice as likely.
328
+
329
+ ```js
330
+ const tutorial = await gem.analytics.experiment('tutorial_length', ['short', 'long']);
331
+ const layout = await gem.analytics.experiment('shop_layout', 3); // 0, 1 or 2
332
+ ```
333
+
334
+ The selection is a hash of the key and the signed-in player's id — FNV-1a
335
+ 32-bit over `"<key>:<playerId>"`, then MurmurHash3's 32-bit finalizer, mod
336
+ the arm count; see
337
+ `experimentSelection`, which computes the same answer for an id you
338
+ hold. Hashing the key in is what keeps your experiments independent: the
339
+ players in one experiment's first arm are not the same players in every
340
+ other experiment's first arm.
341
+
342
+ ⚠️ **Keep the key and the arms fixed for the life of the experiment.** A new
343
+ count or list re-deals every player. To start over on purpose, use a new
344
+ key.
345
+
346
+ ⚠️ **NOTHING IS RECORDED FOR YOU.** The selection is not sent anywhere, and
347
+ the SDK does not add it to your events. To analyse an experiment, declare a
348
+ dimension for it on the events you want to break down, with the arms as its
349
+ values, named after the experiment, and pass the selection there yourself:
350
+ `gem.analytics.track('tutorial_finished', { tutorial_length: tutorial })`.
351
+ Never the platform's `variant` field, which belongs to store-page
352
+ experiments.
353
+
354
+ WAITS FOR THE PLAYER. The id arrives with the first play token, so a call
355
+ during boot waits for it, and rejects as `Gem.awaitIdentity` does
356
+ when none arrives — outside the Arcade, the Gem Editor or the dev host.
357
+ There is no random fallback: catch the rejection and play your control arm.
358
+ A guest is selected by their guest id, which is stable for as long as their
359
+ guest account lasts; once they sign in to an account they are that
360
+ account's player, and its selection applies.
361
+
362
+ In a serverless function it selects for the player who invoked it, with
363
+ no wait, and rejects when a game server invoked it.
364
+
365
+ Works with analytics turned off: it reads no setting and sends nothing.
366
+
367
+ | Parameter | Type | Description |
368
+ | --- | --- | --- |
369
+ | `key` | `string` | Names the experiment — any non-empty string. |
370
+ | `count` | `number` | How many arms, an integer from 1 to 2048 — or, in the list
371
+ form, `options`: the arms themselves, 1 to 2048 strings. |
372
+
373
+ **Returns.** The arm's index, or the chosen string.
374
+
375
+ **Throws.** TypeError for an empty key or a list holding something other than
376
+ strings; RangeError for a count or list size outside 1 to 2048. Both
377
+ reject before waiting for the player.
378
+
313
379
  <a id="analyticsapi-environment"></a>
314
380
 
315
381
  ## AnalyticsApi.environment