@indiegems/gem-web-sdk 13.4.3 → 14.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 CHANGED
@@ -16,6 +16,19 @@ Its entries are grouped under up to five headings:
16
16
  - **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
17
17
  - **New capability**: something a game can now do. Nothing changes unless you use it.
18
18
 
19
+ ## 14.0.0
20
+
21
+ Upgrading from 13.4.3. Impact: required.
22
+
23
+ ### Rebuild
24
+
25
+ - The message name lists, the frame and parent message unions, and the capability union each gained new members for game settings (the gameSettings capability among them). Recompiling is enough for most games. If your code switches exhaustively over message kinds or capability names, add handling for the new members.
26
+
27
+ ### New capability
28
+
29
+ - A game can now mirror up to eight of its own settings into the Arcade's in-game menu using Gem.settings. A setting is a toggle, a slider or a choice. The Arcade shows up to two game toggles in the quick row next to its own sound and vibration controls, and lists the game's settings at the top of its settings screen. The game receives a change whenever the player alters one of them, but only for a setting it declared and only with a value of that setting's kind. Slider values are clamped and stepped. Keep drawing your own settings button and screen, because the declaration only mirrors a few settings. Do not declare the Arcade's own settings such as master volume, mute, vibration, graphics or controls.
30
+ - A game that talks to the Arcade through the frame object directly can now declare its settings with GemFrame.declareSettings and receive player changes through the onSettingChange option or callback. The declaration is held until the connection is ready and re-sent after each handshake. Updates are paced to about two per second.
31
+
19
32
  ## 13.4.3
20
33
 
21
34
  Upgrading from 13.4.2. Impact: none.
package/dist/game.d.ts CHANGED
@@ -2244,6 +2244,31 @@ interface GemMenuSelect {
2244
2244
  readonly entry: "settings" | "item";
2245
2245
  readonly id?: string;
2246
2246
  }
