@indiegems/gem-web-sdk 20.1.0 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/brand/ticket-hero-128.png +0 -0
- package/brand/ticket-hero-256.png +0 -0
- package/brand/ticket-hero-384.png +0 -0
- package/brand/ticket-hero-512.png +0 -0
- package/brand/ticket-hero-768.png +0 -0
- package/brand/ticket-icon-20-1.5x.png +0 -0
- package/brand/ticket-icon-20-1x.png +0 -0
- package/brand/ticket-icon-20-2x.png +0 -0
- package/brand/ticket-icon-20-3x.png +0 -0
- package/brand/ticket-icon-32-1.5x.png +0 -0
- package/brand/ticket-icon-32-1x.png +0 -0
- package/brand/ticket-icon-32-2x.png +0 -0
- package/brand/ticket-icon-32-3x.png +0 -0
- package/brand/ticket-icon-ingame-20-1.5x.png +0 -0
- package/brand/ticket-icon-ingame-20-1x.png +0 -0
- package/brand/ticket-icon-ingame-20-2x.png +0 -0
- package/brand/ticket-icon-ingame-20-3x.png +0 -0
- package/brand/ticket-icon-ingame-32-1.5x.png +0 -0
- package/brand/ticket-icon-ingame-32-1x.png +0 -0
- package/brand/ticket-icon-ingame-32-2x.png +0 -0
- package/brand/ticket-icon-ingame-32-3x.png +0 -0
- package/brand/ticket-pile-1-128.png +0 -0
- package/brand/ticket-pile-1-256.png +0 -0
- package/brand/ticket-pile-1-384.png +0 -0
- package/brand/ticket-pile-1-512.png +0 -0
- package/brand/ticket-pile-1-768.png +0 -0
- package/brand/ticket-pile-2-128.png +0 -0
- package/brand/ticket-pile-2-256.png +0 -0
- package/brand/ticket-pile-2-384.png +0 -0
- package/brand/ticket-pile-2-512.png +0 -0
- package/brand/ticket-pile-2-768.png +0 -0
- package/dist/function.d.ts +1 -1
- package/dist/game.d.ts +1 -1
- package/dist/gem-web-sdk.conformance.js +2 -2
- package/dist/gem-web-sdk.esm.js +1 -1
- package/dist/gem-web-sdk.function.js +1 -1
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +1 -1
- package/dist/sdk.d.ts +1 -1
- package/docs/analytics.md +11 -0
- package/docs/api/api-reference-function.md +7 -2
- package/docs/api/api-reference-game.md +7 -2
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +5 -5
- package/docs/brand-art.md +65 -16
- package/docs/changelog.md +17 -0
- package/docs/getting-started.md +1 -1
- package/docs/manifest.json +10 -10
- package/docs/server-bundles.md +2 -2
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var Me=Object.defineProperty;var $e=(n,e)=>{for(var t in e)Me(n,t,{get:e[t],enumerable:true});};globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:20.1.0]";var je={initialDelayMs:2e3,maxDelayMs:6e4};function Be(n,e){return e!==void 0?!e:n==="chain_exhausted"}var N=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 h(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 h("chain_exhausted","the token chain reached its ceiling");if(this.inFlight===null&&this.options.clock.now()<this.notBefore)throw new h(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}=je,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 h("internal","token arrived for a superseded generation");return this.grant=o,this.rejected=false,this.enter("ready"),o.token}).catch(o=>{let a=Fe(o),{retryable:i,retryAfterMs:d}=o??{};throw t===this.generation&&(Be(a,typeof i=="boolean"?i:void 0)?this.enter("lost",a):(this.scheduleRetry(typeof d=="number"&&Number.isFinite(d)&&d>0?d:void 0),this.enter("degraded",a))),o instanceof h?o:new h(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)}},h=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 Fe(n){if(n instanceof h)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}},H=["if-match","if-none-match"],ee=4096;function Ue(n){if(!n)return;let e={};for(let[t,r]of Object.entries(n)){let o=t.toLowerCase();if(!H.includes(o)||r===void 0)continue;let a=typeof r=="string"?De(r):r;if(typeof a!="string"||a.length>ee||!Ne(a))throw new f(`the ${o} header value cannot be sent \u2014 it is not a string, is longer than ${ee} 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 De(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 Ne(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 He(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 ne(n,e){let t=e.replayUnauthorizedOnce??true;return {async request(r){let o={...r},a=Ue(o.headers),i=He(o),d=Object.freeze({...o,headers:a,...i===void 0?{}:{bytes:i}}),u=await e.tokenSource.get(),c=await n(d,u);if(c.status!==401||!t)return c;e.tokenSource.invalidate(u);let g=await e.tokenSource.get(),S=await n(d,g);if(S.status===401)throw e.tokenSource.close("unavailable"),new h("unavailable","the server refused a freshly minted token");return S}}}function te(n){let e=typeof n=="string"?new TextEncoder().encode(n):n;return btoa(Ve(e))}function Ve(n){let t="";for(let r=0;r<n.length;r+=32768)t+=String.fromCharCode(...n.subarray(r,r+32768));return t}var p=class extends Error{constructor(t,r,o,a,i){super(`${t} (${r}): ${o}`);this.reason=t;this.status=r;this.detail=o;this.retryAfterSeconds=a;this.code=i;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}},ae=new WeakMap;function se(n){return ae.get(n)??{}}var We=new Set([408,429,500,502,503,504]),ze=6e4,Ke=new WeakMap;function re(n){return n!==null&&(n.reason==="rate_limited"||n.retryAfterSeconds!==void 0)}function Ye(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 L=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,...$(o)},o.replaySafe===true,o.retryRateLimited!==false)}async post(e,t,r,o={}){return this.send({method:"POST",path:e,body:t,signal:r,...$(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,...$(o)},o.replaySafe===true,o.retryRateLimited!==false);return {bytes:Je(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,...$(o)},o.replaySafe===true,o.retryRateLimited!==false)}async delete(e,t,r={}){return this.send({method:"DELETE",path:e,signal:t,...$(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?Ye(this.options.maxRetries)+1:1,a=null;if(e.signal?.aborted)throw oe(e.signal);let i=e.signal===void 0?void 0:Ke.get(e.signal);for(let d=0;d<o;d++){if(d>0){let g=this.backoffMs(d,a);if(re(a)&&i!==void 0&&g>=i())throw a;await this.backoff(g,e.signal);}if(e.signal?.aborted)throw re(a)&&i!==void 0&&i()<=0?a:oe(e.signal);let u;try{u=await this.options.transport.request(e);}catch(g){if(h.isAuthError(g)||f.isRefusal(g)||e.signal?.aborted||Xe(g))throw g;a=new p("unavailable",0,String(g));continue}if(u.status>=200&&u.status<300||u.status===304)return u;let c=Qe(u);if(!We.has(u.status)||u.status===429&&!r)throw c;a=c;}throw a??new p("unavailable",0,"no attempt was made")}backoffMs(e,t){let r=t?.retryAfterSeconds,o=r!==void 0?r*1e3:2**(e-1)*250,a=2**(e-1)*250;return Math.min(Math.max(o,a),ze)}backoff(e,t){return t?.aborted?Promise.resolve():new Promise(r=>{let o=this.options.clock,a=false,i=false,d,u=()=>{a||(a=true,i&&o.clearTimeout(d),t?.removeEventListener("abort",u),r());};t?.addEventListener("abort",u,{once:true}),d=o.setTimeout(u,e),i=true,a&&o.clearTimeout(d);})}};function Je(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 $(n){return {...n.keepalive===void 0?{}:{keepalive:n.keepalive},...n.ifMatch===void 0?{}:{headers:{"if-match":n.ifMatch}}}}function oe(n){return n.reason!==void 0?n.reason:new DOMException("The operation was aborted.","AbortError")}function Xe(n){let e=n?.name;return e==="AbortError"||e==="TimeoutError"}function Qe(n){let e=en(n),t=tn(n.body),r={...t===void 0?{}:{details:t},...n.invocationId===void 0?{}:{invocationId:n.invocationId}};return Object.keys(r).length>0&&ae.set(e,r),e}var Ze=new Map([["insufficient_funds","insufficient_funds"]]);function en(n){let{status:e,body:t}=n,r=nn(t),o=n.retryAfterSeconds??on(t),a=rn(t),i=a===void 0?void 0:Ze.get(a);if(i!==void 0)return new p(i,e,r,void 0,a);switch(e){case 304:return new p("not_modified",e,r);case 400:case 422:return new p("invalid",e,r,void 0,a);case 403:return new p("forbidden",e,r,void 0,a);case 404:return new p("not_found",e,r,void 0,a);case 402:return new p("insufficient_funds",e,r,void 0,a);case 409:return new p("conflict",e,r,void 0,a);case 412:return new p("precondition_failed",e,r,void 0,a);case 413:return new p("too_large",e,r,void 0,a);case 429:return new p("rate_limited",e,r,o,a);default:return new p("unavailable",e,r,o,a)}}function nn(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 tn(n){let t=n?.error?.details;return t===null?void 0:t}function rn(n){let t=n?.error?.code;return typeof t=="string"&&t.length>0?t:void 0}function on(n){let e=n?.retryAfterSeconds;return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:void 0}function an(n){let e=n.lastIndexOf("@");return (e===-1?n:`***@${n.slice(e+1)}`).slice(0,200)}var sn=new Set(H);function ln(n){if(!n)return {};let e={};for(let[t,r]of Object.entries(n))sn.has(t.toLowerCase())&&(e[t.toLowerCase()]=r);return e}function ie(n){let e=n.fetch??globalThis.fetch.bind(globalThis);return ne(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"?an(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 R=o.username!==""||o.password!=="",y=a.username!==o.username||a.password!==o.password,x=R?y?"baseUrl (and possibly the request path)":"baseUrl":"request path";throw new f(`the ${x} 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[R,y]of Object.entries(n.defaultQuery??{}))a.searchParams.has(R)||a.searchParams.set(R,y);let i=t.bytes,d=t.bytes===void 0?void 0:"application/octet-stream";try{t.body!==void 0&&(i=JSON.stringify(t.body),d="application/json");}catch(R){throw new f(`the body of ${t.method} ${JSON.stringify(a.pathname)} could not be serialised to JSON: ${String(R)}. 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}`,...d===void 0?{}:{"content-type":d},...ln(t.headers)},body:i,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 R=await u.text();if(R.length>0)try{c=JSON.parse(R);}catch{c=R;}}let g=u.headers.get("etag"),S=dn(u.headers.get("retry-after")),w=u.headers.get("x-gem-invocation-id");return {status:u.status,body:c,...g===null?{}:{etag:g},...S===void 0?{}:{retryAfterSeconds:S},...w===null||w===""?{}:{invocationId:w}}},n)}function dn(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 un="gem-web-bridge";var cn=65536,le=8,V=5e3,de=1024;var W=Object.create(null);var ue=Object.create(null);function mn(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 gn(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 pn(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function yn(n){return /^[a-z_]+$/.test(n)}function fn(n){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(n)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(n)&&!n.includes("--")}function K(n,e,t){if(e>le)return `nesting deeper than ${le}`;if(++t.nodes>V)return `more than ${V} values`;if(t.bytes>t.cap)return `over the ${t.cap} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let 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>de)return `an array of ${n.length} entries, over the ${de} limit`;t.bytes+=2;for(let d of n){let u=K(d,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 i=n;for(let d in i){if(!Object.prototype.hasOwnProperty.call(i,d))continue;if(++t.nodes>V)return `more than ${V} values`;if(t.bytes+=d.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let u=K(i[d],e+1,t);if(u)return u}return null}function hn(n,e=cn){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=K(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var _n={origin:[mn,"not a well-formed origin"],url:[gn,"not a safe absolute http(s) URL"],bcp47:[pn,"not a well-formed BCP 47 tag"],code:[yn,"not a code of lower-case letters and underscores"],gameRef:[fn,"not a game id or web address"]};function ce(n,e,t){for(let r of e){let o=t===""?r[0]:`${t}.${r[0]}`,a=n[r[0]];if(a==null){if(r[1]<"a")continue;return `${o}: required`}let i=Y(a,r,o);if(i)return i}return null}function Y(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:Y(n,typeof n=="string"?[e[0],"s",e[2]]:[e[0],"n",e[3],e[4]],t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e[2])return `${t}: exceeds ${e[2]} characters`;if(e[3]){let[o,a]=_n[e[3]];if(!o(n))return `${t}: ${a}`}return null}case "n":case "i":return typeof n!="number"||!(r=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${r=="i"?"integer":"number"}`:n<e[2]||n>e[3]?`${t}: out of range ${e[2]}..${e[3]}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let o=e[2];return o.includes(n)?null:`${t}: not one of ${o.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:ce(n,e[2],t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e[2])return `${t}: exceeds ${e[2]} items`;let o=e[3],a=`${t}[]`;for(let i of n){if(i==null){if(o[1]<"a")continue;return `${a}: required`}let d=Y(i,o,a);if(d)return d}return null}default:return `${t}: unknown kind ${String(r)}`}}var me=Object.create(null);function l(n,e,t,r,o){return W[n]=t,r&&(ue[n]=r),me[n]=a=>{let i=hn(a,o);if(i)return i;if(typeof a!="object"||a===null||Array.isArray(a))return "message: expected object";let d=a;return d.gem!==un?"gem: wrong protocol marker":d.type!==n?`type: expected ${n}`:ce(d,e,"")}}var j=["","s",64],m=["requestId","s",64],B=["matchId","s",64],I=["outcome","e",["ok","declined","unavailable"]],E=["reason","S",64,"code"];l("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,j],["embedder","s",2048,"origin"],["unbidden","B"]],30);l("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,j]],5);l("gem.token",[["token","s",4096],["requestId","S",64],["generation","i",0,2147483647],["apiBaseUrl","S",512,"url"],["playerId","s",64],["gameId","s",64],["channel","e",["dev","test","release"]],["gameVersion","S",64],["expiresAt","i",0,4102444800],["chainEndsAt","i",0,4102444800],["allowDevTools","B"]],30);l("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);l("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);l("gem.pause",[["paused","b"]],120);l("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);l("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");l("gem.earnAllowanceChanged",[],30,"earnAllowance");l("gem.viewport",[["insets","o",[["top","i",0,4096],["right","i",0,4096],["bottom","i",0,4096],["left","i",0,4096]]],["reserved","A",4,["","o",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384],["control","O",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384]]]]]]],60,"viewport");l("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");l("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);l("gem.roomEvent",[["event","e",["updated","match_started","match_ready","match_failed","version_stale"]],["roomId","s",64],["matchId","S",64],["version","I",0,2147483647],["reason","S",64],["platform","E",["universal","mobile"]]],120,"rooms");l("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");l("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");l("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");l("gem.ticketEvent",[["event","e",["queued","matched","expired","cancelled","failed"]],["ticketId","s",64],["partySize","I",1,64],["queuedAt","I",0,4102444800],["expiresAt","I",0,4102444800],["reason","S",64],["roomId","S",64]],60,"ticketEvents");l("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],B,["data","j"]],1800,"p2p");l("gem.relayDraining",[B,["clusterTargetId","s",64],["relayIps","a",16,j],["drainingSince","s",40],["deadline","s",40]],60,"p2pRelayDrain");l("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],B,["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");l("gem.partyResult",[m,["partyId","S",64]],30,"parties");l("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,j]],12,"clusterTarget");l("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");l("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");l("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");l("gem.requestContext",[],12,"feedbackContext");l("gem.gameContext",[["context","j"]],12,"feedbackContext");l("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],B,["data","j"]],1200,"p2p");l("gem.dedicatedOffer",[B,["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");l("gem.partyRequest",[m],12,"parties");l("gem.ready",[],5);l("gem.engaged",[],5,"engagement");l("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);l("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);l("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");l("gem.localServer",[m,["serverId","S",128]],30,"localHost");l("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");l("gem.exit",[["reason","S",64]],5);l("gem.openExternal",[["url","s",2048,"url"]],6);l("gem.requestFullscreen",[["enabled","b"]],30);l("gem.audioState",[["muted","b"]],120,"audioState");l("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");l("gem.walletDisplay",[["visible","b"],["currencies","A",8,j],["score","B"]],60,"walletDisplay");l("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");l("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");l("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");l("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");l("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");l("gem.settingsDeclare",[["settings","a",8,["","o",[["id","s",40],["label","s",40],["kind","e",["toggle","slider","choice"]],["value","u",40,-1e6,1e6],["quick","B"],["icon","S",16,"code"],["min","N",-1e6,1e6],["max","N",-1e6,1e6],["step","N",0,1e6],["options","A",4,["","o",[["id","s",40],["label","s",40]]]]]]]],120,"gameSettings");l("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");l("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");l("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");l("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");l("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");l("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");l("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);l("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");l("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");l("gem.purchaseResult",[m,I,E,["purchaseId","S",64]],30,"purchases");l("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");l("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");l("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");l("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");l("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");l("gem.momentResult",[m,["shown","b"]],12,"moments");l("gem.requestSignIn",[m],6,"signIn");l("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");l("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");l("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");l("gem.requestAppLinks",[m],12,"appLinks");l("gem.appLinksResult",[m,I,E,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");l("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");l("gem.openAppResult",[m,I,E],30,"appLinks");l("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");l("gem.openGameResult",[m,I,E],30,"openGame");l("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");l("gem.similarGamesResult",[m,I,E,["games","a",24,["","o",[["gameId","s",36,"gameRef"],["slug","S",40,"gameRef"],["iconUrl","s",1024,"url"],["capsuleLandscapeUrl","s",1024,"url"],["capsulePortraitUrl","s",1024,"url"],["previewVideoUrl","S",1024,"url"],["previewPosterUrl","S",1024,"url"],["untrusted","o",[["name","s",64],["tagline","s",64],["shortDescription","s",320],["tags","a",10,["","s",32]]]]]]]],30,"openGame",524288);l("gem.requestStoreOffers",[m],12,"store");l("gem.storeOffersResult",[m,I,E,["offers","a",32,["","o",[["skuId","s",64],["kind","e",["gem_pack","boost","pass","bundle"]],["name","s",80],["description","s",400],["imageUrl","S",1024,"url"],["priceDisplay","s",32],["grant","O",[["currency","s",32],["amount","i",1,1e9]]]]]]],30,"store",262144);l("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");l("gem.storePurchaseResult",[m,I,E],30,"store");l("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");l("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");l("gem.storeOfferSheetResult",[m,I,E],30,"storeOfferSheet");Object.freeze(me);W.__malformed=60;Object.freeze(W);Object.freeze(ue);var q={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)};function ge(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}var bn=1e4;function pe(n,e){return J(n,e)}function J(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=i=>({token:i.token,expiresAt:i.expiresAt,chainEndsAt:Number.POSITIVE_INFINITY}),r=null,o=new N({clock:e.clock,...e.onStateChange===void 0?{}:{onStateChange:e.onStateChange},request:()=>new Promise((i,d)=>{let u=e.clock.setTimeout(()=>{r=null,d({reason:"unavailable",retryable:true});},e.timeoutMs??bn);r={resolve:c=>{e.clock.clearTimeout(u),i(c);},reject:d,timer:u};})}),a=n.onServerToken(i=>{let d=t(i),u=r;if(u){r=null,u.resolve(d);return}o.accept(d);});return {source:o,dispose(){a(),o.dispose(),r&&(e.clock.clearTimeout(r.timer),r.reject({reason:"unavailable",retryable:false}),r=null);}}}var Q={};$e(Q,{acceptTransfer:()=>Rn,addEntityProgression:()=>vn,adminAction:()=>Sn,cancelInventoryTransfer:()=>xn,claimEntityProgressionRewards:()=>wn,closeGameRoom:()=>kn,createEntityBatchOrder:()=>An,createEntityOrder:()=>On,createSession:()=>Pn,deleteEntityGameSetting:()=>Gn,deleteEntitySave:()=>Tn,deleteSession:()=>Cn,deleteShowcaseValue:()=>In,executeEntityRecipe:()=>En,finalizeSaveUpload:()=>Ln,getChannelAssignment:()=>qn,getDropRates:()=>Mn,getEntityCurrency:()=>$n,getEntityGameSetting:()=>jn,getEntityInventory:()=>Bn,getEntityInventoryItem:()=>Fn,getEntityLeaderboard:()=>Un,getEntityOrder:()=>Dn,getEntityProgression:()=>Nn,getEntityRecipeAvailability:()=>Hn,getEntityRecipeUsage:()=>Vn,getEntitySave:()=>Wn,getEvent:()=>zn,getGameRoomV2:()=>Kn,getLeaderboard:()=>Yn,getPlayerGameProfile:()=>Jn,getPurchaseCatalog:()=>Xn,getResolvedTemplate:()=>Qn,getRoomArgs:()=>Zn,getShowcaseValue:()=>et,getTransferDetails:()=>nt,ingestAnalyticsEvents:()=>X,initiateInventoryTransfer:()=>tt,invokeFunction:()=>rt,kickPlayer:()=>ot,listChallenges:()=>at,listEntityGameSettings:()=>st,listEntityOrders:()=>it,listEntityProgressions:()=>lt,listEntitySaves:()=>dt,listEvents:()=>ut,listGameRooms:()=>ct,listResolvedTemplates:()=>mt,listTransfers:()=>gt,maxEntityProgression:()=>pt,putEntityGameSetting:()=>yt,putRoomArgs:()=>ft,putShowcaseValue:()=>ht,rejectTransfer:()=>_t,reportMatchPlayers:()=>bt,requestSaveUploadUrl:()=>Rt,resetEntityProgression:()=>vt,setMatchBackfill:()=>St,simulateEntityOrder:()=>xt,transferHostV2:()=>wt,updateGameRoomV2:()=>kt});function s(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 Rn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/accept`;return n.client.post(r,void 0,t?.signal)}async function vn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/add`;return n.client.post(i,o,a?.signal)}async function Sn(n,e,t,r){let o=`/v1/voice/sessions/${s(e)}/admin`;return n.client.post(o,t,r?.signal)}async function xn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/cancel`;return n.client.post(r,void 0,t?.signal)}async function wn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/claim`;return n.client.post(i,o,a?.signal)}async function kn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.delete(r,t?.signal)}async function An(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/batch`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function On(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Pn(n,e,t){return n.client.post("/v1/voice/sessions",e,t?.signal)}async function Gn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function Tn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function Cn(n,e,t){let r=`/v1/voice/sessions/${s(e)}`;return n.client.delete(r,t?.signal)}async function In(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.delete(o,r?.signal)}async function En(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/recipes/execute`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Ln(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/finalize`;return n.client.post(i,o,a?.signal)}async function qn(n,e,t){let r=`/v1/games/${s(n.gameId())}/config/channels/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Mn(n,e,t){let r=`/v1/inventory/loot-tables/${s(e)}/drop-rates`;return n.client.get(r,t?.signal)}async function $n(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/currencies/${s(r)}`;return n.client.get(a,o?.signal)}async function jn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(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,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(),i=`/v1/inventory/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function Fn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/items/${s(r)}`;return n.client.get(a,o?.signal)}async function Un(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 i=a.toString(),d=`/v1/leaderboards/${s(e)}/${s(t)}/${s(r)}`+(i?`?${i}`:"");return n.client.get(d,o?.signal)}async function Dn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/${s(r)}`;return n.client.get(a,o?.signal)}async function Nn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}`;return n.client.get(a,o?.signal)}async function Hn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/catalogs/${s(r)}/recipes/${s(o)}/availability`;return n.client.get(i,a?.signal)}async function Vn(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}/recipe-usage`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function Wn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function zn(n,e,t){let r=`/v1/events/${s(e)}`;return n.client.get(r,t?.signal)}async function Kn(n,e,t){let r=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Yn(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/${s(e)}`+(o?`?${o}`:"");return n.client.get(a,t?.signal)}async function Jn(n,e,t){let r=`/v1/profiles/players/${s(e)}`;return n.client.get(r,t?.signal)}async function Xn(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 Qn(n,e,t){let r=`/v1/game-room-templates/${s(e)}`;return n.client.get(r,t?.signal)}async function Zn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.get(r,t?.signal)}async function et(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function nt(n,e,t){let r=`/v1/inventory/transfers/${s(e)}`;return n.client.get(r,t?.signal)}async function X(n,e,t){let r=`/v1/games/${s(n.gameId())}/analytics/events`;return n.client.post(r,e,t?.signal,{replaySafe:true,retryRateLimited:false})}async function tt(n,e,t){return n.client.post("/v1/inventory/transfers",e,t?.signal)}async function rt(n,e,t){return n.client.postBytes("/v1/functions/invoke",e,t?.signal)}async function ot(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/kick/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function at(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 st(n,e,t,r){let o=`/v1/settings/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function it(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(),i=`/v1/inventory/${s(e)}/${s(t)}/orders`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function lt(n,e,t,r){let o=`/v1/inventory/${s(e)}/${s(t)}/progressions`;return n.client.get(o,r?.signal)}async function dt(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(),i=`/v1/saves/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function ut(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 ct(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/${s(n.gameId())}/game-rooms`+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function mt(n,e){return n.client.get("/v1/game-room-templates",e?.signal)}async function gt(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 pt(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/max`;return n.client.post(i,o,a?.signal)}async function yt(n,e,t,r,o,a){let i=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.put(i,o,a?.signal,{...a?.ifMatch===void 0?{}:{ifMatch:a.ifMatch}})}async function ft(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.put(o,t,r?.signal)}async function ht(n,e,t,r,o){let a=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.put(a,r,o?.signal)}async function _t(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/reject`;return n.client.post(r,void 0,t?.signal)}async function bt(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/players/report`;return n.client.post(o,t,r?.signal)}async function Rt(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/upload-url`;return n.client.post(i,o,a?.signal,{replaySafe:true})}async function vt(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/reset`;return n.client.post(i,o,a?.signal)}async function St(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/backfill`;return n.client.put(o,t,r?.signal)}async function xt(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/simulate`;return n.client.post(a,r,o?.signal)}async function wt(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}/transfer/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function kt(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.patch(o,t,r?.signal,{...r?.ifMatch===void 0?{}:{ifMatch:r.ifMatch}})}var ye="gem_";function fe(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 Pt=1e4,Gt=50,Tt=1e3,Ct=65536-4*1024,he=n=>typeof n=="number"?n:0,It=n=>typeof n=="string"?n:"";function _e(){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 Et(n){return n?.retryable===true}function Lt(n,e){let t=n??{},r=Array.isArray(t.rejected)?t.rejected.map(o=>{let a=o??{};return {index:he(a.index),code:It(a.code)}}):[];return {accepted:he(t.accepted),rejected:r,dropped:e,duplicate:t.duplicate===true}}function be(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 Re(n){let e=Math.max(1,Math.floor(n.maxQueue??Tt)),t={client:n.client,gameId:()=>n.gameId},r=[],o=0,a=null,i=false,d=null,u=0,c=0,g=false,S=false,w=_=>{n.log?.(_);},R=()=>{a!==null&&(n.clock.clearTimeout(a),a=null);},y=()=>Math.max(0,c-n.clock.now()),x=()=>{a!==null||i||d!==null||(a=n.clock.setTimeout(()=>{a=null,P().catch(()=>{});},Math.max(Pt,y())));},A=()=>{let _=Math.min(r.length,100),b=128;for(let G=0;G<_;G+=1){let T=r[G];if(T===void 0)break;if(b+=JSON.stringify(be(T,0)).length+1,b>Ct)return Math.max(1,G)}return _};async function P(_){for(;d!==null;)await d.catch(()=>{});if(r.length===0)return {accepted:0,rejected:[],dropped:0,duplicate:false};let b=Le(_);d=b;try{return await b}finally{d===b&&(d=null),r.length>0&&x();}}async function Le(_){R();let b=r.splice(0,A()),G=o;o=0;let T=Math.round(n.clock.now()),D={batch_id:_e(),sent_at_ms:T,events:b.map(k=>be(k,T))};try{let k=await X(t,D,_===void 0?void 0:{signal:_}),C=Lt(k,G);u=0,c=0;for(let qe of C.rejected)w(`[gem] analytics: an event was refused (${qe.code}). See the analytics_events section of your game config.`);return C}catch(k){if(Et(k)){for(r=[...b,...r],o+=G;r.length>e;)r.shift(),o+=1;u+=1,c=n.clock.now()+fe(u,k);}else w(`[gem] analytics: ${b.length} event(s) were discarded \u2014 the platform refused the batch and will refuse it again: ${String(k)}`);throw k}}return {track(_,b,G={}){if(i)return;if(typeof _!="string"||_===""){S||(S=true,w(`[gem] analytics: '${b}' 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(b.startsWith(ye)){g||(g=true,w(`[gem] analytics: '${b}' 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 T={},D={};for(let[k,C]of Object.entries(G))typeof C=="string"?T[k]=C:typeof C=="number"&&Number.isFinite(C)&&(D[k]=C);for(;r.length>=e;)r.shift(),o+=1;r.push({uid:_e(),playerId:_,name:b,atMs:n.clock.now(),dimensions:T,measures:D}),r.length>=Gt&&d===null&&y()===0?P().catch(()=>{}):x();},flush:_=>P(_),get pending(){return r.length},dispose(){i=true,R(),r=[],o=0;}}}var qt=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]]),F=class extends p{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:qt.get(this.code))??super.retryable}},M=class{#e;constructor(e){this.#e=e;}async invoke(e,t={}){let r=Mt(e),o;try{o=await this.#e(r,t.signal===void 0?{}:{signal:t.signal});}catch(a){throw a instanceof p?jt(a):a}return $t(o)}};function Mt(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 $t(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 jt(n){let{details:e,invocationId:t}=se(n),o=e?.untrusted?.message,a=e?.reason;return new F(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 O=n=>typeof n=="string"?n:"",Bt=/(?:Z|[+-]\d{2}:?\d{2})$/i;function ve(n){let e=O(n);if(!Bt.test(e))return 0;let t=Date.parse(e);return Number.isFinite(t)?Math.floor(t/1e3):0}var Ft=/^[0-9a-f]{1,64}$/,Ut=/^[0-9a-f]{16}$/,Dt=/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/,Nt=/^(?:[01]\d|2[0-3]):[0-5]\d$/;function Ht(n){let e=O(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])],i=new Date(Date.UTC(r,o-1,a));return i.getUTCFullYear()===r&&i.getUTCMonth()===o-1&&i.getUTCDate()===a?e:void 0}var Vt=4096,Wt=new TextEncoder;function zt(n){let e=O(n.challenge_data),t=O(n.challenge_data_hash);return e===""||!Ut.test(t)?{}:Wt.encode(e).length>Vt?{}:{data:e,dataHash:t}}function Kt(n){let e=n??{},t=ve(e.period_start_at),r=ve(e.period_ends_at);if(t===0||r===0)return;let o=O(e.slug),a=O(e.seed),i=Ht(e.period_local_date),d=O(e.boundary),u=O(e.reset_time);return {id:O(e.progression_definition_id),...o===""?{}:{slug:o},window:O(e.window),periodStartAt:t,periodEndsAt:r,...i===void 0?{}:{periodLocalDate:i},boundary:Dt.test(d)?d:"",resetTime:Nt.test(u)?u:"",seed:Ft.test(a)?a:"",...zt(e)}}function Se(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 xe(n){let e=n??{};return Array.isArray(e.challenges)?e.challenges.map(Kt).filter(t=>t!==void 0):[]}function we(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().");Se(r);let a=await n({player_id:t,...r===void 0?{}:{periods_back:r},...o===void 0?{}:{signal:o}});return xe(a)}}}function Yt(n){let e={};for(let[t,r]of Object.entries(Q))if(typeof r=="function"){let o=r;e[t]=(...a)=>o(n,...a);}return e}function z(n,e={}){let t=e.clock??q,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=J(n,{clock:t,...e.onAuthStateChange===void 0?{}:{onStateChange:e.onAuthStateChange}}),a=ie({baseUrl:r,tokenSource:o.source,defaultQuery:{game_id:n.gameId,channel:n.channel},...e.fetch===void 0?{}:{fetch:e.fetch}}),i=new L({transport:a,clock:t}),d={gameId:n.gameId,channel:n.channel,matchId:n.matchId,...n.gameRoomId===void 0?{}:{gameRoomId:n.gameRoomId}},u=Yt({client:i,gameId:()=>d.gameId}),c=d.gameRoomId,g=n.endMatch,S=n.log,w=Re({client:i,clock:t,gameId:d.gameId,...typeof S=="function"?{log:y=>S.call(n,y)}:{}});return {identity:d,client:i,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,x)=>{let A=await u.setMatchBackfill(d.matchId,{open:y.open,...y.seats===void 0?{}:{seats:y.seats}},x===void 0?{}:{signal:x});return {open:A.open,seats:A.seats??null}},reportPlayers:async(y,x)=>(await u.reportMatchPlayers(d.matchId,{players:y.map(P=>({player_id:P.playerId,state:P.state}))},x===void 0?{}:{signal:x})).players.map(P=>({playerId:P.player_id,outcome:P.outcome}))},analytics:w,functions:new M(u.invokeFunction),challenges:we(u.listChallenges),...c===void 0?{}:{room:{get:y=>u.getGameRoomV2(c,y),args:async y=>{let x=await u.getGameRoomV2(c,y?.signal===void 0?{}:{signal:y.signal}),A=x.modified?x.body:void 0;if(A?.room_args==null||A.room_args_template==null)throw new p("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:()=>{w.dispose(),o.dispose();}}}var Jt=["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"],Xt=new Set(Jt);function ke(n){return typeof n=="string"&&Xt.has(n)?n:"unknown"}var Pe="[gem: use gem.text.toPlain()]",Ae=new Set,Oe;function Qt(n){try{if(Ae.has(n))return;let e=globalThis.location;if(Oe??=!!e?.port&&ge(e.hostname??""),!Oe)return;Ae.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Pe}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
1
|
+
var Me=Object.defineProperty;var $e=(n,e)=>{for(var t in e)Me(n,t,{get:e[t],enumerable:true});};globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:21.0.0]";var je={initialDelayMs:2e3,maxDelayMs:6e4};function Be(n,e){return e!==void 0?!e:n==="chain_exhausted"}var N=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 h(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 h("chain_exhausted","the token chain reached its ceiling");if(this.inFlight===null&&this.options.clock.now()<this.notBefore)throw new h(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}=je,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 h("internal","token arrived for a superseded generation");return this.grant=o,this.rejected=false,this.enter("ready"),o.token}).catch(o=>{let a=Fe(o),{retryable:i,retryAfterMs:d}=o??{};throw t===this.generation&&(Be(a,typeof i=="boolean"?i:void 0)?this.enter("lost",a):(this.scheduleRetry(typeof d=="number"&&Number.isFinite(d)&&d>0?d:void 0),this.enter("degraded",a))),o instanceof h?o:new h(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)}},h=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 Fe(n){if(n instanceof h)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}},H=["if-match","if-none-match"],ee=4096;function Ue(n){if(!n)return;let e={};for(let[t,r]of Object.entries(n)){let o=t.toLowerCase();if(!H.includes(o)||r===void 0)continue;let a=typeof r=="string"?De(r):r;if(typeof a!="string"||a.length>ee||!Ne(a))throw new f(`the ${o} header value cannot be sent \u2014 it is not a string, is longer than ${ee} 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 De(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 Ne(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 He(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 ne(n,e){let t=e.replayUnauthorizedOnce??true;return {async request(r){let o={...r},a=Ue(o.headers),i=He(o),d=Object.freeze({...o,headers:a,...i===void 0?{}:{bytes:i}}),u=await e.tokenSource.get(),c=await n(d,u);if(c.status!==401||!t)return c;e.tokenSource.invalidate(u);let g=await e.tokenSource.get(),S=await n(d,g);if(S.status===401)throw e.tokenSource.close("unavailable"),new h("unavailable","the server refused a freshly minted token");return S}}}function te(n){let e=typeof n=="string"?new TextEncoder().encode(n):n;return btoa(Ve(e))}function Ve(n){let t="";for(let r=0;r<n.length;r+=32768)t+=String.fromCharCode(...n.subarray(r,r+32768));return t}var p=class extends Error{constructor(t,r,o,a,i){super(`${t} (${r}): ${o}`);this.reason=t;this.status=r;this.detail=o;this.retryAfterSeconds=a;this.code=i;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}},ae=new WeakMap;function se(n){return ae.get(n)??{}}var We=new Set([408,429,500,502,503,504]),ze=6e4,Ke=new WeakMap;function re(n){return n!==null&&(n.reason==="rate_limited"||n.retryAfterSeconds!==void 0)}function Ye(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 L=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,...$(o)},o.replaySafe===true,o.retryRateLimited!==false)}async post(e,t,r,o={}){return this.send({method:"POST",path:e,body:t,signal:r,...$(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,...$(o)},o.replaySafe===true,o.retryRateLimited!==false);return {bytes:Je(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,...$(o)},o.replaySafe===true,o.retryRateLimited!==false)}async delete(e,t,r={}){return this.send({method:"DELETE",path:e,signal:t,...$(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?Ye(this.options.maxRetries)+1:1,a=null;if(e.signal?.aborted)throw oe(e.signal);let i=e.signal===void 0?void 0:Ke.get(e.signal);for(let d=0;d<o;d++){if(d>0){let g=this.backoffMs(d,a);if(re(a)&&i!==void 0&&g>=i())throw a;await this.backoff(g,e.signal);}if(e.signal?.aborted)throw re(a)&&i!==void 0&&i()<=0?a:oe(e.signal);let u;try{u=await this.options.transport.request(e);}catch(g){if(h.isAuthError(g)||f.isRefusal(g)||e.signal?.aborted||Xe(g))throw g;a=new p("unavailable",0,String(g));continue}if(u.status>=200&&u.status<300||u.status===304)return u;let c=Qe(u);if(!We.has(u.status)||u.status===429&&!r)throw c;a=c;}throw a??new p("unavailable",0,"no attempt was made")}backoffMs(e,t){let r=t?.retryAfterSeconds,o=r!==void 0?r*1e3:2**(e-1)*250,a=2**(e-1)*250;return Math.min(Math.max(o,a),ze)}backoff(e,t){return t?.aborted?Promise.resolve():new Promise(r=>{let o=this.options.clock,a=false,i=false,d,u=()=>{a||(a=true,i&&o.clearTimeout(d),t?.removeEventListener("abort",u),r());};t?.addEventListener("abort",u,{once:true}),d=o.setTimeout(u,e),i=true,a&&o.clearTimeout(d);})}};function Je(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 $(n){return {...n.keepalive===void 0?{}:{keepalive:n.keepalive},...n.ifMatch===void 0?{}:{headers:{"if-match":n.ifMatch}}}}function oe(n){return n.reason!==void 0?n.reason:new DOMException("The operation was aborted.","AbortError")}function Xe(n){let e=n?.name;return e==="AbortError"||e==="TimeoutError"}function Qe(n){let e=en(n),t=tn(n.body),r={...t===void 0?{}:{details:t},...n.invocationId===void 0?{}:{invocationId:n.invocationId}};return Object.keys(r).length>0&&ae.set(e,r),e}var Ze=new Map([["insufficient_funds","insufficient_funds"]]);function en(n){let{status:e,body:t}=n,r=nn(t),o=n.retryAfterSeconds??on(t),a=rn(t),i=a===void 0?void 0:Ze.get(a);if(i!==void 0)return new p(i,e,r,void 0,a);switch(e){case 304:return new p("not_modified",e,r);case 400:case 422:return new p("invalid",e,r,void 0,a);case 403:return new p("forbidden",e,r,void 0,a);case 404:return new p("not_found",e,r,void 0,a);case 402:return new p("insufficient_funds",e,r,void 0,a);case 409:return new p("conflict",e,r,void 0,a);case 412:return new p("precondition_failed",e,r,void 0,a);case 413:return new p("too_large",e,r,void 0,a);case 429:return new p("rate_limited",e,r,o,a);default:return new p("unavailable",e,r,o,a)}}function nn(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 tn(n){let t=n?.error?.details;return t===null?void 0:t}function rn(n){let t=n?.error?.code;return typeof t=="string"&&t.length>0?t:void 0}function on(n){let e=n?.retryAfterSeconds;return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:void 0}function an(n){let e=n.lastIndexOf("@");return (e===-1?n:`***@${n.slice(e+1)}`).slice(0,200)}var sn=new Set(H);function ln(n){if(!n)return {};let e={};for(let[t,r]of Object.entries(n))sn.has(t.toLowerCase())&&(e[t.toLowerCase()]=r);return e}function ie(n){let e=n.fetch??globalThis.fetch.bind(globalThis);return ne(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"?an(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 R=o.username!==""||o.password!=="",y=a.username!==o.username||a.password!==o.password,x=R?y?"baseUrl (and possibly the request path)":"baseUrl":"request path";throw new f(`the ${x} 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[R,y]of Object.entries(n.defaultQuery??{}))a.searchParams.has(R)||a.searchParams.set(R,y);let i=t.bytes,d=t.bytes===void 0?void 0:"application/octet-stream";try{t.body!==void 0&&(i=JSON.stringify(t.body),d="application/json");}catch(R){throw new f(`the body of ${t.method} ${JSON.stringify(a.pathname)} could not be serialised to JSON: ${String(R)}. 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}`,...d===void 0?{}:{"content-type":d},...ln(t.headers)},body:i,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 R=await u.text();if(R.length>0)try{c=JSON.parse(R);}catch{c=R;}}let g=u.headers.get("etag"),S=dn(u.headers.get("retry-after")),w=u.headers.get("x-gem-invocation-id");return {status:u.status,body:c,...g===null?{}:{etag:g},...S===void 0?{}:{retryAfterSeconds:S},...w===null||w===""?{}:{invocationId:w}}},n)}function dn(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 un="gem-web-bridge";var cn=65536,le=8,V=5e3,de=1024;var W=Object.create(null);var ue=Object.create(null);function mn(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 gn(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 pn(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function yn(n){return /^[a-z_]+$/.test(n)}function fn(n){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(n)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(n)&&!n.includes("--")}function K(n,e,t){if(e>le)return `nesting deeper than ${le}`;if(++t.nodes>V)return `more than ${V} values`;if(t.bytes>t.cap)return `over the ${t.cap} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let 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>de)return `an array of ${n.length} entries, over the ${de} limit`;t.bytes+=2;for(let d of n){let u=K(d,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 i=n;for(let d in i){if(!Object.prototype.hasOwnProperty.call(i,d))continue;if(++t.nodes>V)return `more than ${V} values`;if(t.bytes+=d.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let u=K(i[d],e+1,t);if(u)return u}return null}function hn(n,e=cn){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=K(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var _n={origin:[mn,"not a well-formed origin"],url:[gn,"not a safe absolute http(s) URL"],bcp47:[pn,"not a well-formed BCP 47 tag"],code:[yn,"not a code of lower-case letters and underscores"],gameRef:[fn,"not a game id or web address"]};function ce(n,e,t){for(let r of e){let o=t===""?r[0]:`${t}.${r[0]}`,a=n[r[0]];if(a==null){if(r[1]<"a")continue;return `${o}: required`}let i=Y(a,r,o);if(i)return i}return null}function Y(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:Y(n,typeof n=="string"?[e[0],"s",e[2]]:[e[0],"n",e[3],e[4]],t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e[2])return `${t}: exceeds ${e[2]} characters`;if(e[3]){let[o,a]=_n[e[3]];if(!o(n))return `${t}: ${a}`}return null}case "n":case "i":return typeof n!="number"||!(r=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${r=="i"?"integer":"number"}`:n<e[2]||n>e[3]?`${t}: out of range ${e[2]}..${e[3]}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let o=e[2];return o.includes(n)?null:`${t}: not one of ${o.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:ce(n,e[2],t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e[2])return `${t}: exceeds ${e[2]} items`;let o=e[3],a=`${t}[]`;for(let i of n){if(i==null){if(o[1]<"a")continue;return `${a}: required`}let d=Y(i,o,a);if(d)return d}return null}default:return `${t}: unknown kind ${String(r)}`}}var me=Object.create(null);function l(n,e,t,r,o){return W[n]=t,r&&(ue[n]=r),me[n]=a=>{let i=hn(a,o);if(i)return i;if(typeof a!="object"||a===null||Array.isArray(a))return "message: expected object";let d=a;return d.gem!==un?"gem: wrong protocol marker":d.type!==n?`type: expected ${n}`:ce(d,e,"")}}var j=["","s",64],m=["requestId","s",64],B=["matchId","s",64],I=["outcome","e",["ok","declined","unavailable"]],E=["reason","S",64,"code"];l("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,j],["embedder","s",2048,"origin"],["unbidden","B"]],30);l("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,j]],5);l("gem.token",[["token","s",4096],["requestId","S",64],["generation","i",0,2147483647],["apiBaseUrl","S",512,"url"],["playerId","s",64],["gameId","s",64],["channel","e",["dev","test","release"]],["gameVersion","S",64],["expiresAt","i",0,4102444800],["chainEndsAt","i",0,4102444800],["allowDevTools","B"]],30);l("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);l("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);l("gem.pause",[["paused","b"]],120);l("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);l("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");l("gem.earnAllowanceChanged",[],30,"earnAllowance");l("gem.viewport",[["insets","o",[["top","i",0,4096],["right","i",0,4096],["bottom","i",0,4096],["left","i",0,4096]]],["reserved","A",4,["","o",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384],["control","O",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384]]]]]]],60,"viewport");l("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");l("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);l("gem.roomEvent",[["event","e",["updated","match_started","match_ready","match_failed","version_stale"]],["roomId","s",64],["matchId","S",64],["version","I",0,2147483647],["reason","S",64],["platform","E",["universal","mobile"]]],120,"rooms");l("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");l("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");l("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");l("gem.ticketEvent",[["event","e",["queued","matched","expired","cancelled","failed"]],["ticketId","s",64],["partySize","I",1,64],["queuedAt","I",0,4102444800],["expiresAt","I",0,4102444800],["reason","S",64],["roomId","S",64]],60,"ticketEvents");l("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],B,["data","j"]],1800,"p2p");l("gem.relayDraining",[B,["clusterTargetId","s",64],["relayIps","a",16,j],["drainingSince","s",40],["deadline","s",40]],60,"p2pRelayDrain");l("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],B,["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");l("gem.partyResult",[m,["partyId","S",64]],30,"parties");l("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,j]],12,"clusterTarget");l("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");l("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");l("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");l("gem.requestContext",[],12,"feedbackContext");l("gem.gameContext",[["context","j"]],12,"feedbackContext");l("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],B,["data","j"]],1200,"p2p");l("gem.dedicatedOffer",[B,["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");l("gem.partyRequest",[m],12,"parties");l("gem.ready",[],5);l("gem.engaged",[],5,"engagement");l("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);l("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);l("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");l("gem.localServer",[m,["serverId","S",128]],30,"localHost");l("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");l("gem.exit",[["reason","S",64]],5);l("gem.openExternal",[["url","s",2048,"url"]],6);l("gem.requestFullscreen",[["enabled","b"]],30);l("gem.audioState",[["muted","b"]],120,"audioState");l("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");l("gem.walletDisplay",[["visible","b"],["currencies","A",8,j],["score","B"]],60,"walletDisplay");l("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");l("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");l("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");l("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");l("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");l("gem.settingsDeclare",[["settings","a",8,["","o",[["id","s",40],["label","s",40],["kind","e",["toggle","slider","choice"]],["value","u",40,-1e6,1e6],["quick","B"],["icon","S",16,"code"],["min","N",-1e6,1e6],["max","N",-1e6,1e6],["step","N",0,1e6],["options","A",4,["","o",[["id","s",40],["label","s",40]]]]]]]],120,"gameSettings");l("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");l("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");l("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");l("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");l("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");l("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");l("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);l("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");l("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");l("gem.purchaseResult",[m,I,E,["purchaseId","S",64]],30,"purchases");l("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");l("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");l("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");l("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");l("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");l("gem.momentResult",[m,["shown","b"]],12,"moments");l("gem.requestSignIn",[m],6,"signIn");l("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");l("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");l("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");l("gem.requestAppLinks",[m],12,"appLinks");l("gem.appLinksResult",[m,I,E,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");l("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");l("gem.openAppResult",[m,I,E],30,"appLinks");l("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");l("gem.openGameResult",[m,I,E],30,"openGame");l("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");l("gem.similarGamesResult",[m,I,E,["games","a",24,["","o",[["gameId","s",36,"gameRef"],["slug","S",40,"gameRef"],["iconUrl","s",1024,"url"],["capsuleLandscapeUrl","s",1024,"url"],["capsulePortraitUrl","s",1024,"url"],["previewVideoUrl","S",1024,"url"],["previewPosterUrl","S",1024,"url"],["untrusted","o",[["name","s",64],["tagline","s",64],["shortDescription","s",320],["tags","a",10,["","s",32]]]]]]]],30,"openGame",524288);l("gem.requestStoreOffers",[m],12,"store");l("gem.storeOffersResult",[m,I,E,["offers","a",32,["","o",[["skuId","s",64],["kind","e",["gem_pack","boost","pass","bundle"]],["name","s",80],["description","s",400],["imageUrl","S",1024,"url"],["priceDisplay","s",32],["grant","O",[["currency","s",32],["amount","i",1,1e9]]]]]]],30,"store",262144);l("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");l("gem.storePurchaseResult",[m,I,E],30,"store");l("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");l("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");l("gem.storeOfferSheetResult",[m,I,E],30,"storeOfferSheet");Object.freeze(me);W.__malformed=60;Object.freeze(W);Object.freeze(ue);var q={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)};function ge(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}var bn=1e4;function pe(n,e){return J(n,e)}function J(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=i=>({token:i.token,expiresAt:i.expiresAt,chainEndsAt:Number.POSITIVE_INFINITY}),r=null,o=new N({clock:e.clock,...e.onStateChange===void 0?{}:{onStateChange:e.onStateChange},request:()=>new Promise((i,d)=>{let u=e.clock.setTimeout(()=>{r=null,d({reason:"unavailable",retryable:true});},e.timeoutMs??bn);r={resolve:c=>{e.clock.clearTimeout(u),i(c);},reject:d,timer:u};})}),a=n.onServerToken(i=>{let d=t(i),u=r;if(u){r=null,u.resolve(d);return}o.accept(d);});return {source:o,dispose(){a(),o.dispose(),r&&(e.clock.clearTimeout(r.timer),r.reject({reason:"unavailable",retryable:false}),r=null);}}}var Q={};$e(Q,{acceptTransfer:()=>Rn,addEntityProgression:()=>vn,adminAction:()=>Sn,cancelInventoryTransfer:()=>xn,claimEntityProgressionRewards:()=>wn,closeGameRoom:()=>kn,createEntityBatchOrder:()=>An,createEntityOrder:()=>On,createSession:()=>Pn,deleteEntityGameSetting:()=>Gn,deleteEntitySave:()=>Tn,deleteSession:()=>Cn,deleteShowcaseValue:()=>In,executeEntityRecipe:()=>En,finalizeSaveUpload:()=>Ln,getChannelAssignment:()=>qn,getDropRates:()=>Mn,getEntityCurrency:()=>$n,getEntityGameSetting:()=>jn,getEntityInventory:()=>Bn,getEntityInventoryItem:()=>Fn,getEntityLeaderboard:()=>Un,getEntityOrder:()=>Dn,getEntityProgression:()=>Nn,getEntityRecipeAvailability:()=>Hn,getEntityRecipeUsage:()=>Vn,getEntitySave:()=>Wn,getEvent:()=>zn,getGameRoomV2:()=>Kn,getLeaderboard:()=>Yn,getPlayerGameProfile:()=>Jn,getPurchaseCatalog:()=>Xn,getResolvedTemplate:()=>Qn,getRoomArgs:()=>Zn,getShowcaseValue:()=>et,getTransferDetails:()=>nt,ingestAnalyticsEvents:()=>X,initiateInventoryTransfer:()=>tt,invokeFunction:()=>rt,kickPlayer:()=>ot,listChallenges:()=>at,listEntityGameSettings:()=>st,listEntityOrders:()=>it,listEntityProgressions:()=>lt,listEntitySaves:()=>dt,listEvents:()=>ut,listGameRooms:()=>ct,listResolvedTemplates:()=>mt,listTransfers:()=>gt,maxEntityProgression:()=>pt,putEntityGameSetting:()=>yt,putRoomArgs:()=>ft,putShowcaseValue:()=>ht,rejectTransfer:()=>_t,reportMatchPlayers:()=>bt,requestSaveUploadUrl:()=>Rt,resetEntityProgression:()=>vt,setMatchBackfill:()=>St,simulateEntityOrder:()=>xt,transferHostV2:()=>wt,updateGameRoomV2:()=>kt});function s(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 Rn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/accept`;return n.client.post(r,void 0,t?.signal)}async function vn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/add`;return n.client.post(i,o,a?.signal)}async function Sn(n,e,t,r){let o=`/v1/voice/sessions/${s(e)}/admin`;return n.client.post(o,t,r?.signal)}async function xn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/cancel`;return n.client.post(r,void 0,t?.signal)}async function wn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/claim`;return n.client.post(i,o,a?.signal)}async function kn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.delete(r,t?.signal)}async function An(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/batch`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function On(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Pn(n,e,t){return n.client.post("/v1/voice/sessions",e,t?.signal)}async function Gn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function Tn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function Cn(n,e,t){let r=`/v1/voice/sessions/${s(e)}`;return n.client.delete(r,t?.signal)}async function In(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.delete(o,r?.signal)}async function En(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/recipes/execute`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function Ln(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/finalize`;return n.client.post(i,o,a?.signal)}async function qn(n,e,t){let r=`/v1/games/${s(n.gameId())}/config/channels/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Mn(n,e,t){let r=`/v1/inventory/loot-tables/${s(e)}/drop-rates`;return n.client.get(r,t?.signal)}async function $n(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/currencies/${s(r)}`;return n.client.get(a,o?.signal)}async function jn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(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,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(),i=`/v1/inventory/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function Fn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/items/${s(r)}`;return n.client.get(a,o?.signal)}async function Un(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 i=a.toString(),d=`/v1/leaderboards/${s(e)}/${s(t)}/${s(r)}`+(i?`?${i}`:"");return n.client.get(d,o?.signal)}async function Dn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/${s(r)}`;return n.client.get(a,o?.signal)}async function Nn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}`;return n.client.get(a,o?.signal)}async function Hn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/catalogs/${s(r)}/recipes/${s(o)}/availability`;return n.client.get(i,a?.signal)}async function Vn(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}/recipe-usage`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function Wn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function zn(n,e,t){let r=`/v1/events/${s(e)}`;return n.client.get(r,t?.signal)}async function Kn(n,e,t){let r=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Yn(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/${s(e)}`+(o?`?${o}`:"");return n.client.get(a,t?.signal)}async function Jn(n,e,t){let r=`/v1/profiles/players/${s(e)}`;return n.client.get(r,t?.signal)}async function Xn(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 Qn(n,e,t){let r=`/v1/game-room-templates/${s(e)}`;return n.client.get(r,t?.signal)}async function Zn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.get(r,t?.signal)}async function et(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function nt(n,e,t){let r=`/v1/inventory/transfers/${s(e)}`;return n.client.get(r,t?.signal)}async function X(n,e,t){let r=`/v1/games/${s(n.gameId())}/analytics/events`;return n.client.post(r,e,t?.signal,{replaySafe:true,retryRateLimited:false})}async function tt(n,e,t){return n.client.post("/v1/inventory/transfers",e,t?.signal)}async function rt(n,e,t){return n.client.postBytes("/v1/functions/invoke",e,t?.signal)}async function ot(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/kick/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function at(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 st(n,e,t,r){let o=`/v1/settings/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function it(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(),i=`/v1/inventory/${s(e)}/${s(t)}/orders`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function lt(n,e,t,r){let o=`/v1/inventory/${s(e)}/${s(t)}/progressions`;return n.client.get(o,r?.signal)}async function dt(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(),i=`/v1/saves/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function ut(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 ct(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/${s(n.gameId())}/game-rooms`+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function mt(n,e){return n.client.get("/v1/game-room-templates",e?.signal)}async function gt(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 pt(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/max`;return n.client.post(i,o,a?.signal)}async function yt(n,e,t,r,o,a){let i=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.put(i,o,a?.signal,{...a?.ifMatch===void 0?{}:{ifMatch:a.ifMatch}})}async function ft(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.put(o,t,r?.signal)}async function ht(n,e,t,r,o){let a=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.put(a,r,o?.signal)}async function _t(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/reject`;return n.client.post(r,void 0,t?.signal)}async function bt(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/players/report`;return n.client.post(o,t,r?.signal)}async function Rt(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/upload-url`;return n.client.post(i,o,a?.signal,{replaySafe:true})}async function vt(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/reset`;return n.client.post(i,o,a?.signal)}async function St(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/backfill`;return n.client.put(o,t,r?.signal)}async function xt(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/simulate`;return n.client.post(a,r,o?.signal)}async function wt(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}/transfer/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function kt(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.patch(o,t,r?.signal,{...r?.ifMatch===void 0?{}:{ifMatch:r.ifMatch}})}var ye="gem_";function fe(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 Pt=1e4,Gt=50,Tt=1e3,Ct=65536-4*1024,he=n=>typeof n=="number"?n:0,It=n=>typeof n=="string"?n:"";function _e(){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 Et(n){return n?.retryable===true}function Lt(n,e){let t=n??{},r=Array.isArray(t.rejected)?t.rejected.map(o=>{let a=o??{};return {index:he(a.index),code:It(a.code)}}):[];return {accepted:he(t.accepted),rejected:r,dropped:e,duplicate:t.duplicate===true}}function be(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 Re(n){let e=Math.max(1,Math.floor(n.maxQueue??Tt)),t={client:n.client,gameId:()=>n.gameId},r=[],o=0,a=null,i=false,d=null,u=0,c=0,g=false,S=false,w=_=>{n.log?.(_);},R=()=>{a!==null&&(n.clock.clearTimeout(a),a=null);},y=()=>Math.max(0,c-n.clock.now()),x=()=>{a!==null||i||d!==null||(a=n.clock.setTimeout(()=>{a=null,P().catch(()=>{});},Math.max(Pt,y())));},A=()=>{let _=Math.min(r.length,100),b=128;for(let G=0;G<_;G+=1){let T=r[G];if(T===void 0)break;if(b+=JSON.stringify(be(T,0)).length+1,b>Ct)return Math.max(1,G)}return _};async function P(_){for(;d!==null;)await d.catch(()=>{});if(r.length===0)return {accepted:0,rejected:[],dropped:0,duplicate:false};let b=Le(_);d=b;try{return await b}finally{d===b&&(d=null),r.length>0&&x();}}async function Le(_){R();let b=r.splice(0,A()),G=o;o=0;let T=Math.round(n.clock.now()),D={batch_id:_e(),sent_at_ms:T,events:b.map(k=>be(k,T))};try{let k=await X(t,D,_===void 0?void 0:{signal:_}),C=Lt(k,G);u=0,c=0;for(let qe of C.rejected)w(`[gem] analytics: an event was refused (${qe.code}). See the analytics_events section of your game config.`);return C}catch(k){if(Et(k)){for(r=[...b,...r],o+=G;r.length>e;)r.shift(),o+=1;u+=1,c=n.clock.now()+fe(u,k);}else w(`[gem] analytics: ${b.length} event(s) were discarded \u2014 the platform refused the batch and will refuse it again: ${String(k)}`);throw k}}return {track(_,b,G={}){if(i)return;if(typeof _!="string"||_===""){S||(S=true,w(`[gem] analytics: '${b}' 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(b.startsWith(ye)){g||(g=true,w(`[gem] analytics: '${b}' 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 T={},D={};for(let[k,C]of Object.entries(G))typeof C=="string"?T[k]=C:typeof C=="number"&&Number.isFinite(C)&&(D[k]=C);for(;r.length>=e;)r.shift(),o+=1;r.push({uid:_e(),playerId:_,name:b,atMs:n.clock.now(),dimensions:T,measures:D}),r.length>=Gt&&d===null&&y()===0?P().catch(()=>{}):x();},flush:_=>P(_),get pending(){return r.length},dispose(){i=true,R(),r=[],o=0;}}}var qt=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]]),F=class extends p{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:qt.get(this.code))??super.retryable}},M=class{#e;constructor(e){this.#e=e;}async invoke(e,t={}){let r=Mt(e),o;try{o=await this.#e(r,t.signal===void 0?{}:{signal:t.signal});}catch(a){throw a instanceof p?jt(a):a}return $t(o)}};function Mt(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 $t(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 jt(n){let{details:e,invocationId:t}=se(n),o=e?.untrusted?.message,a=e?.reason;return new F(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 O=n=>typeof n=="string"?n:"",Bt=/(?:Z|[+-]\d{2}:?\d{2})$/i;function ve(n){let e=O(n);if(!Bt.test(e))return 0;let t=Date.parse(e);return Number.isFinite(t)?Math.floor(t/1e3):0}var Ft=/^[0-9a-f]{1,64}$/,Ut=/^[0-9a-f]{16}$/,Dt=/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/,Nt=/^(?:[01]\d|2[0-3]):[0-5]\d$/;function Ht(n){let e=O(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])],i=new Date(Date.UTC(r,o-1,a));return i.getUTCFullYear()===r&&i.getUTCMonth()===o-1&&i.getUTCDate()===a?e:void 0}var Vt=4096,Wt=new TextEncoder;function zt(n){let e=O(n.challenge_data),t=O(n.challenge_data_hash);return e===""||!Ut.test(t)?{}:Wt.encode(e).length>Vt?{}:{data:e,dataHash:t}}function Kt(n){let e=n??{},t=ve(e.period_start_at),r=ve(e.period_ends_at);if(t===0||r===0)return;let o=O(e.slug),a=O(e.seed),i=Ht(e.period_local_date),d=O(e.boundary),u=O(e.reset_time);return {id:O(e.progression_definition_id),...o===""?{}:{slug:o},window:O(e.window),periodStartAt:t,periodEndsAt:r,...i===void 0?{}:{periodLocalDate:i},boundary:Dt.test(d)?d:"",resetTime:Nt.test(u)?u:"",seed:Ft.test(a)?a:"",...zt(e)}}function Se(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 xe(n){let e=n??{};return Array.isArray(e.challenges)?e.challenges.map(Kt).filter(t=>t!==void 0):[]}function we(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().");Se(r);let a=await n({player_id:t,...r===void 0?{}:{periods_back:r},...o===void 0?{}:{signal:o}});return xe(a)}}}function Yt(n){let e={};for(let[t,r]of Object.entries(Q))if(typeof r=="function"){let o=r;e[t]=(...a)=>o(n,...a);}return e}function z(n,e={}){let t=e.clock??q,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=J(n,{clock:t,...e.onAuthStateChange===void 0?{}:{onStateChange:e.onAuthStateChange}}),a=ie({baseUrl:r,tokenSource:o.source,defaultQuery:{game_id:n.gameId,channel:n.channel},...e.fetch===void 0?{}:{fetch:e.fetch}}),i=new L({transport:a,clock:t}),d={gameId:n.gameId,channel:n.channel,matchId:n.matchId,...n.gameRoomId===void 0?{}:{gameRoomId:n.gameRoomId}},u=Yt({client:i,gameId:()=>d.gameId}),c=d.gameRoomId,g=n.endMatch,S=n.log,w=Re({client:i,clock:t,gameId:d.gameId,...typeof S=="function"?{log:y=>S.call(n,y)}:{}});return {identity:d,client:i,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,x)=>{let A=await u.setMatchBackfill(d.matchId,{open:y.open,...y.seats===void 0?{}:{seats:y.seats}},x===void 0?{}:{signal:x});return {open:A.open,seats:A.seats??null}},reportPlayers:async(y,x)=>(await u.reportMatchPlayers(d.matchId,{players:y.map(P=>({player_id:P.playerId,state:P.state}))},x===void 0?{}:{signal:x})).players.map(P=>({playerId:P.player_id,outcome:P.outcome}))},analytics:w,functions:new M(u.invokeFunction),challenges:we(u.listChallenges),...c===void 0?{}:{room:{get:y=>u.getGameRoomV2(c,y),args:async y=>{let x=await u.getGameRoomV2(c,y?.signal===void 0?{}:{signal:y.signal}),A=x.modified?x.body:void 0;if(A?.room_args==null||A.room_args_template==null)throw new p("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:()=>{w.dispose(),o.dispose();}}}var Jt=["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"],Xt=new Set(Jt);function ke(n){return typeof n=="string"&&Xt.has(n)?n:"unknown"}var Pe="[gem: use gem.text.toPlain()]",Ae=new Set,Oe;function Qt(n){try{if(Ae.has(n))return;let e=globalThis.location;if(Oe??=!!e?.port&&ge(e.hostname??""),!Oe)return;Ae.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Pe}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
2
2
|
${new Error().stack}`);}catch{}}var v=class{#e;#n;constructor(e,t="player text"){this.#e=e,this.#n=t,Object.freeze(this);}toString(){return Qt(this.#n),Pe}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function U(n,e){return new v(n,e)}var Ge={toPlain(n){return v.unwrap(n)},compare(n,e,t){let r=n instanceof v?v.unwrap(n):n,o=e instanceof v?v.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 v?v.unwrap(n):n,r=e instanceof v?v.unwrap(e):e;return t.normalize("NFC")===r.normalize("NFC")}};var Te="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 Z(n,e={}){let t=e.clock??q,r=()=>{let d=n();if(d===null)throw new Error(Te);return d},o=null,a=()=>{if(o===null)throw new Error(Te);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(d){let c=r().profile?.(d);if(!c)return null;let g=c.displayName;return typeof g!="string"||g===""?null:{displayName:U(g,"profile.displayName"),...typeof c.displayTag=="string"&&c.displayTag!==""?{displayTag:U(c.displayTag,"profile.displayTag")}:{},...typeof c.discriminator=="string"?{discriminator:c.discriminator}:{}}},playerNumber(d){return r().playerNumber?.(d)},send(d,u,c){c===void 0?r().send(d,u):r().send(d,u,c);},broadcast(d,u){u===void 0?r().broadcast(d):r().broadcast(d,u);},kick(d,u){u===void 0?r().kick(d):r().kick(d,u);},log(d){r().log(d);},warn(d){r().warn(d);}},attach:()=>{o=z(r(),{...e,clock:t});}}}function Ce(n,e={}){let{server:t,attach:r}=Z(()=>n,e);return r(),t}function Zt(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:ke(e)}}}var er=1e4;function nr(n){let e=false;return t=>{if(e)return;let r;try{r=n.match.reportPlayers([{playerId:t,state:"connected"}],AbortSignal.timeout(er));}catch{return}r.catch(o=>{o instanceof p&&o.status>=400&&o.status<500&&(e=o.status!==429);});}}function Ie(n,e={}){return ()=>{let t=null,{server:r,attach:o}=Z(()=>t,e),a=n(r),i={async init(u){t=u,o(),await a.init?.();},async shutdown(u){let c=Zt(u);try{await a.shutdown?.(c);}finally{r.dispose();}}},d=nr(r);return i.join=(u,c)=>(d(u),a.join?.(u,c)),a.leave&&(i.leave=(u,c)=>a.leave?.(u,c)),a.message&&(i.message=(u,c,g,S)=>a.message?.(u,c,g,S)),i}}var tr=new Set(["unlocked","already_unlocked","suppressed"]);function Ee(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"||!tr.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{h as AuthError,H as CONDITIONAL_HEADERS,M as FunctionsApi,p as GemApiError,L as GemClient,F as GemFunctionError,v as PlayerText,f as TransportRefusal,Ee as achievementReceipts,U as asPlayerText,z as createGemServer,pe as createHostTokenSource,Ce as createMatchServer,Ie as defineServer,q as systemClock,Ge as text,te as toBase64};//# sourceMappingURL=gem-web-sdk.server.js.map
|
|
3
3
|
//# sourceMappingURL=gem-web-sdk.server.js.map
|
package/dist/sdk.d.ts
CHANGED
|
@@ -1511,7 +1511,7 @@ declare class FunctionsApi {
|
|
|
1511
1511
|
}
|
|
1512
1512
|
type SessionEndReason = "grant_expiring" | "session_ended" | "navigating" | "kill_switch" | "pagehide";
|
|
1513
1513
|
type MatchTransport = "dedicated" | "p2p";
|
|
1514
|
-
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost";
|
|
1514
|
+
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost" | "gem_graphics_context_lost";
|
|
1515
1515
|
declare const PLATFORM_EVENTS: readonly PlatformEventName[];
|
|
1516
1516
|
type AnalyticsValue = string | number;
|
|
1517
1517
|
interface AnalyticsRejection$1 {
|
package/docs/analytics.md
CHANGED
|
@@ -131,10 +131,21 @@ and a game that declares no events at all still gets them:
|
|
|
131
131
|
| `gem_boot_ready` | — | `boot_ms` |
|
|
132
132
|
| `gem_match_started` / `gem_match_ended` | `transport` (`dedicated`, `p2p`) | — |
|
|
133
133
|
| `gem_auth_lost` | — | — |
|
|
134
|
+
| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |
|
|
134
135
|
|
|
135
136
|
`reason` on a session end is one of `grant_expiring`, `session_ended`, `navigating`,
|
|
136
137
|
`kill_switch` or `pagehide`.
|
|
137
138
|
|
|
139
|
+
`gem_graphics_context_lost` is the browser taking one of your WebGL contexts away — on a phone
|
|
140
|
+
usually a GPU driver fault rather than your code. `gpu` is the GPU family (`powervr_d_series`,
|
|
141
|
+
`powervr_other`, `adreno`, `mali`, `xclipse`, `apple`, `nvidia`, `amd`, `intel`, `software`,
|
|
142
|
+
`other`, `unknown`), `route` is how the browser drives it (`angle_gles`, `angle_vulkan`,
|
|
143
|
+
`angle_d3d11`, `angle_d3d9`, `angle_metal`, `angle_opengl`, `angle_other`, `native_gl`, `webgpu`,
|
|
144
|
+
`unknown`) and `host` is `android_webview` (the Arcade app) or `other`. `hidden` is 1 when the page
|
|
145
|
+
was hidden at the time, which is a phone reclaiming a backgrounded game's memory rather than a
|
|
146
|
+
crash in front of the player. It is sent whether or not your game recovers from the loss, at most
|
|
147
|
+
five times a page.
|
|
148
|
+
|
|
138
149
|
**The `gem_` prefix is reserved.** Your config cannot declare an id beginning with it and
|
|
139
150
|
`gem.analytics.track` refuses one rather than sending it, so pick another name.
|
|
140
151
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The function surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/function`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/function` v21.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.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/function` exports exactly the symbols on this page and nothing else.
|
|
6
6
|
|
|
@@ -470,7 +470,7 @@ the reading a funnel should treat as approximate.
|
|
|
470
470
|
## PlatformEventName
|
|
471
471
|
|
|
472
472
|
```ts
|
|
473
|
-
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost"
|
|
473
|
+
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost" | "gem_graphics_context_lost"
|
|
474
474
|
```
|
|
475
475
|
|
|
476
476
|
Every event the SDK emits by itself.
|
|
@@ -488,6 +488,11 @@ config names them as steps.
|
|
|
488
488
|
| `gem_boot_ready` | — | `boot_ms` |
|
|
489
489
|
| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |
|
|
490
490
|
| `gem_auth_lost` | — | — |
|
|
491
|
+
| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |
|
|
492
|
+
|
|
493
|
+
`gem_graphics_context_lost` is the browser taking a WebGL context away —
|
|
494
|
+
usually a GPU driver fault, sized per device across every game
|
|
495
|
+
(`analytics/graphics.ts` lists the dimension values).
|
|
491
496
|
|
|
492
497
|
<a id="gemerrorreason"></a>
|
|
493
498
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The browser surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v21.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.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
|
@@ -4012,7 +4012,7 @@ How a match was hosted — the closed set `gem_match_started` and
|
|
|
4012
4012
|
## PlatformEventName
|
|
4013
4013
|
|
|
4014
4014
|
```ts
|
|
4015
|
-
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost"
|
|
4015
|
+
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost" | "gem_graphics_context_lost"
|
|
4016
4016
|
```
|
|
4017
4017
|
|
|
4018
4018
|
Every event the SDK emits by itself.
|
|
@@ -4030,6 +4030,11 @@ config names them as steps.
|
|
|
4030
4030
|
| `gem_boot_ready` | — | `boot_ms` |
|
|
4031
4031
|
| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](#matchtransport)) | — |
|
|
4032
4032
|
| `gem_auth_lost` | — | — |
|
|
4033
|
+
| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |
|
|
4034
|
+
|
|
4035
|
+
`gem_graphics_context_lost` is the browser taking a WebGL context away —
|
|
4036
|
+
usually a GPU driver fault, sized per device across every game
|
|
4037
|
+
(`analytics/graphics.ts` lists the dimension values).
|
|
4033
4038
|
|
|
4034
4039
|
<a id="platform_events"></a>
|
|
4035
4040
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The server surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v21.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.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 2,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "21.0.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -4733,9 +4733,9 @@
|
|
|
4733
4733
|
"name": "PlatformEventName",
|
|
4734
4734
|
"anchor": "platformeventname",
|
|
4735
4735
|
"group": "analytics",
|
|
4736
|
-
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\"",
|
|
4736
|
+
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\" | \"gem_graphics_context_lost\"",
|
|
4737
4737
|
"documented": true,
|
|
4738
|
-
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-game.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](api-reference-game.md#matchtransport)) | — |\n| `gem_auth_lost` | — | — |",
|
|
4738
|
+
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-game.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](api-reference-game.md#matchtransport)) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away —\nusually a GPU driver fault, sized per device across every game\n(`analytics/graphics.ts` lists the dimension values).",
|
|
4739
4739
|
"deprecated": null,
|
|
4740
4740
|
"params": [],
|
|
4741
4741
|
"returns": null,
|
|
@@ -30371,9 +30371,9 @@
|
|
|
30371
30371
|
"name": "PlatformEventName",
|
|
30372
30372
|
"anchor": "platformeventname",
|
|
30373
30373
|
"group": "analytics",
|
|
30374
|
-
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\"",
|
|
30374
|
+
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\" | \"gem_graphics_context_lost\"",
|
|
30375
30375
|
"documented": true,
|
|
30376
|
-
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-function.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |\n| `gem_auth_lost` | — | — |",
|
|
30376
|
+
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-function.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away —\nusually a GPU driver fault, sized per device across every game\n(`analytics/graphics.ts` lists the dimension values).",
|
|
30377
30377
|
"deprecated": null,
|
|
30378
30378
|
"params": [],
|
|
30379
30379
|
"returns": null,
|
package/docs/brand-art.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The SDK ships the platform's own art for its two currencies, **Gems** and **Coins**, and for
|
|
4
4
|
**trophies**. You need it only if your game draws Gems, Coins or a trophy on its own screens. Read
|
|
5
|
-
§1 before you pick a file: each currency has
|
|
6
|
-
the screen is for.
|
|
5
|
+
§1 before you pick a file: each currency has more than one kind of art, and which one you use
|
|
6
|
+
depends on what the screen is for.
|
|
7
7
|
|
|
8
8
|
> **Draw the platform's current marks with `gem.branding`, and keep these files as the fallback.**
|
|
9
9
|
> `gem.branding.currencyMark('GEMS', { size: 20 })` and `gem.branding.trophy()` hand you the same
|
|
@@ -19,25 +19,35 @@ screen, and how much more your game can still pay (the earn allowance) — and n
|
|
|
19
19
|
own. §5 is the creator's ruling and is not optional.
|
|
20
20
|
|
|
21
21
|
> **The Coins art is a 3D render, open to a designer pass.** It is a gold coin with the platform's
|
|
22
|
-
> gem
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
> gem at its centre, so a player reads it as the Arcade's currency in every game rather than one
|
|
23
|
+
> game's gold. The hero and the two piles (§3) are one coin — painted gold, a thick rounded edge, a
|
|
24
|
+
> faceted gem standing on its face; the hero's also has a thin groove inside its rim and its gem
|
|
25
|
+
> raised on a sloped base — and the icons keep the earlier gold coin, whose rings and gem channel
|
|
26
|
+
> read better in a few pixels. Every coin is turned so its edge shows on the right. This
|
|
27
|
+
> art replaced the gold coin of 2026-10-25, which replaced the tangerine coin of 2026-10-21. The
|
|
28
|
+
> files keep their `ticket-*` names, as the currency keeps its slug `ARCADE_TICKETS`. If the art is
|
|
29
|
+
> redrawn again, the new art replaces these files **under the same names** in a later SDK release,
|
|
30
|
+
> so a game that imports them picks it up on its next SDK update. Lay your screens out from the
|
|
31
|
+
> sizes on this page, not from the drawing.
|
|
32
|
+
|
|
33
|
+
## 1. Pick the currency art by the role of the screen
|
|
30
34
|
|
|
31
35
|
| You are drawing | Use | Drawn at |
|
|
32
36
|
| --------------- | --- | -------- |
|
|
33
37
|
| Gems **inline**, beside a number: a price, a "+15" reward label, a small chip | the **icon**, `gem-icon-20-*` or `gem-icon-32-*` | 16–40 CSS px |
|
|
34
38
|
| Gems as **the subject of a reward presentation**: a victory or reward screen where the gem is what the player looks at, such as a victory overlay with a sunburst behind the reward | the **hero**, `gem-hero-*` | 64–256 CSS px |
|
|
35
|
-
| Coins **inline
|
|
39
|
+
| Coins **inline** in your game's screens, beside a number | the **in-game icon**, `ticket-icon-ingame-20-*` or `ticket-icon-ingame-32-*` | 16–40 CSS px |
|
|
40
|
+
| Coins as **a reward sized by how much it is**: a reward card, a results screen's payout | a **pile**, `ticket-pile-1-*` or `ticket-pile-2-*` (§3) | 64–256 CSS px |
|
|
36
41
|
| Coins as **the subject of a reward presentation** | the **hero**, `ticket-hero-*` | 64–256 CSS px |
|
|
37
42
|
|
|
38
43
|
The role decides first and the size follows from it. A price tag is inline even if you make it
|
|
39
44
|
large; a reward screen is a presentation even on a small phone.
|
|
40
45
|
|
|
46
|
+
`ticket-icon-20-*` and `ticket-icon-32-*` are the **arcade's wallet icon**: the same coin inside the
|
|
47
|
+
violet rim the Gems icon wears, which the arcade draws in its own wallet. Inside your game that rim
|
|
48
|
+
reads as the arcade's chrome, so draw the in-game icon there; a game that already draws the wallet
|
|
49
|
+
icon keeps working, and moves to the in-game one when it next touches the screen.
|
|
50
|
+
|
|
41
51
|
- **Never use the hero at icon sizes.** It is one render drawn to be seen large; shrunk to 20 px its
|
|
42
52
|
outline goes under a pixel, its facet lines vanish and the gem smudges into the background.
|
|
43
53
|
- **Never use the icon at hero sizes.** Each icon file is a separate render drawn for one small box,
|
|
@@ -114,12 +124,30 @@ outline weight — 1.5 px at 20, 2 px at 32.
|
|
|
114
124
|
| | | `ticket-icon-32-2x.png` | 64 × 64 |
|
|
115
125
|
| | | `ticket-icon-32-3x.png` | 96 × 96 |
|
|
116
126
|
|
|
117
|
-
Like the Gems icon, each size is **its own render
|
|
118
|
-
|
|
119
|
-
groove inside the rim, a crease above the edge, a channel round the gem —
|
|
127
|
+
Like the Gems icon, each size is **its own render**, not the hero scaled down: the earlier gold
|
|
128
|
+
coin, turned a little so its edge shows on the right, with the lines that part its rim, field and
|
|
129
|
+
gem — a groove inside the rim, a crease above the edge, a channel round the gem — drawn wider and
|
|
120
130
|
darker the smaller the size, so they still read in a few pixels. The 20 px coin has a slightly
|
|
121
131
|
smaller gem and no ridges on its edge. Each is drawn on its own pixel grid inside the gem icon's
|
|
122
|
-
violet outline.
|
|
132
|
+
violet outline. These are the **wallet icon**: the arcade's wallet draws the same files.
|
|
133
|
+
|
|
134
|
+
### The in-game Coins icon
|
|
135
|
+
|
|
136
|
+
The same coin for **your game's own screens**, without the violet rim. A burnt-orange edge runs
|
|
137
|
+
round its light right-hand side instead, so it stays distinct on a pale background, and its edge
|
|
138
|
+
band is smooth. It comes in the same renditions and ratios, framed exactly as the wallet icon, so
|
|
139
|
+
one swaps for the other in the same box.
|
|
140
|
+
|
|
141
|
+
| Rendition | For a CSS box of | File | Pixels |
|
|
142
|
+
| --------- | ---------------- | ---- | ------ |
|
|
143
|
+
| 20 | up to about 24 px | `ticket-icon-ingame-20-1x.png` | 20 × 20 |
|
|
144
|
+
| | | `ticket-icon-ingame-20-1.5x.png` | 30 × 30 |
|
|
145
|
+
| | | `ticket-icon-ingame-20-2x.png` | 40 × 40 |
|
|
146
|
+
| | | `ticket-icon-ingame-20-3x.png` | 60 × 60 |
|
|
147
|
+
| 32 | above 24, up to about 40 px | `ticket-icon-ingame-32-1x.png` | 32 × 32 |
|
|
148
|
+
| | | `ticket-icon-ingame-32-1.5x.png` | 48 × 48 |
|
|
149
|
+
| | | `ticket-icon-ingame-32-2x.png` | 64 × 64 |
|
|
150
|
+
| | | `ticket-icon-ingame-32-3x.png` | 96 × 96 |
|
|
123
151
|
|
|
124
152
|
## 3. The hero art
|
|
125
153
|
|
|
@@ -147,10 +175,31 @@ size you draw it multiplied by the screen's `devicePixelRatio`:
|
|
|
147
175
|
**Do not draw it larger than 256 CSS pixels.** On a 3× screen that needs more than the largest file,
|
|
148
176
|
and the art softens as it is stretched. Below 64 px, use the icon (§2).
|
|
149
177
|
|
|
178
|
+
### The coin piles
|
|
179
|
+
|
|
180
|
+
Two heaps of the hero's coin, for a Coins reward your game **offers or has just paid** where the
|
|
181
|
+
size of the heap says how much: a reward card, or a results screen's payout. Pile 1 is a few short
|
|
182
|
+
stacks; pile 2 holds about twice the coins.
|
|
183
|
+
|
|
184
|
+
| Pile | Use it for | Files |
|
|
185
|
+
| ---- | ---------- | ----- |
|
|
186
|
+
| 1 | an everyday reward | `ticket-pile-1-128.png`, `ticket-pile-1-256.png`, `ticket-pile-1-384.png`, `ticket-pile-1-512.png`, `ticket-pile-1-768.png` |
|
|
187
|
+
| 2 | a bigger reward | `ticket-pile-2-128.png`, `ticket-pile-2-256.png`, `ticket-pile-2-384.png`, `ticket-pile-2-512.png`, `ticket-pile-2-768.png` |
|
|
188
|
+
|
|
189
|
+
**Pick the pile by where the reward sits among your game's own rewards, not by a number of Coins.**
|
|
190
|
+
Where a reward's size doesn't matter, the hero does as well.
|
|
191
|
+
|
|
192
|
+
Both piles share one frame: a square, transparent PNG with a margin all round, each heap centred
|
|
193
|
+
across and standing on the same baseline. Pile 2 fills the frame and pile 1 is drawn smaller, so the
|
|
194
|
+
two side by side climb from left to right with no scaling in your layout. Draw them at the hero's
|
|
195
|
+
sizes, **64 to 256 CSS pixels**, and pick the file as for the hero above.
|
|
196
|
+
|
|
150
197
|
## 4. Loading and drawing the art
|
|
151
198
|
|
|
152
|
-
The platform serves
|
|
199
|
+
The platform serves the icons and the heroes through `gem.branding` ([branding.md](branding.md)),
|
|
153
200
|
which is what to draw first. The copies below are the fallback for when it answers `unavailable`.
|
|
201
|
+
The in-game Coins icon and the coin piles are not in the platform's manifest yet: draw them from
|
|
202
|
+
here.
|
|
154
203
|
|
|
155
204
|
All of it is in the package you already install, under `brand/`, and published beside it on the CDN —
|
|
156
205
|
the same bytes either way, for the version you are on.
|