2247
+ interface GemSettingsDeclare {
2248
+ readonly gem: typeof PROTOCOL;
2249
+ readonly type: "gem.settingsDeclare";
2250
+ readonly settings: {
2251
+ id: string;
2252
+ label: string;
2253
+ kind: "toggle" | "slider" | "choice";
2254
+ value: boolean | number | string;
2255
+ quick?: boolean;
2256
+ icon?: string;
2257
+ min?: number;
2258
+ max?: number;
2259
+ step?: number;
2260
+ options?: {
2261
+ id: string;
2262
+ label: string;
2263
+ }[];
2264
+ }[];
2265
+ }
2266
+ interface GemSettingChange {
2267
+ readonly gem: typeof PROTOCOL;
2268
+ readonly type: "gem.settingChange";
2269
+ readonly id: string;
2270
+ readonly value: boolean | number | string;
2271
+ }
2247
2272
  interface GemPurchaseResult {
2248
2273
  readonly gem: typeof PROTOCOL;
2249
2274
  readonly type: "gem.purchaseResult";
@@ -2835,6 +2860,7 @@ interface FrameCallbacks {
2835
2860
  onToken?(msg: GemToken): void;
2836
2861
  onPause?(msg: GemPause): void;
2837
2862
  onMenuSelect?(msg: GemMenuSelect): void;
2863
+ onSettingChange?(msg: GemSettingChange): void;
2838
2864
  onPrefs?(msg: GemPrefs$1): void;
2839
2865
  onCohorts?(msg: GemCohorts$1): void;
2840
2866
  onEarnAllowanceChanged?(): void;
@@ -2919,6 +2945,7 @@ declare class GemFrame {
2919
2945
  readonly id: string;
2920
2946
  readonly label: string;
2921
2947
  }>): boolean;
2948
+ declareSettings(settings: GemSettingsDeclare["settings"]): boolean;
2922
2949
  setGemCorner(corner: GemCorner): boolean;
2923
2950
  requestShare(params: ReadonlyArray<{
2924
2951
  readonly key: string;
@@ -2996,6 +3023,43 @@ interface ControlsApi {
2996
3023
  look(options?: GemLookOptions): GemLook;
2997
3024
  readonly LOOK_BASE: GemLookBase;
2998
3025
  }
3026
+ type GemGameSettingIcon = "music" | "sfx" | "voice" | "eye" | "text" | "hint" | "speed" | "bright" | "controls" | "star";
3027
+ type GemGameSettingValue = boolean | number | string;
3028
+ interface GemGameSettingBase {
3029
+ readonly id: string;
3030
+ readonly label: string;
3031
+ readonly icon?: GemGameSettingIcon;
3032
+ }
3033
+ interface GemGameToggleSetting extends GemGameSettingBase {
3034
+ readonly kind: "toggle";
3035
+ readonly value: boolean;
3036
+ readonly quick?: boolean;
3037
+ }
3038
+ interface GemGameSliderSetting extends GemGameSettingBase {
3039
+ readonly kind: "slider";
3040
+ readonly value: number;
3041
+ readonly min?: number;
3042
+ readonly max?: number;
3043
+ readonly step?: number;
3044
+ }
3045
+ interface GemGameSettingOption {
3046
+ readonly id: string;
3047
+ readonly label: string;
3048
+ }
3049
+ interface GemGameChoiceSetting extends GemGameSettingBase {
3050
+ readonly kind: "choice";
3051
+ readonly value: string;
3052
+ readonly options: readonly GemGameSettingOption[];
3053
+ }
3054
+ type GemGameSetting = GemGameToggleSetting | GemGameSliderSetting | GemGameChoiceSetting;
3055
+ interface GemGameSettingChange {
3056
+ readonly id: string;
3057
+ readonly value: GemGameSettingValue;
3058
+ }
3059
+ interface GameSettingsApi {
3060
+ declare(settings: readonly GemGameSetting[]): boolean;
3061
+ onChange(handler: (change: GemGameSettingChange) => void): () => void;
3062
+ }
2999
3063
  type GemGraphicsQuality = "low" | "medium" | "high";
3000
3064
  interface RewardedStatus {
3001
3065
  readonly declared: boolean;
@@ -3181,6 +3245,7 @@ interface Gem {
3181
3245
  readonly avatars3d: Avatars3dApi;
3182
3246
  readonly vibration: VibrationApi;
3183
3247
  readonly controls: ControlsApi;
3248
+ readonly settings: GameSettingsApi;
3184
3249
  readonly analytics: AnalyticsApi;
3185
3250
  readonly leaderboards: LeaderboardsApi;
3186
3251
  readonly challenges: ChallengesApi;
@@ -3227,4 +3292,4 @@ interface ExitAnalytics {
3227
3292
  }
3228
3293
  declare function leaveAndExit(rooms: ExitRooms, bridge: ExitBridge | null, reason?: string, analytics?: ExitAnalytics): Promise<void>;
3229
3294
  declare function createGem(options?: GemOptions): Gem;
3230
- export { ARCADE_EMBEDDERS, AVATAR_RIGS, type AccountApi, type AdFormat, type AdOutcome, type AdOutcomeKind, type AdRequest, type AdsApi, AnalyticsApi, type AnalyticsFlushOptions, type AnalyticsFlushResult, type AnalyticsOptions, type AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Avatar3dFamily, type Avatar3dOffer, type Avatar3dPrice, type Avatar3dPurchaseRequest, type AvatarRigNode, type AvatarRigs, type Avatars3dApi, type BootMark, type BootTimeline, type Challenge, type ChallengeWindow, ChallengesApi, type ConfiguredPurchasePayment, type ConfiguredPurchaseRequest, type ConfiguredPurchaseResult, type ControlsApi, DEVICE_SETTINGS_MAX_BYTES, DedicatedServerError, type DedicatedServerFailure, type DedicatedServerSession, type DevToolsOpener, type DeviceSettings, type DeviceSettingsApi, type DeviceSettingsPutOutcome, type DirectPurchaseRequest, type DirectPurchaseResult, type EarnAllowance, type EarnAllowanceEvent, type EarnAllowancePeriod, type EarnAllowanceReadOptions, type EntityLeaderboard, type EntityLeaderboardEntry, type EntityLeaderboardOptions, type EntityType, type ExitAnalytics, type ExitBridge, type ExitRooms, type FrameState, type Friend, type FriendChallenge, FriendChallengeError, type FriendChallengeErrorCode, type FriendChallengeOutcome, type FriendChallengeOutcomeKind, type FriendChallengeRequest, type FriendChallengesPage, FriendsApi, type FriendsPage, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameConfig, type GameMenu, type GameMenuItem, type GameServerLink, type Gem, GemApiError, GemClient, type GemCohort, type GemCohorts, GemConfigError, type GemControlBox, type GemCorner, type GemDevTools, GemFrame, GemFunctionError, type GemGraphicsQuality, type GemIdentity, type GemInsets, type GemLook, type GemLookBase, type GemLookDelta, type GemLookOptions, type GemLookSpeed, type GemMatchApi, type GemMatchServerModule, type GemOptions, type GemPlayer, type GemPrefs, type GemRecommendedSettings, type GemReservedArea, GemSaveError, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerLifecycle, type GemTerminationNotice, GemUnframedError, type GemViewport, type GetLeaderboardOptions, type JoinWelcome, type Leaderboard, type LeaderboardEntry, type LeaderboardPeriod, LeaderboardsApi, type ListChallengesOptions, type ListFriendChallengesOptions, type ListFriendsOptions, MAX_PERIODS_BACK, type MatchConnectOptions, type MatchConnection, type MatchHosting, type MatchHostingMode, type MatchServeOptions, type MatchTransport, type MessageMeta, type MomentKind, type MomentResult, type MomentSignal, type MomentsApi, type MultiTransportSession, PLATFORM_EVENTS, type PeriodOptions, PlatformApi, PlatformCurrenciesApi, type PlatformCurrency, type PlatformCurrencyAccess, type PlatformCurrencyPurchasePayment, type PlatformCurrencyPurchaseRequest, type PlatformCurrencyPurchaseResult, type PlatformCurrencyReadOptions, PlatformEarnAllowanceApi, type PlatformEventName, type PlayerBadge, type PlayerBoost, type PlayerBoostMultiplier, type PlayerIdentity, type PlayerProfile, PlayerText, type ProfileCallOptions, type ProfileShowcaseEntry, ProfilesApi, type PurchaseApi, type PurchaseOptions, type PurchaseOutcome, PurchaseRefusal, type PurchaseRefusalCode, type PurchaseRequest, type PutBytesOptions, type PutSaveOptions, type RankedRow, type RecipeAvailability, type RecipeLock, type RewardCue, type RewardedStatus, type SavePage, type SaveRecord, type SendOptions$1 as SendOptions, type ServerTokenGrant, type SessionEndReason, type ShareApi, type ShareRequest, ShowcaseApi, type ShowcaseCallOptions, type ShowcaseFriendEntry, type ShowcaseFriends, type ShowcaseValue, type SignInOutcome, type SignInOutcomeKind, type SnapshotCurrency, type SnapshotItem, type TransportAdvert, type TransportKind, TransportRefusal, UNRELIABLE_CHANNEL_LABEL, type VibrationApi, type WalletVisibleOptions, type WornAvatar3d, asPlayerText, createBootTimeline, createGem, leaveAndExit, mapIdentity, markOnResolve, platformIconUrl, presentJoinCredential, readTemplateTransports, readTransportAdvert, reportOnResolve, text, webTransportAvailable };
3295
+ export { ARCADE_EMBEDDERS, AVATAR_RIGS, type AccountApi, type AdFormat, type AdOutcome, type AdOutcomeKind, type AdRequest, type AdsApi, AnalyticsApi, type AnalyticsFlushOptions, type AnalyticsFlushResult, type AnalyticsOptions, type AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Avatar3dFamily, type Avatar3dOffer, type Avatar3dPrice, type Avatar3dPurchaseRequest, type AvatarRigNode, type AvatarRigs, type Avatars3dApi, type BootMark, type BootTimeline, type Challenge, type ChallengeWindow, ChallengesApi, type ConfiguredPurchasePayment, type ConfiguredPurchaseRequest, type ConfiguredPurchaseResult, type ControlsApi, DEVICE_SETTINGS_MAX_BYTES, DedicatedServerError, type DedicatedServerFailure, type DedicatedServerSession, type DevToolsOpener, type DeviceSettings, type DeviceSettingsApi, type DeviceSettingsPutOutcome, type DirectPurchaseRequest, type DirectPurchaseResult, type EarnAllowance, type EarnAllowanceEvent, type EarnAllowancePeriod, type EarnAllowanceReadOptions, type EntityLeaderboard, type EntityLeaderboardEntry, type EntityLeaderboardOptions, type EntityType, type ExitAnalytics, type ExitBridge, type ExitRooms, type FrameState, type Friend, type FriendChallenge, FriendChallengeError, type FriendChallengeErrorCode, type FriendChallengeOutcome, type FriendChallengeOutcomeKind, type FriendChallengeRequest, type FriendChallengesPage, FriendsApi, type FriendsPage, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameConfig, type GameMenu, type GameMenuItem, type GameServerLink, type GameSettingsApi, type Gem, GemApiError, GemClient, type GemCohort, type GemCohorts, GemConfigError, type GemControlBox, type GemCorner, type GemDevTools, GemFrame, GemFunctionError, type GemGameChoiceSetting, type GemGameSetting, type GemGameSettingBase, type GemGameSettingChange, type GemGameSettingIcon, type GemGameSettingOption, type GemGameSettingValue, type GemGameSliderSetting, type GemGameToggleSetting, type GemGraphicsQuality, type GemIdentity, type GemInsets, type GemLook, type GemLookBase, type GemLookDelta, type GemLookOptions, type GemLookSpeed, type GemMatchApi, type GemMatchServerModule, type GemOptions, type GemPlayer, type GemPrefs, type GemRecommendedSettings, type GemReservedArea, GemSaveError, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerLifecycle, type GemTerminationNotice, GemUnframedError, type GemViewport, type GetLeaderboardOptions, type JoinWelcome, type Leaderboard, type LeaderboardEntry, type LeaderboardPeriod, LeaderboardsApi, type ListChallengesOptions, type ListFriendChallengesOptions, type ListFriendsOptions, MAX_PERIODS_BACK, type MatchConnectOptions, type MatchConnection, type MatchHosting, type MatchHostingMode, type MatchServeOptions, type MatchTransport, type MessageMeta, type MomentKind, type MomentResult, type MomentSignal, type MomentsApi, type MultiTransportSession, PLATFORM_EVENTS, type PeriodOptions, PlatformApi, PlatformCurrenciesApi, type PlatformCurrency, type PlatformCurrencyAccess, type PlatformCurrencyPurchasePayment, type PlatformCurrencyPurchaseRequest, type PlatformCurrencyPurchaseResult, type PlatformCurrencyReadOptions, PlatformEarnAllowanceApi, type PlatformEventName, type PlayerBadge, type PlayerBoost, type PlayerBoostMultiplier, type PlayerIdentity, type PlayerProfile, PlayerText, type ProfileCallOptions, type ProfileShowcaseEntry, ProfilesApi, type PurchaseApi, type PurchaseOptions, type PurchaseOutcome, PurchaseRefusal, type PurchaseRefusalCode, type PurchaseRequest, type PutBytesOptions, type PutSaveOptions, type RankedRow, type RecipeAvailability, type RecipeLock, type RewardCue, type RewardedStatus, type SavePage, type SaveRecord, type SendOptions$1 as SendOptions, type ServerTokenGrant, type SessionEndReason, type ShareApi, type ShareRequest, ShowcaseApi, type ShowcaseCallOptions, type ShowcaseFriendEntry, type ShowcaseFriends, type ShowcaseValue, type SignInOutcome, type SignInOutcomeKind, type SnapshotCurrency, type SnapshotItem, type TransportAdvert, type TransportKind, TransportRefusal, UNRELIABLE_CHANNEL_LABEL, type VibrationApi, type WalletVisibleOptions, type WornAvatar3d, asPlayerText, createBootTimeline, createGem, leaveAndExit, mapIdentity, markOnResolve, platformIconUrl, presentJoinCredential, readTemplateTransports, readTransportAdvert, reportOnResolve, text, webTransportAvailable };
@@ -1,2 +1,2 @@
1
- var GemWebSdkBridgeBundle=(function(){'use strict';var E="GemWebSdkHandshake";function te(){return typeof document>"u"?void 0:document.readyState}var j=()=>Date.now();function D(t={}){let e=t.win??window,n=e,r=n[E];if(r!==void 0)return r;let s=t.readyState??te,i=t.now??j,d=[],a=[],m=p=>{let c=p.data;!c||c.type!=="gem.handshake"||(d.length>=4&&(d.shift(),a.shift(),g.dropped+=1),d.push(p),a.push(i()));};e.addEventListener("message",m);let g={events:d,times:a,dropped:0,late:s()==="complete",uninstall(){e.removeEventListener("message",m);}};return n[E]=g,g}function F(t,e,n={}){let r=t[E];if(r===void 0)return null;let s=n.ttlMs??9500,i=n.now??j,d=r.events.splice(0),a=r.times?.splice(0),m=0,g=0,p=false,c=i()-s;for(let y=d.length-1;y>=0;y--){let f=a?.[y];if(f!==void 0&&f<c){g+=1;continue}if(m+=1,e(d[y])===true){p=true;break}}return r.uninstall(),{capture:r,delivered:m,stale:g,accepted:p}}var l="gem-web-bridge",k={min:1,max:1},h=65536,$=8,w=5e3,U=1024;var v=Object.create(null),R="__malformed";var q=Object.create(null),re=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"]]);function H(t){let e=q[t];if(e!==void 0)return e;for(let[n,r]of re)if(t.startsWith(n))return r}function oe(t){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(t);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function ae(t){let e;try{e=new URL(t);}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 se(t){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(t)}function ie(t){return /^[a-z_]+$/.test(t)}function I(t,e,n){if(e>$)return `nesting deeper than ${$}`;if(++n.nodes>w)return `more than ${w} values`;if(n.bytes>h)return `over the ${h} byte limit`;if(t==null)return n.bytes+=4,null;let r=typeof t;if(r==="boolean")return n.bytes+=5,null;if(r==="number")return n.bytes+=8,null;if(r==="string")return n.bytes+=t.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let s=t;if(n.seen.has(s))return "a repeated or cyclic object reference";if(n.seen.add(s),Array.isArray(t)){if(t.length>U)return `an array of ${t.length} entries, over the ${U} limit`;n.bytes+=2;for(let a of t){let m=I(a,e+1,n);if(m)return m}return null}let i=Object.getPrototypeOf(t);if(i!==Object.prototype&&i!==null)return "a value that is not a plain object";n.bytes+=2;let d=t;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++n.nodes>w)return `more than ${w} values`;if(n.bytes+=a.length*3+4,n.bytes>h)return `over the ${h} byte limit`;let m=I(d[a],e+1,n);if(m)return m}return null}function le(t){let e={nodes:0,bytes:0,seen:new Set},n=I(t,0,e);return n?`message contains ${n}`:e.bytes>h?`message is over the ${h} byte limit`:null}var de={origin:[oe,"not a well-formed origin"],url:[ae,"not a safe absolute http(s) URL"],bcp47:[se,"not a well-formed BCP 47 tag"],code:[ie,"not a code of lower-case letters and underscores"]};function W(t,e,n){for(let r of e){let s=n===""?r.n:`${n}.${r.n}`,i=t[r.n];if(i==null){if(r.o)continue;return `${s}: required`}let d=B(i,r,s);if(d)return d}return null}function B(t,e,n){switch(e.k){case "s":{if(typeof t!="string")return `${n}: expected string`;if(t.length>e.max)return `${n}: exceeds ${e.max} characters`;if(e.f){let[r,s]=de[e.f];if(!r(t))return `${n}: ${s}`}return null}case "i":return typeof t!="number"||!Number.isInteger(t)?`${n}: expected integer`:t<e.lo||t>e.hi?`${n}: out of range ${e.lo}..${e.hi}`:null;case "b":return typeof t=="boolean"?null:`${n}: expected boolean`;case "e":{let r=e.v;return r.includes(t)?null:`${n}: not one of ${r.join("|")}`}case "o":return typeof t!="object"||t===null||Array.isArray(t)?`${n}: expected object`:W(t,e.fs,n);case "j":return typeof t!="object"||t===null||Array.isArray(t)?`${n}: expected a plain object`:null;case "a":{if(!Array.isArray(t))return `${n}: expected array`;if(t.length>e.max)return `${n}: exceeds ${e.max} items`;let r=e.it,s=`${n}[]`;for(let i of t){if(i==null){if(r.o)continue;return `${s}: required`}let d=B(i,r,s);if(d)return d}return null}default:{let r=e.k;return `${n}: unknown kind ${String(r)}`}}}var N=Object.create(null);function o(t,e,n,r){return v[t]=n,r&&(q[t]=r),N[t]=s=>{let i=le(s);if(i)return i;if(typeof s!="object"||s===null||Array.isArray(s))return "message: expected object";let d=s;return d.gem!==l?"gem: wrong protocol marker":d.type!==t?`type: expected ${t}`:W(d,e,"")}}o("gem.handshake",[{n:"versions",k:"o",fs:[{n:"min",k:"i",lo:1,hi:1e3},{n:"max",k:"i",lo:1,hi:1e3}]},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}},{n:"embedder",k:"s",max:2048,f:"origin"},{n:"unbidden",k:"b",o:1}],30);o("gem.handshake.accept",[{n:"v",k:"i",lo:1,hi:1e3},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}}],5);o("gem.token",[{n:"token",k:"s",max:4096},{n:"requestId",k:"s",o:1,max:64},{n:"generation",k:"i",lo:0,hi:2147483647},{n:"apiBaseUrl",k:"s",o:1,max:512,f:"url"},{n:"playerId",k:"s",max:64},{n:"gameId",k:"s",max:64},{n:"channel",k:"e",v:["dev","test","release"]},{n:"gameVersion",k:"s",o:1,max:64},{n:"expiresAt",k:"i",lo:0,hi:4102444800},{n:"chainEndsAt",k:"i",lo:0,hi:4102444800},{n:"allowDevTools",k:"b",o:1}],30);o("gem.token.denied",[{n:"requestId",k:"s",max:64},{n:"retryable",k:"b"},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:36e5},{n:"reason",k:"e",v:["chain_exhausted","unavailable"]},{n:"correlationId",k:"s",max:64}],30);o("gem.player",[{n:"untrusted",k:"o",fs:[{n:"displayName",k:"s",max:64},{n:"displayTag",k:"s",o:1,max:80},{n:"displayHandle",k:"s",max:80}]},{n:"playerId",k:"s",o:1,max:64},{n:"discriminator",k:"s",o:1,max:8},{n:"avatarIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatarIconId",k:"s",o:1,max:64},{n:"avatar3dId",k:"s",o:1,max:64},{n:"avatar3dModelUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatar3dIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatars3d",k:"a",o:1,max:8,it:{n:"",k:"o",fs:[{n:"family",k:"s",max:32},{n:"id",k:"s",max:64},{n:"modelUrl",k:"s",max:256,f:"url"},{n:"iconUrl",k:"s",max:256,f:"url"}]}},{n:"badges",k:"a",o:1,max:2,it:{n:"",k:"o",fs:[{n:"itemId",k:"s",max:64},{n:"iconUrl",k:"s",max:2048,f:"url"}]}},{n:"displayTag",k:"s",max:8},{n:"avatarUrl",k:"s",o:1,max:2048,f:"url"},{n:"boost",k:"o",o:1,fs:[{n:"multipliers",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"currencyId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"multiplierBp",k:"i",lo:1e4,hi:1e6}]}},{n:"expiresInDays",k:"i",o:1,lo:1,hi:7}]}],12);o("gem.pause",[{n:"paused",k:"b"}],120);o("gem.prefs",[{n:"reducedMotion",k:"b"},{n:"muted",k:"b"},{n:"showFrameStats",k:"b",o:1},{n:"locale",k:"s",max:35,f:"bcp47"},{n:"recommended",k:"o",o:1,fs:[{n:"volume",k:"i",lo:0,hi:100},{n:"batterySaver",k:"b"},{n:"keyboardLayout",k:"e",v:["wasd","arrows"]},{n:"gamepadConfirm",k:"e",v:["south","east"]},{n:"vibration",k:"b"},{n:"lookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"gamepadLookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"invertLookY",k:"b",o:1},{n:"touchMovement",k:"s",o:1,max:16,f:"code"},{n:"graphicsQuality",k:"s",o:1,max:16,f:"code"},{n:"graphicsQualityAuto",k:"b",o:1}]}],60);o("gem.cohorts",[{n:"cohorts",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"experimentId",k:"s",max:36},{n:"variantKey",k:"s",max:32}]}}],12,"cohorts");o("gem.earnAllowanceChanged",[],30,"earnAllowance");o("gem.viewport",[{n:"insets",k:"o",fs:[{n:"top",k:"i",lo:0,hi:4096},{n:"right",k:"i",lo:0,hi:4096},{n:"bottom",k:"i",lo:0,hi:4096},{n:"left",k:"i",lo:0,hi:4096}]},{n:"reserved",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384},{n:"control",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384}]}]}}],60,"viewport");o("gem.deviceSettings",[{n:"settings",k:"j",o:1}],12,"deviceSettings");o("gem.willTerminate",[{n:"reason",k:"e",v:["grant_expiring","session_ended","navigating","kill_switch"]},{n:"graceMs",k:"i",lo:0,hi:6e4}],10);o("gem.roomEvent",[{n:"event",k:"e",v:["updated","match_started","match_ready","match_failed","version_stale"]},{n:"roomId",k:"s",max:64},{n:"matchId",k:"s",o:1,max:64},{n:"version",k:"i",o:1,lo:0,hi:2147483647},{n:"reason",k:"s",o:1,max:64},{n:"platform",k:"e",o:1,v:["universal","mobile"]}],120,"rooms");o("gem.socketState",[{n:"connected",k:"b"},{n:"reason",k:"e",o:1,v:["connecting","dropped","watchdog","terminal"]}],12,"socketState");o("gem.seatedEvent",[{n:"roomId",k:"s",max:64}],60,"partySeating");o("gem.autoStartFailedEvent",[{n:"roomId",k:"s",max:64},{n:"code",k:"s",max:64}],30,"autoStartFailure");o("gem.ticketEvent",[{n:"event",k:"e",v:["queued","matched","expired","cancelled","failed"]},{n:"ticketId",k:"s",max:64},{n:"partySize",k:"i",o:1,lo:1,hi:64},{n:"queuedAt",k:"i",o:1,lo:0,hi:4102444800},{n:"expiresAt",k:"i",o:1,lo:0,hi:4102444800},{n:"reason",k:"s",o:1,max:64},{n:"roomId",k:"s",o:1,max:64}],60,"ticketEvents");o("gem.signalEvent",[{n:"event",k:"e",v:["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1800,"p2p");o("gem.dedicatedEvent",[{n:"event",k:"e",v:["answer","error","matchEnded"]},{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",o:1,max:10240},{n:"sessionId",k:"s",o:1,max:64},{n:"code",k:"s",o:1,max:64},{n:"message",k:"s",o:1,max:256},{n:"reason",k:"s",o:1,max:64}],120,"dedicated");o("gem.partyResult",[{n:"requestId",k:"s",max:64},{n:"partyId",k:"s",o:1,max:64}],30,"parties");o("gem.clusterTarget",[{n:"clusterTargetId",k:"s",max:64},{n:"clusterTargetIds",k:"a",o:1,max:3,it:{n:"",k:"s",max:64}}],12,"clusterTarget");o("gem.devtools",[{n:"action",k:"e",v:["open","close","toggle"]}],30,"devtools");o("gem.screenshot",[{n:"maxWidth",k:"i",lo:1,hi:7680},{n:"maxHeight",k:"i",lo:1,hi:4320}],6,"screenshot");o("gem.screenshot.denied",[{n:"reason",k:"e",v:["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]},{n:"retryable",k:"b"}],12,"screenshot");o("gem.requestContext",[],12,"feedbackContext");o("gem.gameContext",[{n:"context",k:"j"}],12,"feedbackContext");o("gem.signal",[{n:"signal",k:"e",v:["join","offer","answer","ice","migrationAck","hostUnreachable"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1200,"p2p");o("gem.dedicatedOffer",[{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",max:10240},{n:"intent",k:"e",v:["initial","ice_restart","full_reconnect"]}],60,"dedicated");o("gem.partyRequest",[{n:"requestId",k:"s",max:64}],12,"parties");o("gem.ready",[],5);o("gem.engaged",[],5,"engagement");o("gem.progress",[{n:"loaded",k:"i",lo:0,hi:1099511627776},{n:"total",k:"i",lo:0,hi:1099511627776}],600);o("gem.requestToken",[{n:"requestId",k:"s",max:64},{n:"reason",k:"e",v:["initial","expiring","rejected"]}],12);o("gem.requestLocalServer",[{n:"requestId",k:"s",max:64},{n:"gameRoomId",k:"s",max:64}],6,"localHost");o("gem.localServer",[{n:"requestId",k:"s",max:64},{n:"serverId",k:"s",o:1,max:128}],30,"localHost");o("gem.localServer.denied",[{n:"requestId",k:"s",max:64},{n:"retryable",k:"b"},{n:"reason",k:"e",v:["refused","unavailable"]}],30,"localHost");o("gem.exit",[{n:"reason",k:"s",o:1,max:64}],5);o("gem.openExternal",[{n:"url",k:"s",max:2048,f:"url"}],6);o("gem.requestFullscreen",[{n:"enabled",k:"b"}],30);o("gem.audioState",[{n:"muted",k:"b"}],120,"audioState");o("gem.rewardCue",[{n:"currencySlug",k:"s",max:32},{n:"expected",k:"i",o:1,lo:1,hi:1e9},{n:"origin",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384}]}],60,"rewardCue");o("gem.walletDisplay",[{n:"visible",k:"b"},{n:"currencies",k:"a",o:1,max:8,it:{n:"",k:"s",max:64}},{n:"score",k:"b",o:1}],60,"walletDisplay");o("gem.controlsDeclare",[{n:"uses",k:"o",fs:[{n:"lookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"gamepadLookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"invertLookY",k:"s",o:1,max:16,f:"code"},{n:"touchMovement",k:"s",o:1,max:16,f:"code"}]}],60,"controls");o("gem.menu",[{n:"settings",k:"b"},{n:"items",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:32},{n:"label",k:"s",max:40}]}}],60,"gameMenu");o("gem.menuSelect",[{n:"entry",k:"e",v:["settings","item"]},{n:"id",k:"s",o:1,max:32}],60,"gameMenu");o("gem.gemCorner",[{n:"corner",k:"e",v:["top-left","top-right","bottom-left","bottom-right"]}],12,"gemCorner");o("gem.requestShare",[{n:"params",k:"a",max:16,it:{n:"",k:"o",fs:[{n:"key",k:"s",max:64},{n:"value",k:"s",max:512}]}}],6,"share");o("gem.requestAvatarPicker",[{n:"tab",k:"e",o:1,v:["2d","3d"]}],6,"avatarPicker");o("gem.vibrate",[{n:"pattern",k:"a",max:99,it:{n:"",k:"i",lo:0,hi:1e4}}],600,"vibration");o("gem.frameStats",[{n:"fps",k:"i",lo:0,hi:1e3},{n:"frameTimeP95Ms",k:"i",lo:0,hi:1e5},{n:"counts",k:"e",o:1,v:["pageFrames","gameDraws"]}],120,"frameStats");o("gem.portraitBorders",[{n:"name",k:"s",o:1,max:40}],60,"portraitBorders");o("gem.error",[{n:"code",k:"e",v:["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]},{n:"message",k:"s",o:1,max:256}],60);o("gem.requestPurchase",[{n:"requestId",k:"s",max:64},{n:"purchaseKind",k:"e",v:["item","recipe"]},{n:"targetId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"quantity",k:"i",o:1,lo:1,hi:10},{n:"catalogId",k:"s",o:1,max:64}],12,"purchases");o("gem.requestAvatar3dPurchase",[{n:"requestId",k:"s",max:64},{n:"itemId",k:"s",max:64},{n:"currencySlug",k:"s",max:32}],12,"avatar3dPurchases");o("gem.purchaseResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["ok","declined","unavailable"]},{n:"reason",k:"s",o:1,max:64,f:"code"},{n:"purchaseId",k:"s",o:1,max:64}],30,"purchases");o("gem.deviceSettings.put",[{n:"requestId",k:"s",max:64},{n:"settings",k:"j"}],30,"deviceSettings");o("gem.deviceSettings.result",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["stored","too_large","unavailable"]}],30,"deviceSettings");o("gem.requestAd",[{n:"requestId",k:"s",max:64},{n:"placement",k:"s",max:32},{n:"format",k:"e",v:["interstitial","rewarded"]}],6,"ads");o("gem.adResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["shown","no_ad","unavailable"]}],12,"ads");o("gem.signalMoment",[{n:"requestId",k:"s",max:64},{n:"kind",k:"e",v:["level_end","run_end","match_end","menu_return","other_break"]},{n:"placement",k:"s",o:1,max:32}],6,"moments");o("gem.momentResult",[{n:"requestId",k:"s",max:64},{n:"shown",k:"b"}],12,"moments");o("gem.requestSignIn",[{n:"requestId",k:"s",max:64}],6,"signIn");o("gem.signInResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["signed_in","declined","unavailable"]}],12,"signIn");o("gem.requestFriendChallenge",[{n:"requestId",k:"s",max:64},{n:"playerId",k:"s",max:64},{n:"template",k:"s",max:64},{n:"params",k:"j",o:1}],12,"friendChallenges");o("gem.friendChallengeResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["accepted","cancelled","refused","unavailable","rate_limited","invalid"]},{n:"code",k:"s",o:1,max:64},{n:"param",k:"s",o:1,max:64},{n:"channel",k:"s",o:1,max:16},{n:"configVersion",k:"i",o:1,lo:0,hi:9007199254740991},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:864e5},{n:"detail",k:"s",o:1,max:300}],30,"friendChallenges");var S=Object.freeze(N);v.__malformed=60;Object.freeze(v);Object.freeze(q);var me=/^[a-z0-9][a-z0-9_-]{0,39}$/;function ce(t){return typeof t=="string"&&me.test(t)}function V(t){if(t===null||ce(t))return t;let e=typeof t=="string"?JSON.stringify(t.length>48?`${t.slice(0,48)}\u2026`:t):`a value of type ${typeof t}`;console.warn(`[gem] showPortraitBorders: ${e} is not a border name, so nothing was sent. A name is 1 to 40 of a-z, 0-9, _ and -, starting with a letter or digit, exactly as your store page declares it under media.portrait_borders. Pass null for no art.`);}var L={now:()=>Date.now(),setTimeout:(t,e)=>setTimeout(t,e),clearTimeout:t=>clearTimeout(t)},x=class{constructor(e=L){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let n=v[e];if(n===void 0)return false;let r=this.clock.now(),s=this.lastRefill.get(e)??r,i=this.tokens.get(e)??n,d=Math.max(0,r-s),a=Math.min(n,i+d*n/6e4);return a<1?(this.tokens.set(e,a),this.lastRefill.set(e,r),false):(this.tokens.set(e,a-1),this.lastRefill.set(e,r),true)}};function A(t,e){return t.toLowerCase()===e.toLowerCase()}function G(t){let e=t.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function z(t,e){if(typeof e!="string"||e==="")return false;let n,r;try{n=new URL(t),r=new URL(e);}catch{return false}return n.protocol!=="http:"&&n.protocol!=="https:"||r.protocol!=="http:"&&r.protocol!=="https:"||n.username!==""||n.password!==""||n.pathname!=="/"||n.search!==""||n.hash!==""||n.port===""?false:G(n.hostname)&&G(r.hostname)}function Y(t,e){if(t.min>t.max||e.min>e.max)return null;let n=Math.min(t.max,e.max),r=Math.max(t.min,e.min);return n>=r?n:null}var Q="[gem: use gem.text.toPlain()]",K=new Set,J;function ue(t){try{if(K.has(t))return;let e=globalThis.location;if(J??=!!e?.port&&G(e.hostname??""),!J)return;K.add(t),console.warn(`[gem] ${t} is PlayerText, turned into a string: players see ${Q}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
2
- ${new Error().stack}`);}catch{}}var M=class{#e;#n;constructor(e,n="player text"){this.#e=e,this.#n=n,Object.freeze(this);}toString(){return ue(this.#n),Q}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function _(t,e){return new M(t,e)}function X(t){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(t)?t:0)))}function ge(t,e){if(t!==true)return {visible:false};let n=[],r=false;for(let s of Array.isArray(e)?e:[])s==="score"?r=true:typeof s=="string"&&s.length>0&&s.length<=64&&!n.includes(s)&&n.push(s);return !r&&n.length===0?{visible:true}:{visible:true,currencies:n.slice(0,8),score:r}}function pe(t){let e=[];for(let n of Array.isArray(t)?t:[]){if(e.length===4)break;if(typeof n!="object"||n===null)continue;let{id:r,label:s}=n;typeof r!="string"||r.length===0||r.length>32||typeof s!="string"||s.trim().length===0||e.some(i=>i.id===r)||e.push({id:r,label:s.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function Z(t,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(t)?t:0)))}function ye(t){let e=t.untrusted,n=_(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,s=r?_(r,"gem.player.displayTag"):void 0,i=t.discriminator||t.displayTag,d=i&&/^[0-9]{1,8}$/.test(i)?i:void 0,a=t.avatarIconUrl||t.avatarUrl,m=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,g=t.avatarIconId,p=t.avatar3dId,c={displayName:n,displayHandle:s??n,badges:(t.badges??[]).map(u=>({itemId:u.itemId,iconUrl:u.iconUrl}))};t.playerId&&(c.playerId=t.playerId),d&&(c.discriminator=d),s&&(c.displayTag=s),a&&(c.avatarIconUrl=c.avatarUrl=a),g&&m.test(g)&&(c.avatarIconId=g),p&&m.test(p)&&(c.avatar3dId=p),t.avatar3dModelUrl&&(c.avatar3dModelUrl=t.avatar3dModelUrl),t.avatar3dIconUrl&&(c.avatar3dIconUrl=t.avatar3dIconUrl),t.avatars3d&&(c.avatars3d=t.avatars3d.filter(u=>m.test(u.id)&&/^[a-z][a-z0-9_]*$/.test(u.family)).filter((u,C,P)=>P.findIndex(T=>T.family==u.family)==C).map(({family:u,id:C,modelUrl:P,iconUrl:T})=>({family:u,id:C,modelUrl:P,iconUrl:T})));let y=t.boost,f=(y?.multipliers??[]).map(u=>({currencyId:u.currencyId,currencySlug:u.currencySlug,multiplier:u.multiplierBp/1e4})),O=f.some(u=>u.multiplier>1);return c.boost=O&&y?.expiresInDays?{active:O,multipliers:f,expiresInDays:y.expiresInDays}:{active:O,multipliers:f},c}var fe=1e4,b=class{constructor(e){this.options=e;if(e.expectedSource===void 0||e.expectedSource===null)throw new Error("GemFrame: expectedSource is required and must not be null \u2014 pass window.parent. The handshake transfers the MessagePort, and origin alone does not say WHICH document on that origin sent it.");this.expectedSource=e.expectedSource,this.clock=e.clock??L,this.inbound=new x(this.clock),this.outbound=new x(this.clock);}options;state="waiting";port=null;version=null;peerCapabilities=[];unbidden=false;engagedSent=false;readyPending=false;readySent=false;settleWaiters=[];clock;inbound;outbound;expectedSource;get currentState(){return this.state}get parentCapabilities(){return this.peerCapabilities}parentSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(e){if(e.source!==this.expectedSource)return this.refuse(e,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>A(a,e.origin))||z(e.origin,this.options.selfOrigin)))return this.refuse(e,`embedder '${e.origin}' is not in allowedEmbedders`),this.state==="waiting"&&this.transition("refused"),false;if(this.state==="connected")return this.fail("handshake_duplicate","this document already has a port"),false;if(this.state!=="waiting")return false;let r=S["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let s=e.data;if(!A(s.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${s.embedder}' but arrived from '${e.origin}'`),false;let i=e.ports?.[0];if(!i)return this.refuse(e,"handshake transferred no MessagePort"),false;let d=Y(k,s.versions);return d===null?(i.onmessage=null,i.postMessage({gem:l,type:"gem.error",code:"version_mismatch",message:`frame supports v${k.min}..v${k.max}`}),i.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=i,this.version=d,i.onmessage=a=>this.receive(a.data,a.ports??[]),i.start?.(),this.peerCapabilities=s.capabilities??[],this.unbidden=s.unbidden===true,i.postMessage({gem:l,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:l,type:"gem.ready"});}settled(e=fe){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(n=>{let r=i=>{this.clock.clearTimeout(s),n(i);},s=this.clock.setTimeout(()=>{let i=this.settleWaiters.indexOf(r);i>=0&&this.settleWaiters.splice(i,1),n(this.state);},e);this.settleWaiters.push(r);})}progress(e,n){this.send({gem:l,type:"gem.progress",loaded:e,total:n});}requestToken(e,n){this.send({gem:l,type:"gem.requestToken",requestId:e,reason:n});}requestLocalServer(e,n){return this.parentSupports("localHost")?(this.send({gem:l,type:"gem.requestLocalServer",requestId:e,gameRoomId:n}),true):false}exit(e){this.send(e===void 0?{gem:l,type:"gem.exit"}:{gem:l,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:l,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:l,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:l,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:l,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let n={gem:l,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return e.expected!==void 0&&Number.isFinite(e.expected)&&e.expected>=1&&(n.expected=Math.min(1e9,Math.round(e.expected))),e.origin&&(n.origin={x:X(e.origin.x),y:X(e.origin.y)}),this.send(n),true}showPortraitBorders(e){let n=V(e);return n===void 0||!this.gated("portraitBorders")?false:this.send(n===null?{gem:l,type:"gem.portraitBorders"}:{gem:l,type:"gem.portraitBorders",name:n})}setWalletVisible(e,n){return this.gated("walletDisplay")?this.send({gem:l,type:"gem.walletDisplay",...ge(e,n?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:l,type:"gem.menu",...pe(e)}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:l,type:"gem.gemCorner",corner:e}):false}requestShare(e){return this.gated("share")?this.send({gem:l,type:"gem.requestShare",params:e.map(({key:n,value:r})=>({key:n,value:r}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:l,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:l,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:l,type:"gem.frameStats",fps:Z(e.fps,1e3),frameTimeP95Ms:Z(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:l,type:"gem.gameContext",context:e}),true)}signal(e,n,r){this.send({gem:l,type:"gem.signal",signal:e,matchId:n,data:r});}dedicatedOffer(e,n,r){this.send({gem:l,type:"gem.dedicatedOffer",matchId:e,sdp:n,intent:r});}requestParty(e){return this.send({gem:l,type:"gem.partyRequest",requestId:e})}requestPurchase(e,n,r,s,i,d){return this.send({gem:l,type:"gem.requestPurchase",requestId:e,purchaseKind:n,targetId:r,currencySlug:s,...i===void 0?{}:{quantity:i},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(e,n,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:l,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:n,currencySlug:r}):false}putDeviceSettings(e,n){return this.send({gem:l,type:"gem.deviceSettings.put",requestId:e,settings:n})}requestAd(e,n,r){return this.send({gem:l,type:"gem.requestAd",requestId:e,placement:n,format:r})}signalMoment(e,n,r){return this.send({gem:l,type:"gem.signalMoment",requestId:e,kind:n,...r===void 0?{}:{placement:r}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:l,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,n,r,s){return this.send({gem:l,type:"gem.requestFriendChallenge",requestId:e,playerId:n,template:r,...s===void 0?{}:{params:s}})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(e){if(this.state!==e){this.state=e;for(let n of this.settleWaiters.splice(0))n(e);this.options.onStateChange?.(e);}}refuseMalformed(e,n){if(!this.inbound.allow(R)){this.options.onDropped?.(e,n);return}this.fail(e,n);}dropUnknownType(e){this.inbound.allow(R)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,n){e.ports?.[0]?.close?.(),this.inbound.allow(R)&&this.options.onRefused?.(n);}fail(e,n){this.options.onError?.(e,n),this.state==="connected"&&this.port&&this.send({gem:l,type:"gem.error",code:e,...n===void 0?{}:{message:n}});}gated(e){return this.state==="connected"&&this.parentSupports(e)}send(e){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let n=e.type;if(!this.outbound.allow(n))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${n}`),false;let r=S[n]?.(e);return r?(this.options.onError?.("internal",`refusing to send malformed ${n}: ${r}`),false):(this.port.postMessage(this.version===null?e:{...e,v:this.version}),true)}receive(e,n=[]){let r=false;try{r=this.dispatchInbound(e,n);}finally{r||ee(n);}}dispatchInbound(e,n){if(this.state!=="connected")return false;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let s=S[r];if(!s)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let i=H(r);if(i!==void 0&&!this.options.capabilities?.includes(i))return this.fail("unsupported_capability",`${r} requires the '${i}' capability, which this frame did not advertise`),false;let d=s(e);if(d)return this.fail("invalid_message",`${r}: ${d}`),false;let a=e;if(a.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let m=n[0];if(!m){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let g={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){m.close(),this.screenshotDenied("not_wired",false);break}return ee(n.slice(1)),this.options.onScreenshot(g,m,(p,c)=>this.screenshotDenied(p,c)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(ye(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,n){this.send({gem:l,type:"gem.screenshot.denied",reason:e,retryable:n});}};function ee(t){for(let e of t)try{e?.close?.();}catch{}}D();function he(t){if((t.expectedSource??window.parent)===window)throw new Error("GemWebSdkBridge.connect: this document is not framed (window.parent === window), so there is no embedder to hand it a port. A game runs inside the Gem Arcade iframe; opening it directly will never complete a handshake.");let e=new b({...t,expectedSource:t.expectedSource??window.parent}),n=r=>{let s=e.handleWindowMessage({data:r.data,origin:r.origin,source:r.source,ports:r.ports});return s&&window.removeEventListener("message",n),s};return window.addEventListener("message",n),F(window,n),e}var ne={connect:he,GemFrame:b,PROTOCOL:l,SUPPORTED_VERSIONS:k};window.GemWebSdkBridge=ne;var Nn=ne;return Nn;})();
1
+ var GemWebSdkBridgeBundle=(function(){'use strict';var I="GemWebSdkHandshake";function re(){return typeof document>"u"?void 0:document.readyState}var F=()=>Date.now();function $(t={}){let e=t.win??window,n=e,r=n[I];if(r!==void 0)return r;let s=t.readyState??re,i=t.now??F,d=[],a=[],c=y=>{let u=y.data;!u||u.type!=="gem.handshake"||(d.length>=4&&(d.shift(),a.shift(),p.dropped+=1),d.push(y),a.push(i()));};e.addEventListener("message",c);let p={events:d,times:a,dropped:0,late:s()==="complete",uninstall(){e.removeEventListener("message",c);}};return n[I]=p,p}function U(t,e,n={}){let r=t[I];if(r===void 0)return null;let s=n.ttlMs??9500,i=n.now??F,d=r.events.splice(0),a=r.times?.splice(0),c=0,p=0,y=false,u=i()-s;for(let f=d.length-1;f>=0;f--){let h=a?.[f];if(h!==void 0&&h<u){p+=1;continue}if(c+=1,e(d[f])===true){y=true;break}}return r.uninstall(),{capture:r,delivered:c,stale:p,accepted:y}}var l="gem-web-bridge",v={min:1,max:1},k=65536,H=8,R=5e3,W=1024;var b=Object.create(null),S="__malformed";var q=Object.create(null),oe=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"]]);function B(t){let e=q[t];if(e!==void 0)return e;for(let[n,r]of oe)if(t.startsWith(n))return r}function ae(t){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(t);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function se(t){let e;try{e=new URL(t);}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 ie(t){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(t)}function le(t){return /^[a-z_]+$/.test(t)}function L(t,e,n){if(e>H)return `nesting deeper than ${H}`;if(++n.nodes>R)return `more than ${R} values`;if(n.bytes>k)return `over the ${k} byte limit`;if(t==null)return n.bytes+=4,null;let r=typeof t;if(r==="boolean")return n.bytes+=5,null;if(r==="number")return n.bytes+=8,null;if(r==="string")return n.bytes+=t.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let s=t;if(n.seen.has(s))return "a repeated or cyclic object reference";if(n.seen.add(s),Array.isArray(t)){if(t.length>W)return `an array of ${t.length} entries, over the ${W} limit`;n.bytes+=2;for(let a of t){let c=L(a,e+1,n);if(c)return c}return null}let i=Object.getPrototypeOf(t);if(i!==Object.prototype&&i!==null)return "a value that is not a plain object";n.bytes+=2;let d=t;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++n.nodes>R)return `more than ${R} values`;if(n.bytes+=a.length*3+4,n.bytes>k)return `over the ${k} byte limit`;let c=L(d[a],e+1,n);if(c)return c}return null}function de(t){let e={nodes:0,bytes:0,seen:new Set},n=L(t,0,e);return n?`message contains ${n}`:e.bytes>k?`message is over the ${k} byte limit`:null}var me={origin:[ae,"not a well-formed origin"],url:[se,"not a safe absolute http(s) URL"],bcp47:[ie,"not a well-formed BCP 47 tag"],code:[le,"not a code of lower-case letters and underscores"]};function N(t,e,n){for(let r of e){let s=n===""?r.n:`${n}.${r.n}`,i=t[r.n];if(i==null){if(r.o)continue;return `${s}: required`}let d=A(i,r,s);if(d)return d}return null}function A(t,e,n){switch(e.k){case "u":return typeof t=="boolean"?null:A(t,{...e,k:typeof t=="string"?"s":"n"},n);case "s":{if(typeof t!="string")return `${n}: expected string`;if(t.length>e.max)return `${n}: exceeds ${e.max} characters`;if(e.f){let[r,s]=me[e.f];if(!r(t))return `${n}: ${s}`}return null}case "n":case "i":return typeof t!="number"||!(e.k=="i"?Number.isInteger:Number.isFinite)(t)?`${n}: expected ${e.k=="i"?"integer":"number"}`:t<e.lo||t>e.hi?`${n}: out of range ${e.lo}..${e.hi}`:null;case "b":return typeof t=="boolean"?null:`${n}: expected boolean`;case "e":{let r=e.v;return r.includes(t)?null:`${n}: not one of ${r.join("|")}`}case "o":return typeof t!="object"||t===null||Array.isArray(t)?`${n}: expected object`:N(t,e.fs,n);case "j":return typeof t!="object"||t===null||Array.isArray(t)?`${n}: expected a plain object`:null;case "a":{if(!Array.isArray(t))return `${n}: expected array`;if(t.length>e.max)return `${n}: exceeds ${e.max} items`;let r=e.it,s=`${n}[]`;for(let i of t){if(i==null){if(r.o)continue;return `${s}: required`}let d=A(i,r,s);if(d)return d}return null}default:{let r=e.k;return `${n}: unknown kind ${String(r)}`}}}var V=Object.create(null);function o(t,e,n,r){return b[t]=n,r&&(q[t]=r),V[t]=s=>{let i=de(s);if(i)return i;if(typeof s!="object"||s===null||Array.isArray(s))return "message: expected object";let d=s;return d.gem!==l?"gem: wrong protocol marker":d.type!==t?`type: expected ${t}`:N(d,e,"")}}var m={n:"requestId",k:"s",max:64};o("gem.handshake",[{n:"versions",k:"o",fs:[{n:"min",k:"i",lo:1,hi:1e3},{n:"max",k:"i",lo:1,hi:1e3}]},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}},{n:"embedder",k:"s",max:2048,f:"origin"},{n:"unbidden",k:"b",o:1}],30);o("gem.handshake.accept",[{n:"v",k:"i",lo:1,hi:1e3},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}}],5);o("gem.token",[{n:"token",k:"s",max:4096},{n:"requestId",k:"s",o:1,max:64},{n:"generation",k:"i",lo:0,hi:2147483647},{n:"apiBaseUrl",k:"s",o:1,max:512,f:"url"},{n:"playerId",k:"s",max:64},{n:"gameId",k:"s",max:64},{n:"channel",k:"e",v:["dev","test","release"]},{n:"gameVersion",k:"s",o:1,max:64},{n:"expiresAt",k:"i",lo:0,hi:4102444800},{n:"chainEndsAt",k:"i",lo:0,hi:4102444800},{n:"allowDevTools",k:"b",o:1}],30);o("gem.token.denied",[m,{n:"retryable",k:"b"},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:36e5},{n:"reason",k:"e",v:["chain_exhausted","unavailable"]},{n:"correlationId",k:"s",max:64}],30);o("gem.player",[{n:"untrusted",k:"o",fs:[{n:"displayName",k:"s",max:64},{n:"displayTag",k:"s",o:1,max:80},{n:"displayHandle",k:"s",max:80}]},{n:"playerId",k:"s",o:1,max:64},{n:"discriminator",k:"s",o:1,max:8},{n:"avatarIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatarIconId",k:"s",o:1,max:64},{n:"avatar3dId",k:"s",o:1,max:64},{n:"avatar3dModelUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatar3dIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatars3d",k:"a",o:1,max:8,it:{n:"",k:"o",fs:[{n:"family",k:"s",max:32},{n:"id",k:"s",max:64},{n:"modelUrl",k:"s",max:256,f:"url"},{n:"iconUrl",k:"s",max:256,f:"url"}]}},{n:"badges",k:"a",o:1,max:2,it:{n:"",k:"o",fs:[{n:"itemId",k:"s",max:64},{n:"iconUrl",k:"s",max:2048,f:"url"}]}},{n:"displayTag",k:"s",max:8},{n:"avatarUrl",k:"s",o:1,max:2048,f:"url"},{n:"boost",k:"o",o:1,fs:[{n:"multipliers",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"currencyId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"multiplierBp",k:"i",lo:1e4,hi:1e6}]}},{n:"expiresInDays",k:"i",o:1,lo:1,hi:7}]}],12);o("gem.pause",[{n:"paused",k:"b"}],120);o("gem.prefs",[{n:"reducedMotion",k:"b"},{n:"muted",k:"b"},{n:"showFrameStats",k:"b",o:1},{n:"locale",k:"s",max:35,f:"bcp47"},{n:"recommended",k:"o",o:1,fs:[{n:"volume",k:"i",lo:0,hi:100},{n:"batterySaver",k:"b"},{n:"keyboardLayout",k:"e",v:["wasd","arrows"]},{n:"gamepadConfirm",k:"e",v:["south","east"]},{n:"vibration",k:"b"},{n:"lookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"gamepadLookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"invertLookY",k:"b",o:1},{n:"touchMovement",k:"s",o:1,max:16,f:"code"},{n:"graphicsQuality",k:"s",o:1,max:16,f:"code"},{n:"graphicsQualityAuto",k:"b",o:1}]}],60);o("gem.cohorts",[{n:"cohorts",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"experimentId",k:"s",max:36},{n:"variantKey",k:"s",max:32}]}}],12,"cohorts");o("gem.earnAllowanceChanged",[],30,"earnAllowance");o("gem.viewport",[{n:"insets",k:"o",fs:[{n:"top",k:"i",lo:0,hi:4096},{n:"right",k:"i",lo:0,hi:4096},{n:"bottom",k:"i",lo:0,hi:4096},{n:"left",k:"i",lo:0,hi:4096}]},{n:"reserved",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384},{n:"control",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384}]}]}}],60,"viewport");o("gem.deviceSettings",[{n:"settings",k:"j",o:1}],12,"deviceSettings");o("gem.willTerminate",[{n:"reason",k:"e",v:["grant_expiring","session_ended","navigating","kill_switch"]},{n:"graceMs",k:"i",lo:0,hi:6e4}],10);o("gem.roomEvent",[{n:"event",k:"e",v:["updated","match_started","match_ready","match_failed","version_stale"]},{n:"roomId",k:"s",max:64},{n:"matchId",k:"s",o:1,max:64},{n:"version",k:"i",o:1,lo:0,hi:2147483647},{n:"reason",k:"s",o:1,max:64},{n:"platform",k:"e",o:1,v:["universal","mobile"]}],120,"rooms");o("gem.socketState",[{n:"connected",k:"b"},{n:"reason",k:"e",o:1,v:["connecting","dropped","watchdog","terminal"]}],12,"socketState");o("gem.seatedEvent",[{n:"roomId",k:"s",max:64}],60,"partySeating");o("gem.autoStartFailedEvent",[{n:"roomId",k:"s",max:64},{n:"code",k:"s",max:64}],30,"autoStartFailure");o("gem.ticketEvent",[{n:"event",k:"e",v:["queued","matched","expired","cancelled","failed"]},{n:"ticketId",k:"s",max:64},{n:"partySize",k:"i",o:1,lo:1,hi:64},{n:"queuedAt",k:"i",o:1,lo:0,hi:4102444800},{n:"expiresAt",k:"i",o:1,lo:0,hi:4102444800},{n:"reason",k:"s",o:1,max:64},{n:"roomId",k:"s",o:1,max:64}],60,"ticketEvents");o("gem.signalEvent",[{n:"event",k:"e",v:["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1800,"p2p");o("gem.dedicatedEvent",[{n:"event",k:"e",v:["answer","error","matchEnded"]},{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",o:1,max:10240},{n:"sessionId",k:"s",o:1,max:64},{n:"code",k:"s",o:1,max:64},{n:"message",k:"s",o:1,max:256},{n:"reason",k:"s",o:1,max:64}],120,"dedicated");o("gem.partyResult",[m,{n:"partyId",k:"s",o:1,max:64}],30,"parties");o("gem.clusterTarget",[{n:"clusterTargetId",k:"s",max:64},{n:"clusterTargetIds",k:"a",o:1,max:3,it:{n:"",k:"s",max:64}}],12,"clusterTarget");o("gem.devtools",[{n:"action",k:"e",v:["open","close","toggle"]}],30,"devtools");o("gem.screenshot",[{n:"maxWidth",k:"i",lo:1,hi:7680},{n:"maxHeight",k:"i",lo:1,hi:4320}],6,"screenshot");o("gem.screenshot.denied",[{n:"reason",k:"e",v:["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]},{n:"retryable",k:"b"}],12,"screenshot");o("gem.requestContext",[],12,"feedbackContext");o("gem.gameContext",[{n:"context",k:"j"}],12,"feedbackContext");o("gem.signal",[{n:"signal",k:"e",v:["join","offer","answer","ice","migrationAck","hostUnreachable"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1200,"p2p");o("gem.dedicatedOffer",[{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",max:10240},{n:"intent",k:"e",v:["initial","ice_restart","full_reconnect"]}],60,"dedicated");o("gem.partyRequest",[m],12,"parties");o("gem.ready",[],5);o("gem.engaged",[],5,"engagement");o("gem.progress",[{n:"loaded",k:"i",lo:0,hi:1099511627776},{n:"total",k:"i",lo:0,hi:1099511627776}],600);o("gem.requestToken",[m,{n:"reason",k:"e",v:["initial","expiring","rejected"]}],12);o("gem.requestLocalServer",[m,{n:"gameRoomId",k:"s",max:64}],6,"localHost");o("gem.localServer",[m,{n:"serverId",k:"s",o:1,max:128}],30,"localHost");o("gem.localServer.denied",[m,{n:"retryable",k:"b"},{n:"reason",k:"e",v:["refused","unavailable"]}],30,"localHost");o("gem.exit",[{n:"reason",k:"s",o:1,max:64}],5);o("gem.openExternal",[{n:"url",k:"s",max:2048,f:"url"}],6);o("gem.requestFullscreen",[{n:"enabled",k:"b"}],30);o("gem.audioState",[{n:"muted",k:"b"}],120,"audioState");o("gem.rewardCue",[{n:"currencySlug",k:"s",max:32},{n:"expected",k:"i",o:1,lo:1,hi:1e9},{n:"origin",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384}]}],60,"rewardCue");o("gem.walletDisplay",[{n:"visible",k:"b"},{n:"currencies",k:"a",o:1,max:8,it:{n:"",k:"s",max:64}},{n:"score",k:"b",o:1}],60,"walletDisplay");o("gem.controlsDeclare",[{n:"uses",k:"o",fs:[{n:"lookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"gamepadLookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"invertLookY",k:"s",o:1,max:16,f:"code"},{n:"touchMovement",k:"s",o:1,max:16,f:"code"}]}],60,"controls");o("gem.menu",[{n:"settings",k:"b"},{n:"items",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:32},{n:"label",k:"s",max:40}]}}],60,"gameMenu");o("gem.menuSelect",[{n:"entry",k:"e",v:["settings","item"]},{n:"id",k:"s",o:1,max:32}],60,"gameMenu");o("gem.gemCorner",[{n:"corner",k:"e",v:["top-left","top-right","bottom-left","bottom-right"]}],12,"gemCorner");o("gem.settingsDeclare",[{n:"settings",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:40},{n:"label",k:"s",max:40},{n:"kind",k:"e",v:["toggle","slider","choice"]},{n:"value",k:"u",max:40,lo:-1e6,hi:1e6},{n:"quick",k:"b",o:1},{n:"icon",k:"s",o:1,max:16,f:"code"},{n:"min",k:"n",o:1,lo:-1e6,hi:1e6},{n:"max",k:"n",o:1,lo:-1e6,hi:1e6},{n:"step",k:"n",o:1,lo:0,hi:1e6},{n:"options",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:40},{n:"label",k:"s",max:40}]}}]}}],120,"gameSettings");o("gem.settingChange",[{n:"id",k:"s",max:40},{n:"value",k:"u",max:40,lo:-1e6,hi:1e6}],120,"gameSettings");o("gem.requestShare",[{n:"params",k:"a",max:16,it:{n:"",k:"o",fs:[{n:"key",k:"s",max:64},{n:"value",k:"s",max:512}]}}],6,"share");o("gem.requestAvatarPicker",[{n:"tab",k:"e",o:1,v:["2d","3d"]}],6,"avatarPicker");o("gem.vibrate",[{n:"pattern",k:"a",max:99,it:{n:"",k:"i",lo:0,hi:1e4}}],600,"vibration");o("gem.frameStats",[{n:"fps",k:"i",lo:0,hi:1e3},{n:"frameTimeP95Ms",k:"i",lo:0,hi:1e5},{n:"counts",k:"e",o:1,v:["pageFrames","gameDraws"]}],120,"frameStats");o("gem.portraitBorders",[{n:"name",k:"s",o:1,max:40}],60,"portraitBorders");o("gem.error",[{n:"code",k:"e",v:["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]},{n:"message",k:"s",o:1,max:256}],60);o("gem.requestPurchase",[m,{n:"purchaseKind",k:"e",v:["item","recipe"]},{n:"targetId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"quantity",k:"i",o:1,lo:1,hi:10},{n:"catalogId",k:"s",o:1,max:64}],12,"purchases");o("gem.requestAvatar3dPurchase",[m,{n:"itemId",k:"s",max:64},{n:"currencySlug",k:"s",max:32}],12,"avatar3dPurchases");o("gem.purchaseResult",[m,{n:"outcome",k:"e",v:["ok","declined","unavailable"]},{n:"reason",k:"s",o:1,max:64,f:"code"},{n:"purchaseId",k:"s",o:1,max:64}],30,"purchases");o("gem.deviceSettings.put",[m,{n:"settings",k:"j"}],30,"deviceSettings");o("gem.deviceSettings.result",[m,{n:"outcome",k:"e",v:["stored","too_large","unavailable"]}],30,"deviceSettings");o("gem.requestAd",[m,{n:"placement",k:"s",max:32},{n:"format",k:"e",v:["interstitial","rewarded"]}],6,"ads");o("gem.adResult",[m,{n:"outcome",k:"e",v:["shown","no_ad","unavailable"]}],12,"ads");o("gem.signalMoment",[m,{n:"kind",k:"e",v:["level_end","run_end","match_end","menu_return","other_break"]},{n:"placement",k:"s",o:1,max:32}],6,"moments");o("gem.momentResult",[m,{n:"shown",k:"b"}],12,"moments");o("gem.requestSignIn",[m],6,"signIn");o("gem.signInResult",[m,{n:"outcome",k:"e",v:["signed_in","declined","unavailable"]}],12,"signIn");o("gem.requestFriendChallenge",[m,{n:"playerId",k:"s",max:64},{n:"template",k:"s",max:64},{n:"params",k:"j",o:1}],12,"friendChallenges");o("gem.friendChallengeResult",[m,{n:"outcome",k:"e",v:["accepted","cancelled","refused","unavailable","rate_limited","invalid"]},{n:"code",k:"s",o:1,max:64},{n:"param",k:"s",o:1,max:64},{n:"channel",k:"s",o:1,max:16},{n:"configVersion",k:"i",o:1,lo:0,hi:9007199254740991},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:864e5},{n:"detail",k:"s",o:1,max:300}],30,"friendChallenges");var G=Object.freeze(V);b.__malformed=60;Object.freeze(b);Object.freeze(q);var ce=/^[a-z0-9][a-z0-9_-]{0,39}$/;function ue(t){return typeof t=="string"&&ce.test(t)}function z(t){if(t===null||ue(t))return t;let e=typeof t=="string"?JSON.stringify(t.length>48?`${t.slice(0,48)}\u2026`:t):`a value of type ${typeof t}`;console.warn(`[gem] showPortraitBorders: ${e} is not a border name, so nothing was sent. A name is 1 to 40 of a-z, 0-9, _ and -, starting with a letter or digit, exactly as your store page declares it under media.portrait_borders. Pass null for no art.`);}var M={now:()=>Date.now(),setTimeout:(t,e)=>setTimeout(t,e),clearTimeout:t=>clearTimeout(t)},x=class{constructor(e=M){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let n=b[e];if(n===void 0)return false;let r=this.clock.now(),s=this.lastRefill.get(e)??r,i=this.tokens.get(e)??n,d=Math.max(0,r-s),a=Math.min(n,i+d*n/6e4);return a<1?(this.tokens.set(e,a),this.lastRefill.set(e,r),false):(this.tokens.set(e,a-1),this.lastRefill.set(e,r),true)}};function _(t,e){return t.toLowerCase()===e.toLowerCase()}function O(t){let e=t.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function Y(t,e){if(typeof e!="string"||e==="")return false;let n,r;try{n=new URL(t),r=new URL(e);}catch{return false}return n.protocol!=="http:"&&n.protocol!=="https:"||r.protocol!=="http:"&&r.protocol!=="https:"||n.username!==""||n.password!==""||n.pathname!=="/"||n.search!==""||n.hash!==""||n.port===""?false:O(n.hostname)&&O(r.hostname)}function K(t,e){if(t.min>t.max||e.min>e.max)return null;let n=Math.min(t.max,e.max),r=Math.max(t.min,e.min);return n>=r?n:null}var X="[gem: use gem.text.toPlain()]",J=new Set,Q;function ge(t){try{if(J.has(t))return;let e=globalThis.location;if(Q??=!!e?.port&&O(e.hostname??""),!Q)return;J.add(t),console.warn(`[gem] ${t} is PlayerText, turned into a string: players see ${X}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
2
+ ${new Error().stack}`);}catch{}}var D=class{#e;#n;constructor(e,n="player text"){this.#e=e,this.#n=n,Object.freeze(this);}toString(){return ge(this.#n),X}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function j(t,e){return new D(t,e)}function Z(t){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(t)?t:0)))}function pe(t,e){if(t!==true)return {visible:false};let n=[],r=false;for(let s of Array.isArray(e)?e:[])s==="score"?r=true:typeof s=="string"&&s.length>0&&s.length<=64&&!n.includes(s)&&n.push(s);return !r&&n.length===0?{visible:true}:{visible:true,currencies:n.slice(0,8),score:r}}function ye(t){let e=[];for(let n of Array.isArray(t)?t:[]){if(e.length===4)break;if(typeof n!="object"||n===null)continue;let{id:r,label:s}=n;typeof r!="string"||r.length===0||r.length>32||typeof s!="string"||s.trim().length===0||e.some(i=>i.id===r)||e.push({id:r,label:s.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function ee(t,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(t)?t:0)))}function fe(t){let e=t.untrusted,n=j(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,s=r?j(r,"gem.player.displayTag"):void 0,i=t.discriminator||t.displayTag,d=i&&/^[0-9]{1,8}$/.test(i)?i:void 0,a=t.avatarIconUrl||t.avatarUrl,c=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,p=t.avatarIconId,y=t.avatar3dId,u={displayName:n,displayHandle:s??n,badges:(t.badges??[]).map(g=>({itemId:g.itemId,iconUrl:g.iconUrl}))};t.playerId&&(u.playerId=t.playerId),d&&(u.discriminator=d),s&&(u.displayTag=s),a&&(u.avatarIconUrl=u.avatarUrl=a),p&&c.test(p)&&(u.avatarIconId=p),y&&c.test(y)&&(u.avatar3dId=y),t.avatar3dModelUrl&&(u.avatar3dModelUrl=t.avatar3dModelUrl),t.avatar3dIconUrl&&(u.avatar3dIconUrl=t.avatar3dIconUrl),t.avatars3d&&(u.avatars3d=t.avatars3d.filter(g=>c.test(g.id)&&/^[a-z][a-z0-9_]*$/.test(g.family)).filter((g,P,T)=>T.findIndex(E=>E.family==g.family)==P).map(({family:g,id:P,modelUrl:T,iconUrl:E})=>({family:g,id:P,modelUrl:T,iconUrl:E})));let f=t.boost,h=(f?.multipliers??[]).map(g=>({currencyId:g.currencyId,currencySlug:g.currencySlug,multiplier:g.multiplierBp/1e4})),C=h.some(g=>g.multiplier>1);return u.boost=C&&f?.expiresInDays?{active:C,multipliers:h,expiresInDays:f.expiresInDays}:{active:C,multipliers:h},u}var he=1e4,w=class{constructor(e){this.options=e;if(e.expectedSource===void 0||e.expectedSource===null)throw new Error("GemFrame: expectedSource is required and must not be null \u2014 pass window.parent. The handshake transfers the MessagePort, and origin alone does not say WHICH document on that origin sent it.");this.expectedSource=e.expectedSource,this.clock=e.clock??M,this.inbound=new x(this.clock),this.outbound=new x(this.clock);}options;state="waiting";port=null;version=null;peerCapabilities=[];unbidden=false;engagedSent=false;readyPending=false;readySent=false;settleWaiters=[];clock;inbound;outbound;expectedSource;get currentState(){return this.state}get parentCapabilities(){return this.peerCapabilities}parentSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(e){if(e.source!==this.expectedSource)return this.refuse(e,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>_(a,e.origin))||Y(e.origin,this.options.selfOrigin)))return this.refuse(e,`embedder '${e.origin}' is not in allowedEmbedders`),this.state==="waiting"&&this.transition("refused"),false;if(this.state==="connected")return this.fail("handshake_duplicate","this document already has a port"),false;if(this.state!=="waiting")return false;let r=G["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let s=e.data;if(!_(s.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${s.embedder}' but arrived from '${e.origin}'`),false;let i=e.ports?.[0];if(!i)return this.refuse(e,"handshake transferred no MessagePort"),false;let d=K(v,s.versions);return d===null?(i.onmessage=null,i.postMessage({gem:l,type:"gem.error",code:"version_mismatch",message:`frame supports v${v.min}..v${v.max}`}),i.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=i,this.version=d,i.onmessage=a=>this.receive(a.data,a.ports??[]),i.start?.(),this.peerCapabilities=s.capabilities??[],this.unbidden=s.unbidden===true,i.postMessage({gem:l,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:l,type:"gem.ready"});}settled(e=he){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(n=>{let r=i=>{this.clock.clearTimeout(s),n(i);},s=this.clock.setTimeout(()=>{let i=this.settleWaiters.indexOf(r);i>=0&&this.settleWaiters.splice(i,1),n(this.state);},e);this.settleWaiters.push(r);})}progress(e,n){this.send({gem:l,type:"gem.progress",loaded:e,total:n});}requestToken(e,n){this.send({gem:l,type:"gem.requestToken",requestId:e,reason:n});}requestLocalServer(e,n){return this.parentSupports("localHost")?(this.send({gem:l,type:"gem.requestLocalServer",requestId:e,gameRoomId:n}),true):false}exit(e){this.send(e===void 0?{gem:l,type:"gem.exit"}:{gem:l,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:l,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:l,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:l,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:l,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let n={gem:l,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return e.expected!==void 0&&Number.isFinite(e.expected)&&e.expected>=1&&(n.expected=Math.min(1e9,Math.round(e.expected))),e.origin&&(n.origin={x:Z(e.origin.x),y:Z(e.origin.y)}),this.send(n),true}showPortraitBorders(e){let n=z(e);return n===void 0||!this.gated("portraitBorders")?false:this.send(n===null?{gem:l,type:"gem.portraitBorders"}:{gem:l,type:"gem.portraitBorders",name:n})}setWalletVisible(e,n){return this.gated("walletDisplay")?this.send({gem:l,type:"gem.walletDisplay",...pe(e,n?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:l,type:"gem.menu",...ye(e)}):false}declareSettings(e){return this.gated("gameSettings")?this.send({gem:l,type:"gem.settingsDeclare",settings:e}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:l,type:"gem.gemCorner",corner:e}):false}requestShare(e){return this.gated("share")?this.send({gem:l,type:"gem.requestShare",params:e.map(({key:n,value:r})=>({key:n,value:r}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:l,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:l,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:l,type:"gem.frameStats",fps:ee(e.fps,1e3),frameTimeP95Ms:ee(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:l,type:"gem.gameContext",context:e}),true)}signal(e,n,r){this.send({gem:l,type:"gem.signal",signal:e,matchId:n,data:r});}dedicatedOffer(e,n,r){this.send({gem:l,type:"gem.dedicatedOffer",matchId:e,sdp:n,intent:r});}requestParty(e){return this.send({gem:l,type:"gem.partyRequest",requestId:e})}requestPurchase(e,n,r,s,i,d){return this.send({gem:l,type:"gem.requestPurchase",requestId:e,purchaseKind:n,targetId:r,currencySlug:s,...i===void 0?{}:{quantity:i},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(e,n,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:l,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:n,currencySlug:r}):false}putDeviceSettings(e,n){return this.send({gem:l,type:"gem.deviceSettings.put",requestId:e,settings:n})}requestAd(e,n,r){return this.send({gem:l,type:"gem.requestAd",requestId:e,placement:n,format:r})}signalMoment(e,n,r){return this.send({gem:l,type:"gem.signalMoment",requestId:e,kind:n,...r===void 0?{}:{placement:r}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:l,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,n,r,s){return this.send({gem:l,type:"gem.requestFriendChallenge",requestId:e,playerId:n,template:r,...s===void 0?{}:{params:s}})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(e){if(this.state!==e){this.state=e;for(let n of this.settleWaiters.splice(0))n(e);this.options.onStateChange?.(e);}}refuseMalformed(e,n){if(!this.inbound.allow(S)){this.options.onDropped?.(e,n);return}this.fail(e,n);}dropUnknownType(e){this.inbound.allow(S)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,n){e.ports?.[0]?.close?.(),this.inbound.allow(S)&&this.options.onRefused?.(n);}fail(e,n){this.options.onError?.(e,n),this.state==="connected"&&this.port&&this.send({gem:l,type:"gem.error",code:e,...n===void 0?{}:{message:n}});}gated(e){return this.state==="connected"&&this.parentSupports(e)}send(e){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let n=e.type;if(!this.outbound.allow(n))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${n}`),false;let r=G[n]?.(e);return r?(this.options.onError?.("internal",`refusing to send malformed ${n}: ${r}`),false):(this.port.postMessage(this.version===null?e:{...e,v:this.version}),true)}receive(e,n=[]){let r=false;try{r=this.dispatchInbound(e,n);}finally{r||ne(n);}}dispatchInbound(e,n){if(this.state!=="connected")return false;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let s=G[r];if(!s)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let i=B(r);if(i!==void 0&&!this.options.capabilities?.includes(i))return this.fail("unsupported_capability",`${r} requires the '${i}' capability, which this frame did not advertise`),false;let d=s(e);if(d)return this.fail("invalid_message",`${r}: ${d}`),false;let a=e;if(a.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.settingChange":this.options.onSettingChange?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let c=n[0];if(!c){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let p={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){c.close(),this.screenshotDenied("not_wired",false);break}return ne(n.slice(1)),this.options.onScreenshot(p,c,(y,u)=>this.screenshotDenied(y,u)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(fe(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,n){this.send({gem:l,type:"gem.screenshot.denied",reason:e,retryable:n});}};function ne(t){for(let e of t)try{e?.close?.();}catch{}}$();function ke(t){if((t.expectedSource??window.parent)===window)throw new Error("GemWebSdkBridge.connect: this document is not framed (window.parent === window), so there is no embedder to hand it a port. A game runs inside the Gem Arcade iframe; opening it directly will never complete a handshake.");let e=new w({...t,expectedSource:t.expectedSource??window.parent}),n=r=>{let s=e.handleWindowMessage({data:r.data,origin:r.origin,source:r.source,ports:r.ports});return s&&window.removeEventListener("message",n),s};return window.addEventListener("message",n),U(window,n),e}var te={connect:ke,GemFrame:w,PROTOCOL:l,SUPPORTED_VERSIONS:v};window.GemWebSdkBridge=te;var Yn=te;return Yn;})();