@looplay/sdk 0.5.1 → 0.7.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/README.md +141 -4
- package/dist/looplay-sdk.cjs.js +133 -79
- package/dist/looplay-sdk.cjs.js.map +1 -1
- package/dist/looplay-sdk.esm.js +133 -79
- package/dist/looplay-sdk.esm.js.map +1 -1
- package/dist/looplay-sdk.min.js +2 -2
- package/dist/looplay-sdk.min.js.map +1 -1
- package/dist/types/apps/LooplaySDK.types.d.ts +121 -18
- package/dist/types/apps/api-client.d.ts +36 -17
- package/dist/types/apps/service-client.d.ts +20 -15
- package/dist/types/index.d.ts +0 -1
- package/dist/types/types.d.ts +2 -1
- package/package.json +1 -1
package/dist/looplay-sdk.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var LooplaySDK=(function(exports){'use strict';var o=class extends Error{constructor(e){super(e),this.name="LooplaySDKError";}},l=class extends o{constructor(){super("LooplaySDK not initialized. Call init({ gameId }) first."),this.name="NotInitializedError";}},u=class extends o{constructor(){super("Missing baseUrl. Provide `baseUrl` to enable HTTP integration."),this.name="MissingBaseUrlError";}},p=class extends o{constructor(){super("Missing auth. Provide `auth` options to enable user login/session."),this.name="MissingAuthError";}},g=class extends o{constructor(){super("Not authenticated. Login is required for this operation."),this.name="NotAuthenticatedError";}};var y=class{map=new Map;async getItem(e){return this.map.has(e)?this.map.get(e):null}async setItem(e,t){this.map.set(e,t);}async removeItem(e){this.map.delete(e);}},S=class{async getItem(e){return typeof window>"u"||!window.localStorage?null:window.localStorage.getItem(e)}async setItem(e,t){typeof window>"u"||!window.localStorage||window.localStorage.setItem(e,t);}async removeItem(e){typeof window>"u"||!window.localStorage||window.localStorage.removeItem(e);}};var w=class extends o{constructor(){super("Invalid session returned by auth provider"),this.name="InvalidSessionError";}},f=class{provider;storage;key;session=null;listeners=new Set;constructor(e){this.provider=e.provider,this.storage=e.storage??new y,this.key=`${e.storageKeyPrefix??"looplay"}:auth:session`;}async init(){await this.provider.init?.(),await this.restoreFromStorage(),await this.provider.onSessionChanged?.(this.session);}getSession(){return this.session}async login(e){let t=await this.provider.login(e);if(!t?.session?.user?.id)throw new w;return await this.setSession(t.session,"login"),t.session}async logout(){await this.provider.logout(),await this.setSession(null,"logout");}async getAccessToken(){if(!this.session)return;let e=Date.now();if(!(typeof this.session.expiresAt=="number"&&this.session.expiresAt<=e))return this.session.accessToken;if(!this.provider.refresh)return this.session.accessToken;let s=await this.provider.refresh(),i={...this.session,...s};return await this.setSession(i,"sessionChanged"),i.accessToken}on(e,t){return this.listeners.add(t),()=>this.listeners.delete(t)}async restoreFromStorage(){let e=await this.storage.getItem(this.key);if(e)try{let t=JSON.parse(e);t?.user?.id&&(this.session=t);}catch{await this.storage.removeItem(this.key);}}async setSession(e,t){this.session=e,await this.provider.onSessionChanged?.(this.session),e?await this.storage.setItem(this.key,JSON.stringify(e)):await this.storage.removeItem(this.key);for(let s of this.listeners)s(this.session);}};function L(n){return n?typeof window<"u"&&n===window.fetch?window.fetch.bind(window):n:globalThis.fetch.bind(globalThis)}var A=class extends Error{status;bodyText;constructor(e,t,s){super(e),this.name="HttpError",this.status=t,this.bodyText=s;}},k=class{baseUrl;fetchFn;defaultHeaders;constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.fetchFn=L(e.fetch),this.defaultHeaders=e.defaultHeaders??{};}async request(e,t,s){let i=this.buildUrl(t,s?.query),r=this.mergeHeaders(this.defaultHeaders,s?.headers);s?.bearerToken?r.Authorization=`Bearer ${s.bearerToken}`:s?.anonId&&(r["x-looplay-anon-id"]=s.anonId);let a;s?.body!==void 0&&(r["Content-Type"]=r["Content-Type"]??"application/json",a=JSON.stringify(s.body));let d=await this.fetchFn(i,{method:e,headers:r,body:a});if(!d.ok){let q=await this.safeText(d);throw new A(`HTTP ${d.status} ${d.statusText}`,d.status,q)}let P=await this.safeText(d);if(P)return JSON.parse(P)}mergeHeaders(...e){let t={};for(let s of e)if(s)for(let[i,r]of Object.entries(s))r!==void 0&&(t[i]=r);return t}buildUrl(e,t){let s=e.startsWith("/")?e:`/${e}`,i=new URL(`${this.baseUrl}${s}`);if(t)for(let[r,a]of Object.entries(t))a!==void 0&&i.searchParams.set(r,String(a));return i.toString()}async safeText(e){try{return await e.text()}catch{return ""}}};var c=class{http;constructor(e){this.http=new k(e);}async telegramLogin(e){let t={telegramInitData:e};return this.http.request("POST","/account/telegram-login",{body:t})}async refresh(e){let t={refreshToken:e};return this.http.request("POST","/account/refresh",{body:t})}async logout(e){let t={refreshToken:e};return this.http.request("POST","/account/logout",{body:t})}async listGames(e){return this.http.request("GET","/sdk/games",{query:e})}async getGameDetail(e,t){return this.http.request("GET",`/sdk/games/${encodeURIComponent(e)}`,{bearerToken:t})}async listRecentPlayed(e,t){return this.http.request("GET","/sdk/games/recent-play",{bearerToken:e,query:t})}async trackView(e,t){return await this.http.request("POST",`/sdk/games/${encodeURIComponent(t)}/view`,{...e,headers:this.trackingHeaders(e)}),true}async trackPlay(e,t,s,i){let r={playTimeSeconds:i,attemptId:s};return await this.http.request("POST",`/sdk/games/${encodeURIComponent(t)}/play`,{...e,headers:this.trackingHeaders(e),body:r}),true}async trackMatchEnd(e,t,s){return await this.http.request("POST",`/sdk/games/${encodeURIComponent(t)}/end`,{...e,headers:this.trackingHeaders(e),body:s}),true}async emitGameEvent(e,t,s){return this.http.request("POST",`/sdk/games/${encodeURIComponent(t)}/emit`,{...e,headers:this.trackingHeaders(e),body:s})}async issueTrackingSession(e){return this.http.request("POST","/sdk/games/tracking/bootstrap",{headers:{"x-looplay-anon-id":e}})}trackingHeaders(e){return {"x-game-tracking-session-id":e.trackingSessionId,"x-looplay-anon-token":e.anonTrackingToken}}async listGameAssets(e,t){return this.http.request("GET",`/sdk/games/${encodeURIComponent(e)}/assets`,{bearerToken:t})}async purchaseGameAsset(e,t,s,i={}){return this.http.request("POST",`/sdk/games/${encodeURIComponent(t)}/assets/${encodeURIComponent(s)}/purchase`,{bearerToken:e,body:i})}async getBalances(e,t){return this.http.request("GET","/sdk/balance",{bearerToken:e,query:t})}async getBalanceHistory(e,t){return this.http.request("GET","/sdk/balance/history",{bearerToken:e,query:t})}async getMyProfile(e){return this.http.request("GET","/sdk/user/profile",{bearerToken:e})}async getMyBalance(e){return this.http.request("GET","/sdk/user/balance",{bearerToken:e})}async listReferrals(e,t){return this.http.request("GET","/sdk/referrals",{bearerToken:e,query:t})}async setReferral(e,t){return this.http.request("POST","/sdk/referrals/set",{bearerToken:e,body:t})}async listTasks(e){return this.http.request("GET","/sdk/task",{bearerToken:e})}async listFinishedTasks(e){return this.http.request("GET","/sdk/task/finished",{bearerToken:e})}async startTask(e,t){return this.http.request("POST","/sdk/task/start",{bearerToken:e,body:t})}async claimTask(e,t){return this.http.request("POST","/sdk/task/claim",{bearerToken:e,body:t})}};function E(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let e=Math.random()*16|0;return (n==="x"?e:e&3|8).toString(16)})}var h=class{raw;getAccessToken;getAnonymousId;trackingSession;trackingSessionPromise;currentAttemptId;constructor(e){if(!e.baseUrl)throw new u;this.raw=new c({baseUrl:e.baseUrl,fetch:e.fetch,defaultHeaders:e.defaultHeaders}),this.getAccessToken=e.getAccessToken,this.getAnonymousId=e.getAnonymousId;}unsafeRaw(){return this.raw}async listGames(e){return this.raw.listGames(e)}async requireToken(){let e=await this.getAccessToken?.();if(!e)throw new g;return e}async resolveTrackingAuth(){let e=await this.getAccessToken?.(),t=e?void 0:this.getAnonymousId?.();if(!e&&!t)throw new g;let s=await this.ensureTrackingSession(t);return {bearerToken:e,anonId:t,trackingSessionId:s?.trackingSessionId,anonTrackingToken:s?.anonTrackingToken}}async ensureTrackingSession(e){let t=Date.now();return this.trackingSession&&this.trackingSession.expiresAtMs>t+3e4?this.trackingSession:(this.trackingSessionPromise||(this.trackingSessionPromise=this.raw.issueTrackingSession(e).then(s=>(this.trackingSession={trackingSessionId:s.trackingSessionId,anonTrackingToken:s.trackingToken,expiresAtMs:new Date(s.expiresAt).getTime()},this.trackingSession)).catch(()=>{}).finally(()=>{this.trackingSessionPromise=void 0;})),this.trackingSessionPromise)}async getGameDetail(e){let t=await this.getAccessToken?.();return this.raw.getGameDetail(e,t)}async listRecentPlayed(e){let t=await this.requireToken();return this.raw.listRecentPlayed(t,e)}async trackView(e){let t=await this.resolveTrackingAuth();return this.raw.trackView(t,e)}startAttempt(){return this.currentAttemptId=E(),this.currentAttemptId}async trackPlay(e,t){let s=await this.resolveTrackingAuth(),i=this.currentAttemptId??this.startAttempt();return this.raw.trackPlay(s,e,i,t)}async trackMatch(e,t){let s=await this.resolveTrackingAuth();return this.raw.trackMatchEnd(s,e,t)}async emit(e,t,s){let i=await this.resolveTrackingAuth(),r={actionCode:t,value:s?.value,refId:s?.refId,payload:s?.payload};return this.raw.emitGameEvent(i,e,r)}async listGameAssets(e){let t=await this.getAccessToken?.();return this.raw.listGameAssets(e,t)}async purchaseGameAsset(e,t,s){let i=await this.requireToken();return this.raw.purchaseGameAsset(i,e,t,s)}async getMyProfile(){let e=await this.requireToken();return this.raw.getMyProfile(e)}async getMyBalance(){let e=await this.requireToken();return this.raw.getMyBalance(e)}async getBalances(e){let t=await this.requireToken();return this.raw.getBalances(t,e)}async getBalanceHistory(e){let t=await this.requireToken();return this.raw.getBalanceHistory(t,e)}async listReferrals(e){let t=await this.requireToken();return this.raw.listReferrals(t,e)}async setReferral(e){let t=await this.requireToken();return this.raw.setReferral(t,e)}async listTasks(){let e=await this.requireToken();return this.raw.listTasks(e)}async listFinishedTasks(){let e=await this.requireToken();return this.raw.listFinishedTasks(e)}async startTask(e){let t=await this.requireToken();return this.raw.startTask(t,e)}async claimTask(e){let t=await this.requireToken();return this.raw.claimTask(t,e)}};var R=class{auth;api;gameId;initialized=false;constructor(e={}){let{auth:t,baseUrl:s,fetch:i,defaultHeaders:r}=e;t&&(this.auth=new f(t)),s&&(this.api=new h({baseUrl:s,fetch:i,defaultHeaders:r,getAccessToken:async()=>this.auth?.getAccessToken()}));}async init(e){this.gameId=e.gameId,await this.auth?.init(),await this.verifyGameId(e),this.initialized=true;}async getAccessToken(){return this.auth?.getAccessToken()}getGameId(){return this.gameId}async getMyProfile(){if(this.assertInitialized(),!this.api)throw new u;return this.api.getMyProfile()}async trackView(){if(this.assertInitialized(),!this.api)throw new u;if(!this.gameId)throw new l;return this.api.trackView(this.gameId)}startAttempt(){if(this.assertInitialized(),!this.api)throw new u;return this.api.startAttempt()}async trackPlay(e){if(this.assertInitialized(),!this.api)throw new u;if(!this.gameId)throw new l;return this.api.trackPlay(this.gameId,e)}async trackMatch(e,t){if(this.assertInitialized(),!this.api)throw new u;if(!this.gameId)throw new l;return this.api.trackMatch(this.gameId,{matchId:e,matchDurationSeconds:t.durationSeconds,isCompleted:t.isCompleted,isWin:t.isWin})}async emit(e,t){if(this.assertInitialized(),!this.api)throw new u;if(!this.gameId)throw new l;return this.api.emit(this.gameId,e,t)}assertInitialized(){if(!this.initialized)throw new l}async verifyGameId(e){if((e.verifyMode??"none")!=="none"){if(!this.auth)throw new p;if(!this.api)throw new u;await this.api.getGameDetail(e.gameId);}}};function G(n){if(!n)return;let e=n.split(".");if(!(e.length<2))try{let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),s=t+"===".slice((t.length+3)%4);if(typeof globalThis.atob!="function")return;let i=globalThis.atob(s);return JSON.parse(i)}catch{return}}function T(n){let e=G(n)?.exp;return typeof e=="number"?e*1e3:void 0}var v=class extends o{constructor(){super("Missing Telegram init data"),this.name="MissingTelegramInitDataError";}},I=class extends o{constructor(){super("Missing refresh token"),this.name="MissingRefreshTokenError";}},x=class{id="telegram";client;getTelegramInitData;mapUser;session=null;constructor(e){this.client=new c(e),this.getTelegramInitData=e.getTelegramInitData,this.mapUser=e.mapUser;}onSessionChanged=e=>{this.session=e;};async login(e){let t=e?.telegramInitData??this.getTelegramInitData?.()??globalThis?.Telegram?.WebApp?.initData;if(!t)throw new v;let s=await this.client.telegramLogin(t),i=s.user??{},a={user:this.mapUser?this.mapUser(i):this.defaultMapUser(i),accessToken:s.accessToken,refreshToken:s.refreshToken,expiresAt:T(s.accessToken)};return this.session=a,{session:a}}async refresh(){let e=this.session,t=e?.refreshToken;if(!e||!t)throw new I;let s=await this.client.refresh(t),i={accessToken:s.accessToken,refreshToken:s.refreshToken,expiresAt:T(s.accessToken)};return this.session={...e,...i},i}async logout(){let e=this.session?.refreshToken;if(e)try{await this.client.logout(e);}catch{}this.session=null;}defaultMapUser(e){let t=e.id??e._id??e.userId??e.telegramId;if(t==null||String(t).trim()==="")throw new o("Missing user id from auth response");return {id:String(t),displayName:e.displayName??e.username??e.name,avatarUrl:e.avatarUrl??e.photoUrl??e.avatar}}};var U="looplay:anon-id",O=4e3,M=5e3,D={jwt:null,gameId:null,apiUrl:null,anonymousId:null,origin:null,receivedAt:null},C=n=>typeof n=="object"&&n!==null,m=n=>{if(typeof n!="string")return null;let e=n.trim();return e||null};function _(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let e=Math.random()*16|0;return (n==="x"?e:e&3|8).toString(16)})}function F(n){if(typeof window>"u"||!window.localStorage)return null;try{return window.localStorage.getItem(n)}catch{return null}}function H(n,e){if(!(typeof window>"u"||!window.localStorage))try{window.localStorage.setItem(n,e);}catch{}}var b=class{state=D;initialized=false;lifecycleTrackingInitialized=false;lastGameStartedKey=null;client=null;listeners=new Set;options;messageListener=null;anonymousFallbackTimer=null;pendingAdsRequests=new Map;requestAdsSequence=0;adsRequestInFlight=false;adsRequestsSent=0;lastAdsRequestAt=0;autoAdsActionCount=0;constructor(e={}){this.options=e;}getState(){return this.state}getJwt(){return this.state.jwt}getApiUrl(){return this.options.apiUrl??this.state.apiUrl}getGameId(){return this.options.appId??this.state.gameId}getAnonymousId(){return this.state.anonymousId}canTrack(){return !this.getApiUrl()||!this.getGameId()?false:!!(this.state.jwt||this.state.anonymousId)}subscribe(e){return this.listeners.add(e),e(this.state),()=>this.listeners.delete(e)}requestAuth(){if(typeof window>"u"||window.parent===window){this.debug("standalone mode detected; auth request skipped");return}this.debug("requesting auth from parent",{targetOrigin:this.resolveParentOrigin()??"*"}),window.parent.postMessage({type:"LOOPLAY_AUTH_REQUEST"},this.resolveParentOrigin()??"*");}requestAds(e={}){if(typeof window>"u"||window.parent===window)return this.debug("requestAds skipped; standalone mode detected"),Promise.resolve(false);if(this.adsRequestInFlight)return this.debug("requestAds skipped; another ad request is already in flight"),Promise.resolve(false);let t=this.resolveParentOrigin()??"*",s=`ads-${Date.now()}-${++this.requestAdsSequence}`,i=e.timeoutMs??M;return this.adsRequestInFlight=true,this.adsRequestsSent+=1,this.lastAdsRequestAt=Date.now(),this.debug("requesting rewarded ad from parent",{requestId:s,targetOrigin:t,gameId:this.getGameId()}),new Promise(r=>{let a=setTimeout(()=>{this.pendingAdsRequests.delete(s),this.adsRequestInFlight=false,this.debug("requestAds timed out",{requestId:s,timeoutMs:i}),r(false);},i);this.pendingAdsRequests.set(s,{resolve:r,timer:a}),window.parent.postMessage({type:"LOOPLAY_REQUEST_ADS",requestId:s,gameId:this.getGameId()},t);})}init(){if(this.initialized||typeof window>"u")return;this.initialized=true;let e=window.parent!==window;if(this.debug("auth listener initialized",{isIframe:e,parentOrigin:this.resolveParentOrigin()}),!e){this.activateAnonymousMode();return}let t=this.resolveParentOrigin();t||console.warn("[LooplaySDK] LooplayIframeAuth could not resolve a parent origin to verify postMessage against (document.referrer is empty). Auth messages from window.parent will be accepted regardless of origin. Pass `parentOrigin` explicitly to harden this."),this.messageListener=s=>{if(s.source===window.parent){if(t&&s.origin!==t){this.debug("ignored message from unexpected origin",{expectedOrigin:t,receivedOrigin:s.origin});return}this.handleIncomingMessage(s.data,s.origin);}},window.addEventListener("message",this.messageListener),this.anonymousFallbackTimer=setTimeout(()=>{this.state.jwt||this.activateAnonymousMode();},this.options.anonymousFallbackTimeoutMs??O),this.requestAuth();}dispose(){typeof window<"u"&&this.messageListener&&window.removeEventListener("message",this.messageListener),this.anonymousFallbackTimer!==null&&clearTimeout(this.anonymousFallbackTimer);for(let e of this.pendingAdsRequests.values())e.timer!==null&&clearTimeout(e.timer),e.resolve(false);this.pendingAdsRequests.clear(),this.messageListener=null,this.anonymousFallbackTimer=null,this.initialized=false,this.lifecycleTrackingInitialized=false,this.listeners.clear();}getClient(){if(this.client)return this.client;let e=this.getApiUrl();return this.debug("creating sdk ApiClient",{baseUrl:e}),this.client=new h({baseUrl:e??void 0,getAccessToken:async()=>this.getJwt()??void 0,getAnonymousId:()=>this.getAnonymousId()??void 0}),this.client}trackView(){let e=this.getGameId();return !this.canTrack()||!e?(this.debug("trackView skipped; auth is not ready",{hasGameId:!!e}),Promise.resolve(false)):(this.debug("trackView dispatched",{gameId:e}),this.getClient().trackView(e))}startAttempt(){return this.getClient().startAttempt()}trackPlay(e){let t=this.getGameId();return !this.canTrack()||!t?(this.debug("trackPlay skipped; auth is not ready",{hasGameId:!!t}),Promise.resolve(false)):(this.debug("trackPlay dispatched",{gameId:t,playTimeSeconds:e}),this.getClient().trackPlay(t,e))}trackMatch(e,t){let s=this.getGameId();if(!this.canTrack()||!s)return this.debug("trackMatch skipped; auth is not ready",{matchId:e}),Promise.resolve(false);this.debug("trackMatch dispatched",{gameId:s,matchId:e,...t});let i=this.getClient().trackMatch(s,{matchId:e,matchDurationSeconds:t.durationSeconds,isCompleted:t.isCompleted,isWin:t.isWin});return i.then(r=>{r&&this.recordAutoRequestAdsActivity();}).catch(()=>{}),i}emitGameEvent(e,t){let s=this.getGameId();return !this.canTrack()||!s?(this.debug("emitGameEvent skipped; auth is not ready",{actionCode:e}),Promise.resolve(null)):(this.debug("emitGameEvent dispatched",{gameId:s,actionCode:e,...t}),this.getClient().emit(s,e,t))}initLifecycleTracking(){this.lifecycleTrackingInitialized||typeof window>"u"||(this.lifecycleTrackingInitialized=true,this.debug("lifecycle tracking initialized"),this.subscribe(()=>{let e=this.getTrackingKey();!e||this.lastGameStartedKey===e||(this.lastGameStartedKey=e,this.debug("auto GAME_STARTED dispatch"),this.emitGameEvent("GAME_STARTED"));}));}getTrackingKey(){let e=this.getGameId(),t=this.getApiUrl(),s=this.state.jwt??this.state.anonymousId;return !e||!t||!s?null:`${t}
|
|
1
|
+
var LooplaySDK=(function(exports){'use strict';var u=class extends Error{constructor(e){super(e),this.name="LooplaySDKError";}},d=class extends u{constructor(){super("LooplaySDK not initialized. Call init({ gameId }) first."),this.name="NotInitializedError";}},l=class extends u{constructor(){super("Missing baseUrl. Provide `baseUrl` to enable HTTP integration."),this.name="MissingBaseUrlError";}},S=class extends u{constructor(){super("Missing auth. Provide `auth` options to enable user login/session."),this.name="MissingAuthError";}},m=class extends u{constructor(){super("Not authenticated. Login is required for this operation."),this.name="NotAuthenticatedError";}};var g=class{map=new Map;async getItem(e){return this.map.has(e)?this.map.get(e):null}async setItem(e,t){this.map.set(e,t);}async removeItem(e){this.map.delete(e);}},P=class{async getItem(e){return typeof window>"u"||!window.localStorage?null:window.localStorage.getItem(e)}async setItem(e,t){typeof window>"u"||!window.localStorage||window.localStorage.setItem(e,t);}async removeItem(e){typeof window>"u"||!window.localStorage||window.localStorage.removeItem(e);}};var A=class extends u{constructor(){super("Invalid session returned by auth provider"),this.name="InvalidSessionError";}},y=class{provider;storage;key;session=null;listeners=new Set;constructor(e){this.provider=e.provider,this.storage=e.storage??new g,this.key=`${e.storageKeyPrefix??"looplay"}:auth:session`;}async init(){await this.provider.init?.(),await this.restoreFromStorage(),await this.provider.onSessionChanged?.(this.session);}getSession(){return this.session}async login(e){let t=await this.provider.login(e);if(!t?.session?.user?.id)throw new A;return await this.setSession(t.session,"login"),t.session}async logout(){await this.provider.logout(),await this.setSession(null,"logout");}async getAccessToken(){if(!this.session)return;let e=Date.now();if(!(typeof this.session.expiresAt=="number"&&this.session.expiresAt<=e))return this.session.accessToken;if(!this.provider.refresh)return this.session.accessToken;let s=await this.provider.refresh(),n={...this.session,...s};return await this.setSession(n,"sessionChanged"),n.accessToken}on(e,t){return this.listeners.add(t),()=>this.listeners.delete(t)}async restoreFromStorage(){let e=await this.storage.getItem(this.key);if(e)try{let t=JSON.parse(e);t?.user?.id&&(this.session=t);}catch{await this.storage.removeItem(this.key);}}async setSession(e,t){this.session=e,await this.provider.onSessionChanged?.(this.session),e?await this.storage.setItem(this.key,JSON.stringify(e)):await this.storage.removeItem(this.key);for(let s of this.listeners)s(this.session);}};function q(i){return i?typeof window<"u"&&i===window.fetch?window.fetch.bind(window):i:globalThis.fetch.bind(globalThis)}var k=class extends Error{status;bodyText;constructor(e,t,s){super(e),this.name="HttpError",this.status=t,this.bodyText=s;}},f=class{baseUrl;fetchFn;defaultHeaders;constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.fetchFn=q(e.fetch),this.defaultHeaders=e.defaultHeaders??{};}async request(e,t,s){let n=this.buildUrl(t,s?.query),r=this.mergeHeaders(this.defaultHeaders,s?.headers);s?.bearerToken?r.Authorization=`Bearer ${s.bearerToken}`:s?.anonId&&(r["x-looplay-anon-id"]=s.anonId);let a;s?.body!==void 0&&(r["Content-Type"]=r["Content-Type"]??"application/json",a=JSON.stringify(s.body));let o=await this.fetchFn(n,{method:e,headers:r,body:a});if(!o.ok){let L=await this.safeText(o);throw new k(`HTTP ${o.status} ${o.statusText}`,o.status,L)}let I=await this.safeText(o);if(I)return JSON.parse(I)}mergeHeaders(...e){let t={};for(let s of e)if(s)for(let[n,r]of Object.entries(s))r!==void 0&&(t[n]=r);return t}buildUrl(e,t){let s=e.startsWith("/")?e:`/${e}`,n=new URL(`${this.baseUrl}${s}`);if(t)for(let[r,a]of Object.entries(t))a!==void 0&&n.searchParams.set(r,String(a));return n.toString()}async safeText(e){try{return await e.text()}catch{return ""}}};var c=class{http;constructor(e){this.http=new f(e);}async telegramLogin(e){let t={telegramInitData:e};return this.http.request("POST","/account/telegram-login",{body:t})}async refresh(e){let t={refreshToken:e};return this.http.request("POST","/account/refresh",{body:t})}async logout(e){let t={refreshToken:e};return this.http.request("POST","/account/logout",{body:t})}async recordGameView(e,t={}){return await this.http.request("POST","/sdk/games/views/record",{...t,headers:{"x-game-key":e,...this.trackingHeaders(t)}}),true}async startGameplayAttempt(e,t={},s={}){return this.http.request("POST","/sdk/games/attempts/start",{...t,headers:{"x-game-key":e,...this.trackingHeaders(t)},body:s})}async completeGameplayAttempt(e,t,s){return await this.http.request("POST","/sdk/games/attempts/complete",{...t,headers:{"x-game-key":e,...this.trackingHeaders(t)},body:s}),true}async completeGameplayMatch(e,t,s){return await this.http.request("POST","/sdk/games/matches/complete",{...t,headers:{"x-game-key":e,...this.trackingHeaders(t)},body:s}),true}async recordGameplayAction(e,t,s){return this.http.request("POST","/sdk/games/actions/record",{...t,headers:{"x-game-key":e,...this.trackingHeaders(t)},body:s})}async bootstrapTrackingSession(e){return this.http.request("POST","/sdk/games/tracking/bootstrap",{headers:{"x-looplay-anon-id":e}})}trackingHeaders(e){return {"x-game-tracking-session-id":e.trackingSessionId,"x-looplay-anon-token":e.anonTrackingToken}}async getStore(e){return this.http.request("GET","/sdk/games/store/current",{headers:{"x-game-key":e}})}async listGameAssets(e){return this.http.request("GET","/sdk/games/store/assets",{headers:{"x-game-key":e}})}async listMyGameAssets(e,t){return this.http.request("GET","/sdk/games/store/assets/me",{bearerToken:e,headers:{"x-game-key":t}})}async listMyStorePurchases(e,t){return this.http.request("GET","/sdk/games/store/purchases/me",{bearerToken:e,headers:{"x-game-key":t}})}async getBalances(e,t){return this.http.request("GET","/sdk/balance",{bearerToken:e,query:t})}async getBalanceHistory(e,t){return this.http.request("GET","/sdk/balance/history",{bearerToken:e,query:t})}async getMyProfile(e){return this.http.request("GET","/sdk/user/profile",{bearerToken:e})}async getMyBalance(e){return this.http.request("GET","/sdk/user/balance",{bearerToken:e})}async listReferrals(e,t){return this.http.request("GET","/sdk/referrals",{bearerToken:e,query:t})}async setReferral(e,t){return this.http.request("POST","/sdk/referrals/set",{bearerToken:e,body:t})}async listTasks(e,t){return this.http.request("GET","/sdk/task",{bearerToken:e,query:{...t}})}async listFinishedTasks(e,t){return this.http.request("GET","/sdk/task/finished",{bearerToken:e,query:{...t}})}async startTask(e,t){return this.http.request("POST","/sdk/task/start",{bearerToken:e,body:t})}async claimTask(e,t){return this.http.request("POST","/sdk/task/claim",{bearerToken:e,body:t})}};function G(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let e=Math.random()*16|0;return (i==="x"?e:e&3|8).toString(16)})}var h=class{raw;getAccessToken;getAnonymousId;trackingSession;trackingSessionPromise;currentAttemptId;gameplayAttempt;gameplayAttemptPromise;constructor(e){if(!e.baseUrl)throw new l;this.raw=new c({baseUrl:e.baseUrl,fetch:e.fetch,defaultHeaders:e.defaultHeaders}),this.getAccessToken=e.getAccessToken,this.getAnonymousId=e.getAnonymousId;}unsafeRaw(){return this.raw}async requireToken(){let e=await this.getAccessToken?.();if(!e)throw new m;return e}async resolveTrackingAuth(){let e=await this.getAccessToken?.(),t=e?void 0:this.getAnonymousId?.();if(!e&&!t)throw new m;let s=await this.ensureTrackingSession(t);return {bearerToken:e,anonId:t,trackingSessionId:s?.trackingSessionId,anonTrackingToken:s?.anonTrackingToken}}async ensureTrackingSession(e){let t=Date.now();return this.trackingSession&&this.trackingSession.expiresAtMs>t+3e4?this.trackingSession:(this.trackingSessionPromise||(this.trackingSessionPromise=this.raw.bootstrapTrackingSession(e).then(s=>(this.trackingSession={trackingSessionId:s.trackingSessionId,anonTrackingToken:s.trackingToken,expiresAtMs:new Date(s.expiresAt).getTime()},this.trackingSession)).catch(()=>{}).finally(()=>{this.trackingSessionPromise=void 0;})),this.trackingSessionPromise)}async ensureGameplayAttempt(e,t,s){let n=Date.now();return this.gameplayAttempt&&this.gameplayAttempt.gameKey===e&&this.gameplayAttempt.attemptId===s&&this.gameplayAttempt.expiresAtMs>n+1e4?this.gameplayAttempt:(this.gameplayAttemptPromise||(this.gameplayAttemptPromise=this.raw.startGameplayAttempt(e,t,{attemptId:s}).then(r=>(this.gameplayAttempt={gameKey:e,attemptId:r.attemptId,attemptToken:r.attemptToken,expiresAtMs:new Date(r.expiresAt).getTime()},this.gameplayAttempt)).finally(()=>{this.gameplayAttemptPromise=void 0;})),this.gameplayAttemptPromise)}async trackView(e){let t=await this.resolveTrackingAuth();return this.raw.recordGameView(e,t)}startAttempt(){return this.currentAttemptId=G(),this.gameplayAttempt=void 0,this.currentAttemptId}async trackPlay(e,t){let s=await this.resolveTrackingAuth(),n=this.currentAttemptId??this.startAttempt(),{attemptToken:r}=await this.ensureGameplayAttempt(e,s,n);return this.raw.completeGameplayAttempt(e,s,{playTimeSeconds:t,attemptId:n,attemptToken:r})}async trackMatch(e,t){let s=await this.resolveTrackingAuth(),n=this.currentAttemptId??this.startAttempt(),{attemptToken:r}=await this.ensureGameplayAttempt(e,s,n);return this.raw.completeGameplayMatch(e,s,{...t,attemptId:n,attemptToken:r})}async emit(e,t,s){let n=await this.resolveTrackingAuth(),r=this.currentAttemptId??this.startAttempt(),{attemptToken:a}=await this.ensureGameplayAttempt(e,n,r),o={attemptId:r,attemptToken:a,actionCode:t,value:s?.value,refId:s?.refId,payload:s?.payload};return this.raw.recordGameplayAction(e,n,o)}async getStore(e){return this.raw.getStore(e)}async listGameAssets(e){return this.raw.listGameAssets(e)}async listMyGameAssets(e){let t=await this.requireToken();return this.raw.listMyGameAssets(t,e)}async listMyStorePurchases(e){let t=await this.requireToken();return this.raw.listMyStorePurchases(t,e)}async getMyProfile(){let e=await this.requireToken();return this.raw.getMyProfile(e)}async getMyBalance(){let e=await this.requireToken();return this.raw.getMyBalance(e)}async getBalances(e){let t=await this.requireToken();return this.raw.getBalances(t,e)}async getBalanceHistory(e){let t=await this.requireToken();return this.raw.getBalanceHistory(t,e)}async listReferrals(e){let t=await this.requireToken();return this.raw.listReferrals(t,e)}async setReferral(e){let t=await this.requireToken();return this.raw.setReferral(t,e)}async listTasks(e){let t=await this.requireToken();return this.raw.listTasks(t,e)}async listFinishedTasks(e){let t=await this.requireToken();return this.raw.listFinishedTasks(t,e)}async startTask(e){let t=await this.requireToken();return this.raw.startTask(t,e)}async claimTask(e){let t=await this.requireToken();return this.raw.claimTask(t,e)}};var x=class{auth;api;gameId;initialized=false;constructor(e={}){let{auth:t,baseUrl:s,fetch:n,defaultHeaders:r}=e;t&&(this.auth=new y(t)),s&&(this.api=new h({baseUrl:s,fetch:n,defaultHeaders:r,getAccessToken:async()=>this.auth?.getAccessToken()}));}async init(e){this.gameId=e.gameId,await this.auth?.init(),await this.verifyGameId(e),this.initialized=true;}async getAccessToken(){return this.auth?.getAccessToken()}getGameId(){return this.gameId}async getMyProfile(){if(this.assertInitialized(),!this.api)throw new l;return this.api.getMyProfile()}async trackView(){if(this.assertInitialized(),!this.api)throw new l;if(!this.gameId)throw new d;return this.api.trackView(this.gameId)}startAttempt(){if(this.assertInitialized(),!this.api)throw new l;return this.api.startAttempt()}async trackPlay(e){if(this.assertInitialized(),!this.api)throw new l;if(!this.gameId)throw new d;return this.api.trackPlay(this.gameId,e)}async trackMatch(e,t){if(this.assertInitialized(),!this.api)throw new l;if(!this.gameId)throw new d;return this.api.trackMatch(this.gameId,{matchId:e,matchDurationSeconds:t.durationSeconds,isCompleted:t.isCompleted,isWin:t.isWin})}async emit(e,t){if(this.assertInitialized(),!this.api)throw new l;if(!this.gameId)throw new d;return this.api.emit(this.gameId,e,t)}assertInitialized(){if(!this.initialized)throw new d}async verifyGameId(e){if((e.verifyMode??"none")!=="none"){if(!this.api)throw new l;await this.api.listGameAssets(e.gameId);}}};function O(i){if(!i)return;let e=i.split(".");if(!(e.length<2))try{let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),s=t+"===".slice((t.length+3)%4);if(typeof globalThis.atob!="function")return;let n=globalThis.atob(s);return JSON.parse(n)}catch{return}}function w(i){let e=O(i)?.exp;return typeof e=="number"?e*1e3:void 0}var T=class extends u{constructor(){super("Missing Telegram init data"),this.name="MissingTelegramInitDataError";}},v=class extends u{constructor(){super("Missing refresh token"),this.name="MissingRefreshTokenError";}},b=class{id="telegram";client;getTelegramInitData;mapUser;session=null;constructor(e){this.client=new c(e),this.getTelegramInitData=e.getTelegramInitData,this.mapUser=e.mapUser;}onSessionChanged=e=>{this.session=e;};async login(e){let t=e?.telegramInitData??this.getTelegramInitData?.()??globalThis?.Telegram?.WebApp?.initData;if(!t)throw new T;let s=await this.client.telegramLogin(t),n=s.user??{},a={user:this.mapUser?this.mapUser(n):this.defaultMapUser(n),accessToken:s.accessToken,refreshToken:s.refreshToken,expiresAt:w(s.accessToken)};return this.session=a,{session:a}}async refresh(){let e=this.session,t=e?.refreshToken;if(!e||!t)throw new v;let s=await this.client.refresh(t),n={accessToken:s.accessToken,refreshToken:s.refreshToken,expiresAt:w(s.accessToken)};return this.session={...e,...n},n}async logout(){let e=this.session?.refreshToken;if(e)try{await this.client.logout(e);}catch{}this.session=null;}defaultMapUser(e){let t=e.id??e._id??e.userId??e.telegramId;if(t==null||String(t).trim()==="")throw new u("Missing user id from auth response");return {id:String(t),displayName:e.displayName??e.username??e.name,avatarUrl:e.avatarUrl??e.photoUrl??e.avatar}}};var E="looplay:anon-id",U=4e3,M=5e3,D={jwt:null,gameId:null,apiUrl:null,anonymousId:null,origin:null,receivedAt:null},C=i=>typeof i=="object"&&i!==null,p=i=>{if(typeof i!="string")return null;let e=i.trim();return e||null};function _(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let e=Math.random()*16|0;return (i==="x"?e:e&3|8).toString(16)})}function F(i){if(typeof window>"u"||!window.localStorage)return null;try{return window.localStorage.getItem(i)}catch{return null}}function H(i,e){if(!(typeof window>"u"||!window.localStorage))try{window.localStorage.setItem(i,e);}catch{}}var R=class{state=D;initialized=false;lifecycleTrackingInitialized=false;lastGameStartedKey=null;client=null;listeners=new Set;options;messageListener=null;anonymousFallbackTimer=null;pendingAdsRequests=new Map;requestAdsSequence=0;adsRequestInFlight=false;adsRequestsSent=0;lastAdsRequestAt=0;autoAdsActionCount=0;constructor(e={}){this.options=e;}getState(){return this.state}getJwt(){return this.state.jwt}getApiUrl(){return this.options.apiUrl??this.state.apiUrl}getGameId(){return this.options.appId??this.state.gameId}getAnonymousId(){return this.state.anonymousId}canTrack(){return !this.getApiUrl()||!this.getGameId()?false:!!(this.state.jwt||this.state.anonymousId)}subscribe(e){return this.listeners.add(e),e(this.state),()=>this.listeners.delete(e)}requestAuth(){if(typeof window>"u"||window.parent===window){this.debug("standalone mode detected; auth request skipped");return}this.debug("requesting auth from parent",{targetOrigin:this.resolveParentOrigin()??"*"}),window.parent.postMessage({type:"LOOPLAY_AUTH_REQUEST"},this.resolveParentOrigin()??"*");}requestAds(e={}){if(typeof window>"u"||window.parent===window)return this.debug("requestAds skipped; standalone mode detected"),Promise.resolve(false);if(this.adsRequestInFlight)return this.debug("requestAds skipped; another ad request is already in flight"),Promise.resolve(false);let t=this.resolveParentOrigin()??"*",s=`ads-${Date.now()}-${++this.requestAdsSequence}`,n=e.timeoutMs??M;return this.adsRequestInFlight=true,this.adsRequestsSent+=1,this.lastAdsRequestAt=Date.now(),this.debug("requesting rewarded ad from parent",{requestId:s,targetOrigin:t,gameId:this.getGameId()}),new Promise(r=>{let a=setTimeout(()=>{this.pendingAdsRequests.delete(s),this.adsRequestInFlight=false,this.debug("requestAds timed out",{requestId:s,timeoutMs:n}),r(false);},n);this.pendingAdsRequests.set(s,{resolve:r,timer:a}),window.parent.postMessage({type:"LOOPLAY_REQUEST_ADS",requestId:s,gameId:this.getGameId()},t);})}init(){if(this.initialized||typeof window>"u")return;this.initialized=true;let e=window.parent!==window;if(this.debug("auth listener initialized",{isIframe:e,parentOrigin:this.resolveParentOrigin()}),!e){this.activateAnonymousMode();return}let t=this.resolveParentOrigin();t||console.warn("[LooplaySDK] LooplayIframeAuth could not resolve a parent origin to verify postMessage against (document.referrer is empty). Auth messages from window.parent will be accepted regardless of origin. Pass `parentOrigin` explicitly to harden this."),this.messageListener=s=>{if(s.source===window.parent){if(t&&s.origin!==t){this.debug("ignored message from unexpected origin",{expectedOrigin:t,receivedOrigin:s.origin});return}this.handleIncomingMessage(s.data,s.origin);}},window.addEventListener("message",this.messageListener),this.anonymousFallbackTimer=setTimeout(()=>{this.state.jwt||this.activateAnonymousMode();},this.options.anonymousFallbackTimeoutMs??U),this.requestAuth();}dispose(){typeof window<"u"&&this.messageListener&&window.removeEventListener("message",this.messageListener),this.anonymousFallbackTimer!==null&&clearTimeout(this.anonymousFallbackTimer);for(let e of this.pendingAdsRequests.values())e.timer!==null&&clearTimeout(e.timer),e.resolve(false);this.pendingAdsRequests.clear(),this.messageListener=null,this.anonymousFallbackTimer=null,this.initialized=false,this.lifecycleTrackingInitialized=false,this.listeners.clear();}getClient(){if(this.client)return this.client;let e=this.getApiUrl();return this.debug("creating sdk ApiClient",{baseUrl:e}),this.client=new h({baseUrl:e??void 0,getAccessToken:async()=>this.getJwt()??void 0,getAnonymousId:()=>this.getAnonymousId()??void 0}),this.client}trackView(){let e=this.getGameId();return !this.canTrack()||!e?(this.debug("trackView skipped; auth is not ready",{hasGameId:!!e}),Promise.resolve(false)):(this.debug("trackView dispatched",{gameId:e}),this.getClient().trackView(e))}startAttempt(){return this.getClient().startAttempt()}trackPlay(e){let t=this.getGameId();return !this.canTrack()||!t?(this.debug("trackPlay skipped; auth is not ready",{hasGameId:!!t}),Promise.resolve(false)):(this.debug("trackPlay dispatched",{gameId:t,playTimeSeconds:e}),this.getClient().trackPlay(t,e))}trackMatch(e,t){let s=this.getGameId();if(!this.canTrack()||!s)return this.debug("trackMatch skipped; auth is not ready",{matchId:e}),Promise.resolve(false);this.debug("trackMatch dispatched",{gameId:s,matchId:e,...t});let n=this.getClient().trackMatch(s,{matchId:e,matchDurationSeconds:t.durationSeconds,isCompleted:t.isCompleted,isWin:t.isWin});return n.then(r=>{r&&this.recordAutoRequestAdsActivity();}).catch(()=>{}),n}emitGameEvent(e,t){let s=this.getGameId();return !this.canTrack()||!s?(this.debug("emitGameEvent skipped; auth is not ready",{actionCode:e}),Promise.resolve(null)):(this.debug("emitGameEvent dispatched",{gameId:s,actionCode:e,...t}),this.getClient().emit(s,e,t))}initLifecycleTracking(){this.lifecycleTrackingInitialized||typeof window>"u"||(this.lifecycleTrackingInitialized=true,this.debug("lifecycle tracking initialized"),this.subscribe(()=>{let e=this.getTrackingKey();!e||this.lastGameStartedKey===e||(this.lastGameStartedKey=e,this.debug("auto GAME_STARTED dispatch"),this.emitGameEvent("GAME_STARTED"));}));}getTrackingKey(){let e=this.getGameId(),t=this.getApiUrl(),s=this.state.jwt??this.state.anonymousId;return !e||!t||!s?null:`${t}
|
|
2
2
|
${e}
|
|
3
|
-
${s}`}activateAnonymousMode(){if(this.state.anonymousId)return;let e=this.options.anonymousIdStorageKey??
|
|
3
|
+
${s}`}activateAnonymousMode(){if(this.state.anonymousId)return;let e=this.options.anonymousIdStorageKey??E,t=F(e)??_();H(e,t),this.lastGameStartedKey=null,this.setState({...this.state,anonymousId:t,receivedAt:Date.now()}),this.debug("anonymous tracking mode active",{anonymousId:t});}handleAuthMessage(e,t){this.anonymousFallbackTimer!==null&&(clearTimeout(this.anonymousFallbackTimer),this.anonymousFallbackTimer=null);let s=p(e.jwt),n=p(e.gameId),r=p(e.apiUrl),a=n??this.getGameId(),o=r??this.getApiUrl();if(!a||!o){this.lastGameStartedKey=null,this.setState({...this.state,jwt:null,gameId:null,apiUrl:null,origin:t,receivedAt:Date.now()}),this.debug("auth message missing game identity; falling back to anonymous tracking"),this.activateAnonymousMode();return}if(!s){this.lastGameStartedKey=null,this.setState({...this.state,jwt:null,gameId:a,apiUrl:o,origin:t,receivedAt:Date.now()}),this.debug("auth message missing jwt; falling back to anonymous tracking",{gameId:a,apiUrl:o}),this.activateAnonymousMode();return}this.lastGameStartedKey=null,this.setState({jwt:s,gameId:n,apiUrl:r,anonymousId:null,origin:t,receivedAt:Date.now()});}handleRequestAdsResult(e){if(e.type!=="LOOPLAY_ADS_WATCHED")return;let t=p(e.requestId);if(!t)return;let s=this.pendingAdsRequests.get(t);s&&(this.pendingAdsRequests.delete(t),s.timer!==null&&clearTimeout(s.timer),this.adsRequestInFlight=false,this.debug("requestAds watched message received",{requestId:t}),s.resolve(true));}handleRequestAdsUnavailable(e){if(e.type!=="LOOPLAY_ADS_UNAVAILABLE")return;let t=p(e.requestId);if(!t)return;let s=this.pendingAdsRequests.get(t);s&&(this.pendingAdsRequests.delete(t),s.timer!==null&&clearTimeout(s.timer),this.adsRequestInFlight=false,this.debug("requestAds unavailable message received",{requestId:t}),s.resolve(false));}handleIncomingMessage(e,t){if(C(e)){if(e.type==="LOOPLAY_AUTH"){this.handleAuthMessage(e,t);return}if(e.type==="LOOPLAY_ADS_WATCHED"){this.handleRequestAdsResult(e);return}if(e.type==="LOOPLAY_ADS_UNAVAILABLE"){this.handleRequestAdsUnavailable(e);return}}}setState(e){this.state=e,this.client=null,this.debug("auth state updated",{gameId:this.getGameId(),apiUrl:this.getApiUrl(),origin:e.origin,hasJwt:!!e.jwt,hasAnonymousId:!!e.anonymousId}),this.listeners.forEach(t=>t(e));}resolveParentOrigin(){if(this.options.parentOrigin)return this.options.parentOrigin;if(typeof document>"u"||!document.referrer)return null;try{return new URL(document.referrer).origin}catch{return null}}debug(e,t){this.options.onDebug?.(e,t);}recordAutoRequestAdsActivity(){this.autoAdsActionCount+=1;let e=this.options.autoRequestAds;if(!e?.enabled||this.adsRequestInFlight||!this.canTrack())return;let t=Date.now();if(e.cooldownMs&&t-this.lastAdsRequestAt<e.cooldownMs||e.maxRequestsPerSession&&this.adsRequestsSent>=e.maxRequestsPerSession)return;let s=e.afterActionCount??0;if(!(s>0&&this.autoAdsActionCount%s===0))return;let r=e.chance??1;if(r<=0||Math.random()>r){this.debug("auto requestAds skipped by chance",{actionCount:this.autoAdsActionCount,chance:r});return}this.debug("auto requestAds triggered",{actionCount:this.autoAdsActionCount}),this.requestAds();}};exports.ApiClient=h;exports.BrowserLocalStorageAuthStorage=P;exports.HttpClient=f;exports.HttpError=k;exports.LooplayAuth=y;exports.LooplayIframeAuth=R;exports.LooplaySDK=x;exports.LooplaySDKError=u;exports.MemoryAuthStorage=g;exports.MissingAuthError=S;exports.MissingBaseUrlError=l;exports.NotAuthenticatedError=m;exports.NotInitializedError=d;exports.ServiceClient=c;exports.TelegramAuthProvider=b;return exports;})({});//# sourceMappingURL=looplay-sdk.min.js.map
|
|
4
4
|
//# sourceMappingURL=looplay-sdk.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/auth/storage.ts","../src/auth/looplay-auth.ts","../src/apps/http.ts","../src/apps/service-client.ts","../src/apps/api-client.ts","../src/looplay-sdk.ts","../src/auth/jwt.ts","../src/auth/providers/telegram-auth-provider.ts","../src/iframe/iframe-auth.ts"],"names":["LooplaySDKError","message","NotInitializedError","MissingBaseUrlError","MissingAuthError","NotAuthenticatedError","MemoryAuthStorage","key","value","BrowserLocalStorageAuthStorage","InvalidSessionError","LooplayAuth","options","params","result","now","refreshed","next","eventName","listener","raw","parsed","event","getFetchFn","fetchOverride","HttpError","status","bodyText","HttpClient","method","path","url","headers","body","res","text","sources","src","query","normalizedPath","ServiceClient","telegramInitData","refreshToken","gameId","bearerToken","auth","attemptId","playTimeSeconds","anonId","assetCode","generateId","c","r","ApiClient","token","session","dto","actionCode","opts","LooplaySDK","baseUrl","fetch","defaultHeaders","matchId","decodeJwtClaims","parts","payload","padded","json","decodeJwtExpMs","exp","MissingTelegramInitDataError","MissingRefreshTokenError","TelegramAuthProvider","initData","rawUser","currentSession","id","DEFAULT_ANON_ID_STORAGE_KEY","DEFAULT_ANONYMOUS_FALLBACK_TIMEOUT_MS","DEFAULT_REQUEST_ADS_TIMEOUT_MS","INITIAL_STATE","isRecord","toOptionalString","normalized","generateAnonymousId","readStoredAnonymousId","writeStoredAnonymousId","LooplayIframeAuth","parentOrigin","requestId","timeoutMs","resolve","timer","isEmbedded","pending","ok","apiUrl","identity","anonymousId","data","origin","jwt","resolvedGameId","resolvedApiUrl","details","config","threshold","chance"],"mappings":"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","file":"looplay-sdk.min.js","sourcesContent":["export class LooplaySDKError extends Error {\n constructor(message?: string) {\n super(message);\n this.name = 'LooplaySDKError';\n }\n}\n\nexport class NotInitializedError extends LooplaySDKError {\n constructor() {\n super('LooplaySDK not initialized. Call init({ gameId }) first.');\n this.name = 'NotInitializedError';\n }\n}\n\nexport class MissingBaseUrlError extends LooplaySDKError {\n constructor() {\n super('Missing baseUrl. Provide `baseUrl` to enable HTTP integration.');\n this.name = 'MissingBaseUrlError';\n }\n}\n\nexport class MissingAuthError extends LooplaySDKError {\n constructor() {\n super('Missing auth. Provide `auth` options to enable user login/session.');\n this.name = 'MissingAuthError';\n }\n}\n\nexport class NotAuthenticatedError extends LooplaySDKError {\n constructor() {\n super('Not authenticated. Login is required for this operation.');\n this.name = 'NotAuthenticatedError';\n }\n}\n","import type { AuthStorage } from './types';\n\nexport class MemoryAuthStorage implements AuthStorage {\n private readonly map = new Map<string, string>();\n\n async getItem(key: string): Promise<string | null> {\n return this.map.has(key) ? (this.map.get(key) as string) : null;\n }\n\n async setItem(key: string, value: string): Promise<void> {\n this.map.set(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n this.map.delete(key);\n }\n}\n\nexport class BrowserLocalStorageAuthStorage implements AuthStorage {\n async getItem(key: string): Promise<string | null> {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n return window.localStorage.getItem(key);\n }\n\n async setItem(key: string, value: string): Promise<void> {\n if (typeof window === 'undefined' || !window.localStorage) return;\n window.localStorage.setItem(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n if (typeof window === 'undefined' || !window.localStorage) return;\n window.localStorage.removeItem(key);\n }\n}\n","import { LooplaySDKError } from '../errors';\nimport {\n type AuthEventName,\n type AuthLoginParams,\n type AuthProvider,\n type AuthStorage,\n type LooplayAuthOptions,\n type LooplaySession\n} from './types';\nimport { MemoryAuthStorage } from './storage';\n\nclass InvalidSessionError extends LooplaySDKError {\n constructor() {\n super('Invalid session returned by auth provider');\n this.name = 'InvalidSessionError';\n }\n}\n\ntype Listener = (session: LooplaySession | null) => void;\n\nexport class LooplayAuth {\n private readonly provider: AuthProvider;\n private readonly storage: AuthStorage;\n private readonly key: string;\n\n private session: LooplaySession | null = null;\n private readonly listeners = new Set<Listener>();\n\n constructor(options: LooplayAuthOptions) {\n this.provider = options.provider;\n this.storage = options.storage ?? new MemoryAuthStorage();\n this.key = `${options.storageKeyPrefix ?? 'looplay'}:auth:session`;\n }\n\n async init(): Promise<void> {\n await this.provider.init?.();\n await this.restoreFromStorage();\n\n // Keep provider in sync with restored session (if any).\n await this.provider.onSessionChanged?.(this.session);\n }\n\n getSession(): LooplaySession | null {\n return this.session;\n }\n\n async login(params?: AuthLoginParams): Promise<LooplaySession> {\n const result = await this.provider.login(params);\n\n if (!result?.session?.user?.id) {\n throw new InvalidSessionError();\n }\n\n await this.setSession(result.session, 'login');\n return result.session;\n }\n\n async logout(): Promise<void> {\n await this.provider.logout();\n await this.setSession(null, 'logout');\n }\n\n async getAccessToken(): Promise<string | undefined> {\n if (!this.session) return undefined;\n\n const now = Date.now();\n const isExpired =\n typeof this.session.expiresAt === 'number' && this.session.expiresAt <= now;\n\n if (!isExpired) return this.session.accessToken;\n\n if (!this.provider.refresh) return this.session.accessToken;\n\n const refreshed = await this.provider.refresh();\n const next = { ...this.session, ...refreshed };\n await this.setSession(next, 'sessionChanged');\n\n return next.accessToken;\n }\n\n on(eventName: AuthEventName, listener: Listener): () => void {\n // eventName reserved for future filtering; for now notify on any change\n void eventName;\n\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private async restoreFromStorage(): Promise<void> {\n const raw = await this.storage.getItem(this.key);\n if (!raw) return;\n\n try {\n const parsed = JSON.parse(raw) as LooplaySession;\n if (parsed?.user?.id) {\n this.session = parsed;\n }\n } catch {\n // ignore corrupted cache\n await this.storage.removeItem(this.key);\n }\n }\n\n private async setSession(next: LooplaySession | null, event: AuthEventName): Promise<void> {\n this.session = next;\n\n await this.provider.onSessionChanged?.(this.session);\n\n if (next) {\n await this.storage.setItem(this.key, JSON.stringify(next));\n } else {\n await this.storage.removeItem(this.key);\n }\n\n for (const listener of this.listeners) {\n listener(this.session);\n }\n\n // reserved for future event-specific hooks\n void event;\n }\n}\n","export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nexport interface HttpClientOptions {\n baseUrl: string;\n fetch?: FetchLike;\n defaultHeaders?: Record<string, string>;\n}\n\nfunction getFetchFn(fetchOverride?: FetchLike): FetchLike {\n if (fetchOverride) {\n if (typeof window !== 'undefined' && fetchOverride === window.fetch) {\n return window.fetch.bind(window);\n }\n\n return fetchOverride;\n }\n\n return globalThis.fetch.bind(globalThis);\n}\n\nexport class HttpError extends Error {\n readonly status: number;\n readonly bodyText?: string;\n\n constructor(message: string, status: number, bodyText?: string) {\n super(message);\n this.name = 'HttpError';\n this.status = status;\n this.bodyText = bodyText;\n }\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly fetchFn: FetchLike;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.fetchFn = getFetchFn(options.fetch);\n this.defaultHeaders = options.defaultHeaders ?? {};\n }\n\n async request<T>(\n method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE',\n path: string,\n options?: {\n query?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string | undefined>;\n bearerToken?: string;\n /** Anonymous device id, used when there is no `bearerToken` (no logged-in user). */\n anonId?: string;\n body?: unknown;\n }\n ): Promise<T> {\n const url = this.buildUrl(path, options?.query);\n\n const headers: Record<string, string> = this.mergeHeaders(\n this.defaultHeaders,\n options?.headers\n );\n\n if (options?.bearerToken) {\n headers.Authorization = `Bearer ${options.bearerToken}`;\n } else if (options?.anonId) {\n headers['x-looplay-anon-id'] = options.anonId;\n }\n\n let body: BodyInit | undefined;\n if (options?.body !== undefined) {\n headers['Content-Type'] = headers['Content-Type'] ?? 'application/json';\n body = JSON.stringify(options.body);\n }\n\n const res = await this.fetchFn(url, { method, headers, body });\n\n if (!res.ok) {\n const text = await this.safeText(res);\n throw new HttpError(`HTTP ${res.status} ${res.statusText}`, res.status, text);\n }\n\n // Some endpoints may return empty body.\n const text = await this.safeText(res);\n if (!text) return undefined as T;\n\n return JSON.parse(text) as T;\n }\n\n private mergeHeaders(\n ...sources: Array<Record<string, string | undefined> | undefined>\n ): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const src of sources) {\n if (!src) continue;\n for (const [key, value] of Object.entries(src)) {\n if (value === undefined) continue;\n result[key] = value;\n }\n }\n\n return result;\n }\n\n private buildUrl(\n path: string,\n query?: Record<string, string | number | boolean | undefined>\n ): string {\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(`${this.baseUrl}${normalizedPath}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n }\n\n private async safeText(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return '';\n }\n }\n}\n","import { HttpClient, type HttpClientOptions } from './http';\nimport type {\n AuthResultDto,\n EmitGameEventRequest,\n EmitGameEventResponse,\n GameAssetDto,\n GameAssetPurchaseResultDto,\n GameTrackingSessionDto,\n PagingResponse,\n PurchaseGameAssetRequest,\n RefreshTokenRequest,\n TelegramLoginRequest,\n TrackGamePlayRequest,\n TrackMatchEndRequest,\n} from './LooplaySDK.types';\n\nexport interface ServiceClientOptions extends HttpClientOptions {}\n\n/**\n * Auth for the tracking endpoints (`view`/`play`/`end`/`emit`), which accept\n * either a real user's `bearerToken` (full tracking, eligible for\n * quest/reward) or an `anonId` for an unauthenticated device\n * (analytics-only — the backend never awards quest/reward progress to an\n * anonymous id). `trackingSessionId`/`anonTrackingToken` come from\n * `issueTrackingSession()` and should be attached either way, per the\n * platform's tracking contract.\n */\nexport interface TrackingAuth {\n bearerToken?: string;\n anonId?: string;\n trackingSessionId?: string;\n anonTrackingToken?: string;\n}\n\n/**\n * Low-level HTTP client that maps 1:1 to backend routes.\n * Methods requiring user auth accept `bearerToken` explicitly.\n */\nexport class ServiceClient {\n private readonly http: HttpClient;\n\n constructor(options: ServiceClientOptions) {\n this.http = new HttpClient(options);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Auth\n // ─────────────────────────────────────────────────────────────────────────────\n\n async telegramLogin(telegramInitData: string): Promise<AuthResultDto> {\n const body: TelegramLoginRequest = { telegramInitData };\n return this.http.request('POST', '/account/telegram-login', { body });\n }\n\n async refresh(refreshToken: string): Promise<AuthResultDto> {\n const body: RefreshTokenRequest = { refreshToken };\n return this.http.request('POST', '/account/refresh', { body });\n }\n\n async logout(refreshToken: string): Promise<boolean> {\n const body: RefreshTokenRequest = { refreshToken };\n return this.http.request('POST', '/account/logout', { body });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Games\n // ─────────────────────────────────────────────────────────────────────────────\n\n async listGames(query?: Record<string, any>): Promise<PagingResponse<any>> {\n return this.http.request('GET', '/sdk/games', { query });\n }\n\n async getGameDetail(gameId: string, bearerToken?: string): Promise<any> {\n return this.http.request('GET', `/sdk/games/${encodeURIComponent(gameId)}`, {\n bearerToken,\n });\n }\n\n async listRecentPlayed(\n bearerToken: string,\n query?: Record<string, any>\n ): Promise<PagingResponse<any>> {\n return this.http.request('GET', '/sdk/games/recent-play', { bearerToken, query });\n }\n\n async trackView(auth: TrackingAuth, gameId: string): Promise<boolean> {\n await this.http.request('POST', `/sdk/games/${encodeURIComponent(gameId)}/view`, {\n ...auth,\n headers: this.trackingHeaders(auth),\n });\n return true;\n }\n\n async trackPlay(\n auth: TrackingAuth,\n gameId: string,\n attemptId: string,\n playTimeSeconds?: number\n ): Promise<boolean> {\n const body: TrackGamePlayRequest = { playTimeSeconds, attemptId };\n await this.http.request('POST', `/sdk/games/${encodeURIComponent(gameId)}/play`, {\n ...auth,\n headers: this.trackingHeaders(auth),\n body,\n });\n return true;\n }\n\n async trackMatchEnd(\n auth: TrackingAuth,\n gameId: string,\n body: TrackMatchEndRequest\n ): Promise<boolean> {\n await this.http.request('POST', `/sdk/games/${encodeURIComponent(gameId)}/end`, {\n ...auth,\n headers: this.trackingHeaders(auth),\n body,\n });\n return true;\n }\n\n async emitGameEvent(\n auth: TrackingAuth,\n gameId: string,\n body: EmitGameEventRequest\n ): Promise<EmitGameEventResponse> {\n return this.http.request('POST', `/sdk/games/${encodeURIComponent(gameId)}/emit`, {\n ...auth,\n headers: this.trackingHeaders(auth),\n body,\n });\n }\n\n /**\n * Anonymous runtimes must call this before any tracking call; logged-in\n * runtimes should still call it to get a `trackingSessionId` for unified\n * tracing — see `GameTrackingSessionDto`.\n */\n async issueTrackingSession(anonId?: string): Promise<GameTrackingSessionDto> {\n return this.http.request('POST', '/sdk/games/tracking/bootstrap', {\n headers: { 'x-looplay-anon-id': anonId },\n });\n }\n\n private trackingHeaders(auth: TrackingAuth): Record<string, string | undefined> {\n return {\n 'x-game-tracking-session-id': auth.trackingSessionId,\n 'x-looplay-anon-token': auth.anonTrackingToken,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Game assets — spend coin balance on assets defined by the game's\n // creator. Coin balance itself comes from getBalances()/getMyBalance().\n // ─────────────────────────────────────────────────────────────────────────────\n\n async listGameAssets(gameId: string, bearerToken?: string): Promise<GameAssetDto[]> {\n return this.http.request('GET', `/sdk/games/${encodeURIComponent(gameId)}/assets`, {\n bearerToken,\n });\n }\n\n async purchaseGameAsset(\n bearerToken: string,\n gameId: string,\n assetCode: string,\n body: PurchaseGameAssetRequest = {}\n ): Promise<GameAssetPurchaseResultDto> {\n return this.http.request(\n 'POST',\n `/sdk/games/${encodeURIComponent(gameId)}/assets/${encodeURIComponent(assetCode)}/purchase`,\n { bearerToken, body }\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Balance\n // ─────────────────────────────────────────────────────────────────────────────\n\n async getBalances(bearerToken: string, query?: Record<string, any>): Promise<any> {\n return this.http.request('GET', '/sdk/balance', { bearerToken, query });\n }\n\n async getBalanceHistory(\n bearerToken: string,\n query?: Record<string, any>\n ): Promise<PagingResponse<any>> {\n return this.http.request('GET', '/sdk/balance/history', { bearerToken, query });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // User\n // ─────────────────────────────────────────────────────────────────────────────\n\n async getMyProfile(bearerToken: string): Promise<any> {\n return this.http.request('GET', '/sdk/user/profile', { bearerToken });\n }\n\n async getMyBalance(bearerToken: string): Promise<any> {\n return this.http.request('GET', '/sdk/user/balance', { bearerToken });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Referrals\n // ─────────────────────────────────────────────────────────────────────────────\n\n async listReferrals(\n bearerToken: string,\n query?: Record<string, any>\n ): Promise<PagingResponse<any>> {\n return this.http.request('GET', '/sdk/referrals', { bearerToken, query });\n }\n\n async setReferral(bearerToken: string, body: Record<string, any>): Promise<boolean> {\n return this.http.request('POST', '/sdk/referrals/set', { bearerToken, body });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tasks\n // ─────────────────────────────────────────────────────────────────────────────\n\n async listTasks(bearerToken: string): Promise<any[]> {\n return this.http.request('GET', '/sdk/task', { bearerToken });\n }\n\n async listFinishedTasks(bearerToken: string): Promise<any[]> {\n return this.http.request('GET', '/sdk/task/finished', { bearerToken });\n }\n\n async startTask(bearerToken: string, body: Record<string, any>): Promise<any> {\n return this.http.request('POST', '/sdk/task/start', { bearerToken, body });\n }\n\n async claimTask(bearerToken: string, body: Record<string, any>): Promise<any> {\n return this.http.request('POST', '/sdk/task/claim', { bearerToken, body });\n }\n}\n","import { MissingBaseUrlError, NotAuthenticatedError } from '../errors';\nimport type {\n EmitGameEventRequest,\n EmitGameEventResponse,\n GameAssetDto,\n GameAssetPurchaseResultDto,\n GameEventKey,\n PagingResponse,\n PurchaseGameAssetRequest,\n TrackMatchEndRequest,\n} from './LooplaySDK.types';\nimport { ServiceClient, type TrackingAuth } from './service-client';\nimport type { ServiceClientOptions } from './service-client';\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n\n // Fallback UUID v4 for environments without crypto.randomUUID. Not\n // cryptographically strong, but only used as a non-sensitive tracking id.\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\ninterface CachedTrackingSession {\n trackingSessionId: string;\n anonTrackingToken: string;\n expiresAtMs: number;\n}\n\nexport interface ApiClientOptions extends Omit<ServiceClientOptions, 'baseUrl'> {\n baseUrl?: string;\n getAccessToken?: () => Promise<string | undefined>;\n /**\n * Resolves an anonymous device id, used for the tracking endpoints\n * (`trackPlay`/`trackMatch`/`emit`) when `getAccessToken` yields nothing.\n * Anonymous tracking is analytics-only — see `TrackingAuth`.\n */\n getAnonymousId?: () => string | undefined;\n}\n\n/**\n * High-level API client that injects user auth automatically.\n */\nexport class ApiClient {\n private readonly raw: ServiceClient;\n private readonly getAccessToken?: () => Promise<string | undefined>;\n private readonly getAnonymousId?: () => string | undefined;\n private trackingSession?: CachedTrackingSession;\n private trackingSessionPromise?: Promise<CachedTrackingSession | undefined>;\n private currentAttemptId?: string;\n\n constructor(options: ApiClientOptions) {\n if (!options.baseUrl) throw new MissingBaseUrlError();\n\n this.raw = new ServiceClient({\n baseUrl: options.baseUrl,\n fetch: options.fetch,\n defaultHeaders: options.defaultHeaders,\n });\n\n this.getAccessToken = options.getAccessToken;\n this.getAnonymousId = options.getAnonymousId;\n }\n\n /** Expose the underlying route-level client (requires manual bearerToken passing). */\n unsafeRaw(): ServiceClient {\n return this.raw;\n }\n\n // Public endpoints\n async listGames(query?: Record<string, any>): Promise<PagingResponse<any>> {\n return this.raw.listGames(query);\n }\n\n // Authenticated endpoints\n private async requireToken(): Promise<string> {\n const token = await this.getAccessToken?.();\n if (!token) throw new NotAuthenticatedError();\n return token;\n }\n\n /**\n * Resolves auth for the tracking endpoints: a real bearer token if logged\n * in, otherwise an anonymous device id. Throws only when neither is\n * available — anonymous tracking still requires *some* identity. Also\n * attaches a `trackingSessionId`/`anonTrackingToken` (bootstrapped and\n * cached lazily) — the backend's tracking contract expects these even for\n * logged-in callers, for unified tracing.\n */\n private async resolveTrackingAuth(): Promise<TrackingAuth> {\n const bearerToken = await this.getAccessToken?.();\n const anonId = bearerToken ? undefined : this.getAnonymousId?.();\n\n if (!bearerToken && !anonId) throw new NotAuthenticatedError();\n\n const session = await this.ensureTrackingSession(anonId);\n\n return {\n bearerToken,\n anonId,\n trackingSessionId: session?.trackingSessionId,\n anonTrackingToken: session?.anonTrackingToken,\n };\n }\n\n /**\n * Bootstraps (and caches until near expiry) the platform tracking\n * session. Swallows failures — tracking calls still work without it,\n * just without unified session tracing.\n */\n private async ensureTrackingSession(\n anonId?: string\n ): Promise<CachedTrackingSession | undefined> {\n const now = Date.now();\n if (this.trackingSession && this.trackingSession.expiresAtMs > now + 30_000) {\n return this.trackingSession;\n }\n\n if (!this.trackingSessionPromise) {\n this.trackingSessionPromise = this.raw\n .issueTrackingSession(anonId)\n .then((dto) => {\n this.trackingSession = {\n trackingSessionId: dto.trackingSessionId,\n anonTrackingToken: dto.trackingToken,\n expiresAtMs: new Date(dto.expiresAt).getTime(),\n };\n return this.trackingSession;\n })\n .catch(() => undefined)\n .finally(() => {\n this.trackingSessionPromise = undefined;\n });\n }\n\n return this.trackingSessionPromise;\n }\n\n async getGameDetail(gameId: string): Promise<any> {\n const token = await this.getAccessToken?.();\n return this.raw.getGameDetail(gameId, token);\n }\n\n async listRecentPlayed(query?: Record<string, any>): Promise<PagingResponse<any>> {\n const token = await this.requireToken();\n return this.raw.listRecentPlayed(token, query);\n }\n\n /** Call once when the game view opens, before any play/match tracking. */\n async trackView(gameId: string): Promise<boolean> {\n const auth = await this.resolveTrackingAuth();\n return this.raw.trackView(auth, gameId);\n }\n\n /**\n * Starts a new play attempt and returns its id. Call when the user\n * presses Play; `trackPlay` reuses this id across calls until the next\n * `startAttempt()`, auto-starting one on first use if none was started.\n */\n startAttempt(): string {\n this.currentAttemptId = generateId();\n return this.currentAttemptId;\n }\n\n async trackPlay(gameId: string, playTimeSeconds?: number): Promise<boolean> {\n const auth = await this.resolveTrackingAuth();\n const attemptId = this.currentAttemptId ?? this.startAttempt();\n return this.raw.trackPlay(auth, gameId, attemptId, playTimeSeconds);\n }\n\n async trackMatch(gameId: string, body: TrackMatchEndRequest): Promise<boolean> {\n const auth = await this.resolveTrackingAuth();\n return this.raw.trackMatchEnd(auth, gameId, body);\n }\n\n async emit(\n gameId: string,\n actionCode: GameEventKey | string,\n opts?: { value?: number; refId?: string; payload?: Record<string, unknown> }\n ): Promise<EmitGameEventResponse> {\n const auth = await this.resolveTrackingAuth();\n const body: EmitGameEventRequest = {\n actionCode,\n value: opts?.value,\n refId: opts?.refId,\n payload: opts?.payload,\n };\n return this.raw.emitGameEvent(auth, gameId, body);\n }\n\n /** Lists assets purchasable in `gameId`, defined by that game's creator. */\n async listGameAssets(gameId: string): Promise<GameAssetDto[]> {\n const token = await this.getAccessToken?.();\n return this.raw.listGameAssets(gameId, token);\n }\n\n /** Spends coin balance to purchase an asset — see PurchaseGameAssetRequest for dedupe/quantity. */\n async purchaseGameAsset(\n gameId: string,\n assetCode: string,\n opts?: PurchaseGameAssetRequest\n ): Promise<GameAssetPurchaseResultDto> {\n const token = await this.requireToken();\n return this.raw.purchaseGameAsset(token, gameId, assetCode, opts);\n }\n\n async getMyProfile(): Promise<any> {\n const token = await this.requireToken();\n return this.raw.getMyProfile(token);\n }\n\n async getMyBalance(): Promise<any> {\n const token = await this.requireToken();\n return this.raw.getMyBalance(token);\n }\n\n async getBalances(query?: Record<string, any>): Promise<any> {\n const token = await this.requireToken();\n return this.raw.getBalances(token, query);\n }\n\n async getBalanceHistory(query?: Record<string, any>): Promise<PagingResponse<any>> {\n const token = await this.requireToken();\n return this.raw.getBalanceHistory(token, query);\n }\n\n async listReferrals(query?: Record<string, any>): Promise<PagingResponse<any>> {\n const token = await this.requireToken();\n return this.raw.listReferrals(token, query);\n }\n\n async setReferral(body: Record<string, any>): Promise<boolean> {\n const token = await this.requireToken();\n return this.raw.setReferral(token, body);\n }\n\n async listTasks(): Promise<any[]> {\n const token = await this.requireToken();\n return this.raw.listTasks(token);\n }\n\n async listFinishedTasks(): Promise<any[]> {\n const token = await this.requireToken();\n return this.raw.listFinishedTasks(token);\n }\n\n async startTask(body: Record<string, any>): Promise<any> {\n const token = await this.requireToken();\n return this.raw.startTask(token, body);\n }\n\n async claimTask(body: Record<string, any>): Promise<any> {\n const token = await this.requireToken();\n return this.raw.claimTask(token, body);\n }\n}\n","import type { LooplayInitParams, LooplaySDKCreateOptions } from './types';\nimport { LooplayAuth } from './auth/looplay-auth';\nimport { ApiClient } from './apps/api-client';\nimport type { EmitGameEventResponse, GameEventKey } from './apps/LooplaySDK.types';\nimport { MissingAuthError, MissingBaseUrlError, NotInitializedError } from './errors';\n\nexport class LooplaySDK {\n readonly auth?: LooplayAuth;\n readonly api?: ApiClient;\n\n private gameId?: string;\n private initialized = false;\n\n constructor(options: LooplaySDKCreateOptions = {}) {\n const { auth, baseUrl, fetch, defaultHeaders } = options;\n\n if (auth) {\n this.auth = new LooplayAuth(auth);\n }\n\n if (baseUrl) {\n this.api = new ApiClient({\n baseUrl,\n fetch,\n defaultHeaders,\n getAccessToken: async () => this.auth?.getAccessToken(),\n });\n }\n }\n\n async init(params: LooplayInitParams): Promise<void> {\n this.gameId = params.gameId;\n await this.auth?.init();\n await this.verifyGameId(params);\n this.initialized = true;\n }\n\n async getAccessToken(): Promise<string | undefined> {\n return this.auth?.getAccessToken();\n }\n\n getGameId(): string | undefined {\n return this.gameId;\n }\n\n async getMyProfile(): Promise<any> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n return this.api.getMyProfile();\n }\n\n /** Call once when the game view opens, before any play/match tracking. */\n async trackView(): Promise<boolean> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.trackView(this.gameId);\n }\n\n /** Starts a new play attempt; call when the user presses Play. Returns the attempt id. */\n startAttempt(): string {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n return this.api.startAttempt();\n }\n\n async trackPlay(playTimeSeconds?: number): Promise<boolean> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.trackPlay(this.gameId, playTimeSeconds);\n }\n\n /**\n * Report that a match/round ended.\n * Maps to PLAY_MATCH (and WIN_MATCH when isWin=true).\n * matchId is the idempotency anchor — safe to call twice with the same matchId.\n */\n async trackMatch(\n matchId: string,\n opts: { durationSeconds: number; isCompleted?: boolean; isWin?: boolean }\n ): Promise<boolean> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.trackMatch(this.gameId, {\n matchId,\n matchDurationSeconds: opts.durationSeconds,\n isCompleted: opts.isCompleted,\n isWin: opts.isWin,\n });\n }\n\n /**\n * Emit a custom action event (CUSTOM_ACTION metric).\n * Use GameEventKey for platform-defined codes; custom strings for game-specific quests.\n * Pass refId to make the call idempotent — same refId = same event, dedup-safe.\n */\n async emit(\n actionCode: GameEventKey | string,\n opts?: { value?: number; refId?: string; payload?: Record<string, unknown> }\n ): Promise<EmitGameEventResponse> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.emit(this.gameId, actionCode, opts);\n }\n\n private assertInitialized(): void {\n if (!this.initialized) throw new NotInitializedError();\n }\n\n private async verifyGameId(params: LooplayInitParams): Promise<void> {\n if ((params.verifyMode ?? 'none') === 'none') return;\n // strict: requires auth + calls GET /games/:id\n if (!this.auth) throw new MissingAuthError();\n if (!this.api) throw new MissingBaseUrlError();\n await this.api.getGameDetail(params.gameId);\n }\n}\n","export function decodeJwtClaims(token: string | undefined): Record<string, unknown> | undefined {\n if (!token) return undefined;\n const parts = token.split('.');\n if (parts.length < 2) return undefined;\n\n try {\n const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const padded = payload + '==='.slice((payload.length + 3) % 4);\n if (typeof (globalThis as any).atob !== 'function') return undefined;\n\n const json = (globalThis as any).atob(padded) as string;\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\nexport function decodeJwtExpMs(token: string | undefined): number | undefined {\n const exp = decodeJwtClaims(token)?.exp;\n return typeof exp === 'number' ? exp * 1000 : undefined;\n}\n","import { LooplaySDKError } from '../../errors';\nimport { ServiceClient, type ServiceClientOptions } from '../../apps/service-client';\nimport { decodeJwtExpMs } from '../jwt';\nimport type {\n AuthLoginParams,\n AuthLoginResult,\n AuthProvider,\n LooplaySession,\n LooplayUser\n} from '../types';\n\nexport interface TelegramAuthLoginParams extends AuthLoginParams {\n telegramInitData?: string;\n}\n\nexport interface TelegramAuthProviderOptions extends ServiceClientOptions {\n /**\n * Optional override for how to read Telegram init data.\n * If omitted, provider will try `params.telegramInitData` then `globalThis.Telegram.WebApp.initData`.\n */\n getTelegramInitData?: () => string | undefined;\n\n /**\n * Optional mapper from backend user payload to LooplayUser.\n * If omitted, provider will try common fields like id/_id/userId/telegramId.\n */\n mapUser?: (rawUser: Record<string, any>) => LooplayUser;\n}\n\nclass MissingTelegramInitDataError extends LooplaySDKError {\n constructor() {\n super('Missing Telegram init data');\n this.name = 'MissingTelegramInitDataError';\n }\n}\n\nclass MissingRefreshTokenError extends LooplaySDKError {\n constructor() {\n super('Missing refresh token');\n this.name = 'MissingRefreshTokenError';\n }\n}\n\nexport class TelegramAuthProvider implements AuthProvider {\n readonly id = 'telegram';\n\n private readonly client: ServiceClient;\n private readonly getTelegramInitData?: () => string | undefined;\n private readonly mapUser?: (rawUser: Record<string, any>) => LooplayUser;\n\n private session: LooplaySession | null = null;\n\n constructor(options: TelegramAuthProviderOptions) {\n this.client = new ServiceClient(options);\n this.getTelegramInitData = options.getTelegramInitData;\n this.mapUser = options.mapUser;\n }\n\n onSessionChanged = (session: LooplaySession | null): void => {\n this.session = session;\n };\n\n async login(params?: TelegramAuthLoginParams): Promise<AuthLoginResult> {\n const initData =\n params?.telegramInitData ??\n this.getTelegramInitData?.() ??\n (globalThis as any)?.Telegram?.WebApp?.initData;\n\n if (!initData) throw new MissingTelegramInitDataError();\n\n const result = await this.client.telegramLogin(initData);\n\n const rawUser = (result.user ?? {}) as Record<string, any>;\n const user = this.mapUser ? this.mapUser(rawUser) : this.defaultMapUser(rawUser);\n\n const session: LooplaySession = {\n user,\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: decodeJwtExpMs(result.accessToken)\n };\n\n this.session = session;\n return { session };\n }\n\n async refresh(): Promise<Pick<LooplaySession, 'accessToken' | 'refreshToken' | 'expiresAt'>> {\n const currentSession = this.session;\n const refreshToken = currentSession?.refreshToken;\n if (!currentSession || !refreshToken) throw new MissingRefreshTokenError();\n\n const result = await this.client.refresh(refreshToken);\n\n const next = {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: decodeJwtExpMs(result.accessToken)\n } satisfies Pick<LooplaySession, 'accessToken' | 'refreshToken' | 'expiresAt'>;\n\n this.session = { ...currentSession, ...next };\n return next;\n }\n\n async logout(): Promise<void> {\n const refreshToken = this.session?.refreshToken;\n\n if (refreshToken) {\n try {\n await this.client.logout(refreshToken);\n } catch {\n // best-effort\n }\n }\n\n this.session = null;\n }\n\n private defaultMapUser(raw: Record<string, any>): LooplayUser {\n const id = raw.id ?? raw._id ?? raw.userId ?? raw.telegramId;\n if (id === undefined || id === null || String(id).trim() === '') {\n throw new LooplaySDKError('Missing user id from auth response');\n }\n\n return {\n id: String(id),\n displayName: raw.displayName ?? raw.username ?? raw.name,\n avatarUrl: raw.avatarUrl ?? raw.photoUrl ?? raw.avatar\n };\n }\n}\n","import { ApiClient } from \"../apps/api-client\";\nimport type {\n EmitGameEventResponse,\n GameEventKey,\n} from \"../apps/LooplaySDK.types\";\nimport type {\n EmitGameEventOptions,\n LooplayIframeAuthListener,\n LooplayIframeAuthOptions,\n LooplayIframeAuthState,\n RequestAdsOptions,\n TrackMatchOptions,\n} from \"./types\";\n\nconst DEFAULT_ANON_ID_STORAGE_KEY = \"looplay:anon-id\";\nconst DEFAULT_ANONYMOUS_FALLBACK_TIMEOUT_MS = 4000;\nconst DEFAULT_REQUEST_ADS_TIMEOUT_MS = 5000;\n\nconst INITIAL_STATE: LooplayIframeAuthState = {\n jwt: null,\n gameId: null,\n apiUrl: null,\n anonymousId: null,\n origin: null,\n receivedAt: null,\n};\n\ntype LooplayIframeAuthMessage =\n | {\n type: \"LOOPLAY_AUTH\";\n jwt?: unknown;\n gameId?: unknown;\n apiUrl?: unknown;\n }\n | {\n type: \"LOOPLAY_ADS_WATCHED\";\n requestId?: unknown;\n }\n | {\n type: \"LOOPLAY_ADS_UNAVAILABLE\";\n requestId?: unknown;\n };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst toOptionalString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null;\n const normalized = value.trim();\n return normalized ? normalized : null;\n};\n\nfunction generateAnonymousId(): string {\n if (\n typeof crypto !== \"undefined\" &&\n typeof crypto.randomUUID === \"function\"\n ) {\n return crypto.randomUUID();\n }\n\n // Fallback UUID v4 for environments without crypto.randomUUID (older\n // browsers, non-secure contexts). Not cryptographically strong, but only\n // used as an anonymous, non-sensitive device identifier.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction readStoredAnonymousId(key: string): string | null {\n if (typeof window === \"undefined\" || !window.localStorage) return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStoredAnonymousId(key: string, value: string): void {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Private browsing, storage full, or blocked — the id just won't\n // persist across reloads; tracking for this session still works.\n }\n}\n\n/**\n * Handles auth for games that may run in two contexts:\n *\n * 1. Embedded as an iframe inside the Looplay web app — the host page\n * pushes `{ type: 'LOOPLAY_AUTH', jwt?, gameId, apiUrl }` via postMessage\n * in response to a `LOOPLAY_AUTH_REQUEST`. Tracking calls use the real\n * user's bearer token when available, otherwise fall back to anonymous\n * tracking (analytics-only, not eligible for quest/reward).\n * 2. Standalone — published anywhere else, or the parent never responds.\n * Falls back to an anonymous, locally-generated device id. Tracking\n * still works (the backend records it for analytics), but never feeds\n * the quest/reward engine.\n *\n * Either way, `appId` (issued to the creator after registering the game)\n * is required to track — see `LooplayIframeAuthOptions.appId`.\n */\nexport class LooplayIframeAuth {\n private state: LooplayIframeAuthState = INITIAL_STATE;\n private initialized = false;\n private lifecycleTrackingInitialized = false;\n private lastGameStartedKey: string | null = null;\n private client: ApiClient | null = null;\n private readonly listeners = new Set<LooplayIframeAuthListener>();\n private readonly options: LooplayIframeAuthOptions;\n private messageListener: ((event: MessageEvent<unknown>) => void) | null =\n null;\n private anonymousFallbackTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingAdsRequests = new Map<\n string,\n {\n resolve: (value: boolean) => void;\n timer: ReturnType<typeof setTimeout> | null;\n }\n >();\n private requestAdsSequence = 0;\n private adsRequestInFlight = false;\n private adsRequestsSent = 0;\n private lastAdsRequestAt = 0;\n private autoAdsActionCount = 0;\n\n constructor(options: LooplayIframeAuthOptions = {}) {\n this.options = options;\n }\n\n getState(): LooplayIframeAuthState {\n return this.state;\n }\n\n getJwt(): string | null {\n return this.state.jwt;\n }\n\n getApiUrl(): string | null {\n return this.options.apiUrl ?? this.state.apiUrl;\n }\n\n /** Resolves to the configured `appId`, falling back to whatever the parent pushed as `gameId`. */\n getGameId(): string | null {\n return this.options.appId ?? this.state.gameId;\n }\n\n getAnonymousId(): string | null {\n return this.state.anonymousId;\n }\n\n /** True once authenticated (bearer) or anonymous tracking is ready, given a configured `apiUrl`/`appId`. */\n canTrack(): boolean {\n if (!this.getApiUrl() || !this.getGameId()) return false;\n return Boolean(this.state.jwt || this.state.anonymousId);\n }\n\n subscribe(listener: LooplayIframeAuthListener): () => void {\n this.listeners.add(listener);\n listener(this.state);\n return () => this.listeners.delete(listener);\n }\n\n requestAuth(): void {\n if (typeof window === \"undefined\" || window.parent === window) {\n this.debug(\"standalone mode detected; auth request skipped\");\n return;\n }\n\n this.debug(\"requesting auth from parent\", {\n targetOrigin: this.resolveParentOrigin() ?? \"*\",\n });\n window.parent.postMessage(\n { type: \"LOOPLAY_AUTH_REQUEST\" },\n this.resolveParentOrigin() ?? \"*\"\n );\n }\n\n /**\n * Requests the parent host to show a rewarded ad.\n * In a Telegram Mini App setup, the game sends this request from the iframe\n * and the parent page (the Mini App shell) bridges it to Adsgram, then\n * responds with `LOOPLAY_ADS_WATCHED` after the ad finishes.\n */\n requestAds(options: RequestAdsOptions = {}): Promise<boolean> {\n if (typeof window === \"undefined\" || window.parent === window) {\n this.debug(\"requestAds skipped; standalone mode detected\");\n return Promise.resolve(false);\n }\n\n if (this.adsRequestInFlight) {\n this.debug(\"requestAds skipped; another ad request is already in flight\");\n return Promise.resolve(false);\n }\n\n const parentOrigin = this.resolveParentOrigin() ?? \"*\";\n const requestId = `ads-${Date.now()}-${++this.requestAdsSequence}`;\n const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_ADS_TIMEOUT_MS;\n\n this.adsRequestInFlight = true;\n this.adsRequestsSent += 1;\n this.lastAdsRequestAt = Date.now();\n this.debug(\"requesting rewarded ad from parent\", {\n requestId,\n targetOrigin: parentOrigin,\n gameId: this.getGameId(),\n });\n\n return new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n this.pendingAdsRequests.delete(requestId);\n this.adsRequestInFlight = false;\n this.debug(\"requestAds timed out\", { requestId, timeoutMs });\n resolve(false);\n }, timeoutMs);\n\n this.pendingAdsRequests.set(requestId, { resolve, timer });\n\n window.parent.postMessage(\n {\n type: \"LOOPLAY_REQUEST_ADS\",\n requestId,\n gameId: this.getGameId(),\n },\n parentOrigin\n );\n });\n }\n\n /** Idempotent; attaches the postMessage listener (if embedded) and arms the anonymous fallback. */\n init(): void {\n if (this.initialized || typeof window === \"undefined\") return;\n this.initialized = true;\n\n const isEmbedded = window.parent !== window;\n this.debug(\"auth listener initialized\", {\n isIframe: isEmbedded,\n parentOrigin: this.resolveParentOrigin(),\n });\n\n if (!isEmbedded) {\n // No parent frame to push auth at all — go anonymous right away.\n this.activateAnonymousMode();\n return;\n }\n\n const parentOrigin = this.resolveParentOrigin();\n\n if (!parentOrigin) {\n // `document.referrer` can be empty (Referrer-Policy, browser privacy\n // settings, extensions), which silently disables the origin check\n // below and lets any window that is literally `window.parent` push\n // auth. Pass `parentOrigin` explicitly to keep this check effective.\n console.warn(\n \"[LooplaySDK] LooplayIframeAuth could not resolve a parent origin to verify postMessage \" +\n \"against (document.referrer is empty). Auth messages from window.parent will be accepted \" +\n \"regardless of origin. Pass `parentOrigin` explicitly to harden this.\"\n );\n }\n\n this.messageListener = (event: MessageEvent<unknown>) => {\n if (event.source !== window.parent) return;\n if (parentOrigin && event.origin !== parentOrigin) {\n this.debug(\"ignored message from unexpected origin\", {\n expectedOrigin: parentOrigin,\n receivedOrigin: event.origin,\n });\n return;\n }\n\n this.handleIncomingMessage(event.data, event.origin);\n };\n\n window.addEventListener(\"message\", this.messageListener);\n\n // The host page might be an older protocol version, slow, or simply not\n // respond — don't leave the game untrackable forever.\n this.anonymousFallbackTimer = setTimeout(() => {\n if (this.state.jwt) return;\n this.activateAnonymousMode();\n }, this.options.anonymousFallbackTimeoutMs ?? DEFAULT_ANONYMOUS_FALLBACK_TIMEOUT_MS);\n\n this.requestAuth();\n }\n\n /** Removes the postMessage listener, cancels timers, and clears subscribers; safe to call multiple times. */\n dispose(): void {\n if (typeof window !== \"undefined\" && this.messageListener) {\n window.removeEventListener(\"message\", this.messageListener);\n }\n if (this.anonymousFallbackTimer !== null) {\n clearTimeout(this.anonymousFallbackTimer);\n }\n for (const pending of this.pendingAdsRequests.values()) {\n if (pending.timer !== null) clearTimeout(pending.timer);\n pending.resolve(false);\n }\n this.pendingAdsRequests.clear();\n\n this.messageListener = null;\n this.anonymousFallbackTimer = null;\n this.initialized = false;\n this.lifecycleTrackingInitialized = false;\n this.listeners.clear();\n }\n\n /** Lazily builds (and rebuilds on auth change) the `ApiClient` bound to the current auth mode. */\n getClient(): ApiClient {\n if (this.client) return this.client;\n\n const baseUrl = this.getApiUrl();\n\n this.debug(\"creating sdk ApiClient\", { baseUrl });\n this.client = new ApiClient({\n baseUrl: baseUrl ?? undefined,\n getAccessToken: async () => this.getJwt() ?? undefined,\n getAnonymousId: () => this.getAnonymousId() ?? undefined,\n });\n\n return this.client;\n }\n\n /** Call once when the game view opens, before any play/match tracking. */\n trackView(): Promise<boolean> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"trackView skipped; auth is not ready\", {\n hasGameId: Boolean(gameId),\n });\n return Promise.resolve(false);\n }\n\n this.debug(\"trackView dispatched\", { gameId });\n return this.getClient().trackView(gameId);\n }\n\n /** Starts a new play attempt; call when the user presses Play. Returns the attempt id. */\n startAttempt(): string {\n return this.getClient().startAttempt();\n }\n\n trackPlay(playTimeSeconds?: number): Promise<boolean> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"trackPlay skipped; auth is not ready\", {\n hasGameId: Boolean(gameId),\n });\n return Promise.resolve(false);\n }\n\n this.debug(\"trackPlay dispatched\", { gameId, playTimeSeconds });\n return this.getClient().trackPlay(gameId, playTimeSeconds);\n }\n\n trackMatch(matchId: string, opts: TrackMatchOptions): Promise<boolean> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"trackMatch skipped; auth is not ready\", { matchId });\n return Promise.resolve(false);\n }\n\n this.debug(\"trackMatch dispatched\", { gameId, matchId, ...opts });\n const result = this.getClient().trackMatch(gameId, {\n matchId,\n matchDurationSeconds: opts.durationSeconds,\n isCompleted: opts.isCompleted,\n isWin: opts.isWin,\n });\n void result\n .then((ok) => {\n if (ok) this.recordAutoRequestAdsActivity();\n })\n .catch(() => undefined);\n return result;\n }\n\n emitGameEvent(\n actionCode: GameEventKey | string,\n opts?: EmitGameEventOptions\n ): Promise<EmitGameEventResponse | null> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"emitGameEvent skipped; auth is not ready\", { actionCode });\n return Promise.resolve(null);\n }\n\n this.debug(\"emitGameEvent dispatched\", { gameId, actionCode, ...opts });\n return this.getClient().emit(gameId, actionCode, opts);\n }\n\n /** Idempotent; auto-emits GAME_STARTED exactly once per distinct auth session (bearer or anonymous). */\n initLifecycleTracking(): void {\n if (this.lifecycleTrackingInitialized || typeof window === \"undefined\")\n return;\n this.lifecycleTrackingInitialized = true;\n this.debug(\"lifecycle tracking initialized\");\n\n this.subscribe(() => {\n const key = this.getTrackingKey();\n if (!key || this.lastGameStartedKey === key) return;\n\n this.lastGameStartedKey = key;\n this.debug(\"auto GAME_STARTED dispatch\");\n void this.emitGameEvent(\"GAME_STARTED\");\n });\n }\n\n private getTrackingKey(): string | null {\n const gameId = this.getGameId();\n const apiUrl = this.getApiUrl();\n const identity = this.state.jwt ?? this.state.anonymousId;\n if (!gameId || !apiUrl || !identity) return null;\n return `${apiUrl}\\n${gameId}\\n${identity}`;\n }\n\n /** Generates (or restores) a persisted anonymous device id and activates anonymous tracking. */\n private activateAnonymousMode(): void {\n if (this.state.anonymousId) return;\n\n const key =\n this.options.anonymousIdStorageKey ?? DEFAULT_ANON_ID_STORAGE_KEY;\n const anonymousId = readStoredAnonymousId(key) ?? generateAnonymousId();\n writeStoredAnonymousId(key, anonymousId);\n\n this.lastGameStartedKey = null;\n this.setState({ ...this.state, anonymousId, receivedAt: Date.now() });\n this.debug(\"anonymous tracking mode active\", { anonymousId });\n }\n\n private handleAuthMessage(\n data: Record<string, unknown>,\n origin: string\n ): void {\n if (this.anonymousFallbackTimer !== null) {\n clearTimeout(this.anonymousFallbackTimer);\n this.anonymousFallbackTimer = null;\n }\n\n const jwt = toOptionalString(data.jwt);\n const gameId = toOptionalString(data.gameId);\n const apiUrl = toOptionalString(data.apiUrl);\n const resolvedGameId = gameId ?? this.getGameId();\n const resolvedApiUrl = apiUrl ?? this.getApiUrl();\n\n if (!resolvedGameId || !resolvedApiUrl) {\n this.lastGameStartedKey = null;\n this.setState({\n ...this.state,\n jwt: null,\n gameId: null,\n apiUrl: null,\n origin,\n receivedAt: Date.now(),\n });\n this.debug(\n \"auth message missing game identity; falling back to anonymous tracking\"\n );\n this.activateAnonymousMode();\n return;\n }\n\n if (!jwt) {\n this.lastGameStartedKey = null;\n this.setState({\n ...this.state,\n jwt: null,\n gameId: resolvedGameId,\n apiUrl: resolvedApiUrl,\n origin,\n receivedAt: Date.now(),\n });\n this.debug(\n \"auth message missing jwt; falling back to anonymous tracking\",\n {\n gameId: resolvedGameId,\n apiUrl: resolvedApiUrl,\n }\n );\n this.activateAnonymousMode();\n return;\n }\n\n this.lastGameStartedKey = null;\n this.setState({\n jwt,\n gameId,\n apiUrl,\n anonymousId: null,\n origin,\n receivedAt: Date.now(),\n });\n }\n\n private handleRequestAdsResult(data: LooplayIframeAuthMessage): void {\n if (data.type !== \"LOOPLAY_ADS_WATCHED\") return;\n\n const requestId = toOptionalString(data.requestId);\n if (!requestId) return;\n\n const pending = this.pendingAdsRequests.get(requestId);\n if (!pending) return;\n\n this.pendingAdsRequests.delete(requestId);\n if (pending.timer !== null) clearTimeout(pending.timer);\n\n this.adsRequestInFlight = false;\n this.debug(\"requestAds watched message received\", { requestId });\n // TODO: call API to grant reward after the rewarded ad has been watched.\n pending.resolve(true);\n }\n\n private handleRequestAdsUnavailable(data: LooplayIframeAuthMessage): void {\n if (data.type !== \"LOOPLAY_ADS_UNAVAILABLE\") return;\n\n const requestId = toOptionalString(data.requestId);\n if (!requestId) return;\n\n const pending = this.pendingAdsRequests.get(requestId);\n if (!pending) return;\n\n this.pendingAdsRequests.delete(requestId);\n if (pending.timer !== null) clearTimeout(pending.timer);\n\n this.adsRequestInFlight = false;\n this.debug(\"requestAds unavailable message received\", { requestId });\n pending.resolve(false);\n }\n\n private handleIncomingMessage(data: unknown, origin: string): void {\n if (!isRecord(data)) return;\n\n if (data.type === \"LOOPLAY_AUTH\") {\n this.handleAuthMessage(data, origin);\n return;\n }\n\n if (data.type === \"LOOPLAY_ADS_WATCHED\") {\n this.handleRequestAdsResult(data as LooplayIframeAuthMessage);\n return;\n }\n\n if (data.type === \"LOOPLAY_ADS_UNAVAILABLE\") {\n this.handleRequestAdsUnavailable(data as LooplayIframeAuthMessage);\n return;\n }\n }\n\n private setState(next: LooplayIframeAuthState): void {\n this.state = next;\n this.client = null;\n this.debug(\"auth state updated\", {\n gameId: this.getGameId(),\n apiUrl: this.getApiUrl(),\n origin: next.origin,\n hasJwt: Boolean(next.jwt),\n hasAnonymousId: Boolean(next.anonymousId),\n });\n this.listeners.forEach((listener) => listener(next));\n }\n\n private resolveParentOrigin(): string | null {\n if (this.options.parentOrigin) return this.options.parentOrigin;\n if (typeof document === \"undefined\" || !document.referrer) return null;\n\n try {\n return new URL(document.referrer).origin;\n } catch {\n return null;\n }\n }\n\n private debug(message: string, details?: Record<string, unknown>): void {\n this.options.onDebug?.(message, details);\n }\n\n private recordAutoRequestAdsActivity(): void {\n this.autoAdsActionCount += 1;\n\n const config = this.options.autoRequestAds;\n if (!config?.enabled) return;\n if (this.adsRequestInFlight) return;\n if (!this.canTrack()) return;\n\n const now = Date.now();\n if (config.cooldownMs && now - this.lastAdsRequestAt < config.cooldownMs) {\n return;\n }\n\n if (config.maxRequestsPerSession && this.adsRequestsSent >= config.maxRequestsPerSession) {\n return;\n }\n\n const threshold = config.afterActionCount ?? 0;\n const shouldRequest = threshold > 0 && this.autoAdsActionCount % threshold === 0;\n\n if (!shouldRequest) return;\n\n const chance = config.chance ?? 1;\n if (chance <= 0 || Math.random() > chance) {\n this.debug(\"auto requestAds skipped by chance\", {\n actionCount: this.autoAdsActionCount,\n chance,\n });\n return;\n }\n\n this.debug(\"auto requestAds triggered\", {\n actionCount: this.autoAdsActionCount,\n });\n\n void this.requestAds();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/auth/storage.ts","../src/auth/looplay-auth.ts","../src/apps/http.ts","../src/apps/service-client.ts","../src/apps/api-client.ts","../src/looplay-sdk.ts","../src/auth/jwt.ts","../src/auth/providers/telegram-auth-provider.ts","../src/iframe/iframe-auth.ts"],"names":["LooplaySDKError","message","NotInitializedError","MissingBaseUrlError","MissingAuthError","NotAuthenticatedError","MemoryAuthStorage","key","value","BrowserLocalStorageAuthStorage","InvalidSessionError","LooplayAuth","options","params","result","now","refreshed","next","eventName","listener","raw","parsed","event","getFetchFn","fetchOverride","HttpError","status","bodyText","HttpClient","method","path","url","headers","body","res","text","sources","src","query","normalizedPath","ServiceClient","telegramInitData","refreshToken","gameKey","auth","anonId","bearerToken","generateId","c","r","ApiClient","token","session","dto","attemptId","playTimeSeconds","attemptToken","actionCode","opts","LooplaySDK","baseUrl","fetch","defaultHeaders","matchId","decodeJwtClaims","parts","payload","padded","json","decodeJwtExpMs","exp","MissingTelegramInitDataError","MissingRefreshTokenError","TelegramAuthProvider","initData","rawUser","currentSession","id","DEFAULT_ANON_ID_STORAGE_KEY","DEFAULT_ANONYMOUS_FALLBACK_TIMEOUT_MS","DEFAULT_REQUEST_ADS_TIMEOUT_MS","INITIAL_STATE","isRecord","toOptionalString","normalized","generateAnonymousId","readStoredAnonymousId","writeStoredAnonymousId","LooplayIframeAuth","parentOrigin","requestId","timeoutMs","resolve","timer","isEmbedded","pending","gameId","ok","apiUrl","identity","anonymousId","data","origin","jwt","resolvedGameId","resolvedApiUrl","details","config","threshold","chance"],"mappings":"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","file":"looplay-sdk.min.js","sourcesContent":["export class LooplaySDKError extends Error {\n constructor(message?: string) {\n super(message);\n this.name = 'LooplaySDKError';\n }\n}\n\nexport class NotInitializedError extends LooplaySDKError {\n constructor() {\n super('LooplaySDK not initialized. Call init({ gameId }) first.');\n this.name = 'NotInitializedError';\n }\n}\n\nexport class MissingBaseUrlError extends LooplaySDKError {\n constructor() {\n super('Missing baseUrl. Provide `baseUrl` to enable HTTP integration.');\n this.name = 'MissingBaseUrlError';\n }\n}\n\nexport class MissingAuthError extends LooplaySDKError {\n constructor() {\n super('Missing auth. Provide `auth` options to enable user login/session.');\n this.name = 'MissingAuthError';\n }\n}\n\nexport class NotAuthenticatedError extends LooplaySDKError {\n constructor() {\n super('Not authenticated. Login is required for this operation.');\n this.name = 'NotAuthenticatedError';\n }\n}\n","import type { AuthStorage } from './types';\n\nexport class MemoryAuthStorage implements AuthStorage {\n private readonly map = new Map<string, string>();\n\n async getItem(key: string): Promise<string | null> {\n return this.map.has(key) ? (this.map.get(key) as string) : null;\n }\n\n async setItem(key: string, value: string): Promise<void> {\n this.map.set(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n this.map.delete(key);\n }\n}\n\nexport class BrowserLocalStorageAuthStorage implements AuthStorage {\n async getItem(key: string): Promise<string | null> {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n return window.localStorage.getItem(key);\n }\n\n async setItem(key: string, value: string): Promise<void> {\n if (typeof window === 'undefined' || !window.localStorage) return;\n window.localStorage.setItem(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n if (typeof window === 'undefined' || !window.localStorage) return;\n window.localStorage.removeItem(key);\n }\n}\n","import { LooplaySDKError } from '../errors';\nimport {\n type AuthEventName,\n type AuthLoginParams,\n type AuthProvider,\n type AuthStorage,\n type LooplayAuthOptions,\n type LooplaySession\n} from './types';\nimport { MemoryAuthStorage } from './storage';\n\nclass InvalidSessionError extends LooplaySDKError {\n constructor() {\n super('Invalid session returned by auth provider');\n this.name = 'InvalidSessionError';\n }\n}\n\ntype Listener = (session: LooplaySession | null) => void;\n\nexport class LooplayAuth {\n private readonly provider: AuthProvider;\n private readonly storage: AuthStorage;\n private readonly key: string;\n\n private session: LooplaySession | null = null;\n private readonly listeners = new Set<Listener>();\n\n constructor(options: LooplayAuthOptions) {\n this.provider = options.provider;\n this.storage = options.storage ?? new MemoryAuthStorage();\n this.key = `${options.storageKeyPrefix ?? 'looplay'}:auth:session`;\n }\n\n async init(): Promise<void> {\n await this.provider.init?.();\n await this.restoreFromStorage();\n\n // Keep provider in sync with restored session (if any).\n await this.provider.onSessionChanged?.(this.session);\n }\n\n getSession(): LooplaySession | null {\n return this.session;\n }\n\n async login(params?: AuthLoginParams): Promise<LooplaySession> {\n const result = await this.provider.login(params);\n\n if (!result?.session?.user?.id) {\n throw new InvalidSessionError();\n }\n\n await this.setSession(result.session, 'login');\n return result.session;\n }\n\n async logout(): Promise<void> {\n await this.provider.logout();\n await this.setSession(null, 'logout');\n }\n\n async getAccessToken(): Promise<string | undefined> {\n if (!this.session) return undefined;\n\n const now = Date.now();\n const isExpired =\n typeof this.session.expiresAt === 'number' && this.session.expiresAt <= now;\n\n if (!isExpired) return this.session.accessToken;\n\n if (!this.provider.refresh) return this.session.accessToken;\n\n const refreshed = await this.provider.refresh();\n const next = { ...this.session, ...refreshed };\n await this.setSession(next, 'sessionChanged');\n\n return next.accessToken;\n }\n\n on(eventName: AuthEventName, listener: Listener): () => void {\n // eventName reserved for future filtering; for now notify on any change\n void eventName;\n\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private async restoreFromStorage(): Promise<void> {\n const raw = await this.storage.getItem(this.key);\n if (!raw) return;\n\n try {\n const parsed = JSON.parse(raw) as LooplaySession;\n if (parsed?.user?.id) {\n this.session = parsed;\n }\n } catch {\n // ignore corrupted cache\n await this.storage.removeItem(this.key);\n }\n }\n\n private async setSession(next: LooplaySession | null, event: AuthEventName): Promise<void> {\n this.session = next;\n\n await this.provider.onSessionChanged?.(this.session);\n\n if (next) {\n await this.storage.setItem(this.key, JSON.stringify(next));\n } else {\n await this.storage.removeItem(this.key);\n }\n\n for (const listener of this.listeners) {\n listener(this.session);\n }\n\n // reserved for future event-specific hooks\n void event;\n }\n}\n","export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nexport interface HttpClientOptions {\n baseUrl: string;\n fetch?: FetchLike;\n defaultHeaders?: Record<string, string>;\n}\n\nfunction getFetchFn(fetchOverride?: FetchLike): FetchLike {\n if (fetchOverride) {\n if (typeof window !== 'undefined' && fetchOverride === window.fetch) {\n return window.fetch.bind(window);\n }\n\n return fetchOverride;\n }\n\n return globalThis.fetch.bind(globalThis);\n}\n\nexport class HttpError extends Error {\n readonly status: number;\n readonly bodyText?: string;\n\n constructor(message: string, status: number, bodyText?: string) {\n super(message);\n this.name = 'HttpError';\n this.status = status;\n this.bodyText = bodyText;\n }\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly fetchFn: FetchLike;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.fetchFn = getFetchFn(options.fetch);\n this.defaultHeaders = options.defaultHeaders ?? {};\n }\n\n async request<T>(\n method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE',\n path: string,\n options?: {\n query?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string | undefined>;\n bearerToken?: string;\n /** Anonymous device id, used when there is no `bearerToken` (no logged-in user). */\n anonId?: string;\n body?: unknown;\n }\n ): Promise<T> {\n const url = this.buildUrl(path, options?.query);\n\n const headers: Record<string, string> = this.mergeHeaders(\n this.defaultHeaders,\n options?.headers\n );\n\n if (options?.bearerToken) {\n headers.Authorization = `Bearer ${options.bearerToken}`;\n } else if (options?.anonId) {\n headers['x-looplay-anon-id'] = options.anonId;\n }\n\n let body: BodyInit | undefined;\n if (options?.body !== undefined) {\n headers['Content-Type'] = headers['Content-Type'] ?? 'application/json';\n body = JSON.stringify(options.body);\n }\n\n const res = await this.fetchFn(url, { method, headers, body });\n\n if (!res.ok) {\n const text = await this.safeText(res);\n throw new HttpError(`HTTP ${res.status} ${res.statusText}`, res.status, text);\n }\n\n // Some endpoints may return empty body.\n const text = await this.safeText(res);\n if (!text) return undefined as T;\n\n return JSON.parse(text) as T;\n }\n\n private mergeHeaders(\n ...sources: Array<Record<string, string | undefined> | undefined>\n ): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const src of sources) {\n if (!src) continue;\n for (const [key, value] of Object.entries(src)) {\n if (value === undefined) continue;\n result[key] = value;\n }\n }\n\n return result;\n }\n\n private buildUrl(\n path: string,\n query?: Record<string, string | number | boolean | undefined>\n ): string {\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(`${this.baseUrl}${normalizedPath}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n }\n\n private async safeText(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return '';\n }\n }\n}\n","import { HttpClient, type HttpClientOptions } from './http';\nimport type {\n AuthResultDto,\n CompleteGameplayAttemptRequest,\n CompleteGameplayMatchRequest,\n EmitGameEventResponse,\n GameAssetDto,\n GameOwnedAssetDto,\n GameplayAttemptSessionDto,\n GameStoreDto,\n GameStoreOfferPurchaseResultDto,\n GameTrackingSessionDto,\n PagingResponse,\n RecordGameplayActionRequest,\n RefreshTokenRequest,\n StartGameplayAttemptRequest,\n TaskListQuery,\n TelegramLoginRequest,\n} from './LooplaySDK.types';\n\nexport interface ServiceClientOptions extends HttpClientOptions {}\n\n/**\n * Auth for the tracking endpoints (view/attempt/match/action), which accept\n * either a real user's `bearerToken` (full tracking, eligible for\n * quest/reward) or an `anonId` for an unauthenticated device\n * (analytics-only — the backend never awards quest/reward progress to an\n * anonymous id). `trackingSessionId`/`anonTrackingToken` come from\n * `bootstrapTrackingSession()` and should be attached either way, per the\n * platform's tracking contract.\n */\nexport interface TrackingAuth {\n bearerToken?: string;\n anonId?: string;\n trackingSessionId?: string;\n anonTrackingToken?: string;\n}\n\n/**\n * Low-level HTTP client that maps 1:1 to backend routes.\n * Methods requiring user auth accept `bearerToken` explicitly.\n */\nexport class ServiceClient {\n private readonly http: HttpClient;\n\n constructor(options: ServiceClientOptions) {\n this.http = new HttpClient(options);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Auth\n // ─────────────────────────────────────────────────────────────────────────────\n\n async telegramLogin(telegramInitData: string): Promise<AuthResultDto> {\n const body: TelegramLoginRequest = { telegramInitData };\n return this.http.request('POST', '/account/telegram-login', { body });\n }\n\n async refresh(refreshToken: string): Promise<AuthResultDto> {\n const body: RefreshTokenRequest = { refreshToken };\n return this.http.request('POST', '/account/refresh', { body });\n }\n\n async logout(refreshToken: string): Promise<boolean> {\n const body: RefreshTokenRequest = { refreshToken };\n return this.http.request('POST', '/account/logout', { body });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Games — one SDK integration = one game, resolved on every call from the\n // public `x-game-key` header (the game's `appId`). There is no multi-game\n // catalog browsing here (list/search/detail across games) and no `:id`/\n // `storeId` path segments — that's the app-facing surface, out of scope\n // for this per-game SDK. Never use the creator-secret tier\n // (`x-creator-key`/`x-game-secret`) from client/browser code — that would\n // leak the secret to every player.\n // ─────────────────────────────────────────────────────────────────────────────\n\n async recordGameView(gameKey: string, auth: TrackingAuth = {}): Promise<boolean> {\n // `referer` is a forbidden header name — browsers attach it automatically,\n // it cannot (and doesn't need to) be set here.\n await this.http.request('POST', '/sdk/games/views/record', {\n ...auth,\n headers: { 'x-game-key': gameKey, ...this.trackingHeaders(auth) },\n });\n return true;\n }\n\n /** Signs a fresh `attemptToken` — required by `completeGameplayAttempt`/`completeGameplayMatch`/`recordGameplayAction`. */\n async startGameplayAttempt(\n gameKey: string,\n auth: TrackingAuth = {},\n body: StartGameplayAttemptRequest = {}\n ): Promise<GameplayAttemptSessionDto> {\n return this.http.request('POST', '/sdk/games/attempts/start', {\n ...auth,\n headers: { 'x-game-key': gameKey, ...this.trackingHeaders(auth) },\n body,\n });\n }\n\n async completeGameplayAttempt(\n gameKey: string,\n auth: TrackingAuth,\n body: CompleteGameplayAttemptRequest\n ): Promise<boolean> {\n await this.http.request('POST', '/sdk/games/attempts/complete', {\n ...auth,\n headers: { 'x-game-key': gameKey, ...this.trackingHeaders(auth) },\n body,\n });\n return true;\n }\n\n async completeGameplayMatch(\n gameKey: string,\n auth: TrackingAuth,\n body: CompleteGameplayMatchRequest\n ): Promise<boolean> {\n await this.http.request('POST', '/sdk/games/matches/complete', {\n ...auth,\n headers: { 'x-game-key': gameKey, ...this.trackingHeaders(auth) },\n body,\n });\n return true;\n }\n\n async recordGameplayAction(\n gameKey: string,\n auth: TrackingAuth,\n body: RecordGameplayActionRequest\n ): Promise<EmitGameEventResponse> {\n return this.http.request('POST', '/sdk/games/actions/record', {\n ...auth,\n headers: { 'x-game-key': gameKey, ...this.trackingHeaders(auth) },\n body,\n });\n }\n\n /**\n * Anonymous runtimes must call this before any tracking call; logged-in\n * runtimes should still call it to get a `trackingSessionId` for unified\n * tracing — see `GameTrackingSessionDto`.\n */\n async bootstrapTrackingSession(anonId?: string): Promise<GameTrackingSessionDto> {\n return this.http.request('POST', '/sdk/games/tracking/bootstrap', {\n headers: { 'x-looplay-anon-id': anonId },\n });\n }\n\n private trackingHeaders(auth: TrackingAuth): Record<string, string | undefined> {\n return {\n 'x-game-tracking-session-id': auth.trackingSessionId,\n 'x-looplay-anon-token': auth.anonTrackingToken,\n };\n }\n\n /** Public storefront (sections + offers) for the integration's game. Read-only. */\n async getStore(gameKey: string): Promise<GameStoreDto> {\n return this.http.request('GET', '/sdk/games/store/current', {\n headers: { 'x-game-key': gameKey },\n });\n }\n\n /** Public catalog of purchasable/ownable assets for the game. Read-only. */\n async listGameAssets(gameKey: string): Promise<GameAssetDto[]> {\n return this.http.request('GET', '/sdk/games/store/assets', {\n headers: { 'x-game-key': gameKey },\n });\n }\n\n /** Assets the authenticated player already owns for this game. */\n async listMyGameAssets(bearerToken: string, gameKey: string): Promise<GameOwnedAssetDto[]> {\n return this.http.request('GET', '/sdk/games/store/assets/me', {\n bearerToken,\n headers: { 'x-game-key': gameKey },\n });\n }\n\n /** The authenticated player's past store purchases for this game. */\n async listMyStorePurchases(\n bearerToken: string,\n gameKey: string\n ): Promise<GameStoreOfferPurchaseResultDto[]> {\n return this.http.request('GET', '/sdk/games/store/purchases/me', {\n bearerToken,\n headers: { 'x-game-key': gameKey },\n });\n }\n\n // No purchase route is reachable from client/browser code — spending coin\n // balance always requires the creator+game secret pair\n // (`x-creator-key`/`x-game-secret`), called from the game's own backend.\n // This SDK intentionally never wires that tier up.\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Balance\n // ─────────────────────────────────────────────────────────────────────────────\n\n async getBalances(bearerToken: string, query?: Record<string, any>): Promise<any> {\n return this.http.request('GET', '/sdk/balance', { bearerToken, query });\n }\n\n async getBalanceHistory(\n bearerToken: string,\n query?: Record<string, any>\n ): Promise<PagingResponse<any>> {\n return this.http.request('GET', '/sdk/balance/history', { bearerToken, query });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // User\n // ─────────────────────────────────────────────────────────────────────────────\n\n async getMyProfile(bearerToken: string): Promise<any> {\n return this.http.request('GET', '/sdk/user/profile', { bearerToken });\n }\n\n async getMyBalance(bearerToken: string): Promise<any> {\n return this.http.request('GET', '/sdk/user/balance', { bearerToken });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Referrals\n // ─────────────────────────────────────────────────────────────────────────────\n\n async listReferrals(\n bearerToken: string,\n query?: Record<string, any>\n ): Promise<PagingResponse<any>> {\n return this.http.request('GET', '/sdk/referrals', { bearerToken, query });\n }\n\n async setReferral(bearerToken: string, body: Record<string, any>): Promise<boolean> {\n return this.http.request('POST', '/sdk/referrals/set', { bearerToken, body });\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tasks\n // ─────────────────────────────────────────────────────────────────────────────\n\n async listTasks(bearerToken: string, query?: TaskListQuery): Promise<any[]> {\n return this.http.request('GET', '/sdk/task', { bearerToken, query: { ...query } });\n }\n\n async listFinishedTasks(bearerToken: string, query?: TaskListQuery): Promise<any[]> {\n return this.http.request('GET', '/sdk/task/finished', { bearerToken, query: { ...query } });\n }\n\n async startTask(bearerToken: string, body: Record<string, any>): Promise<any> {\n return this.http.request('POST', '/sdk/task/start', { bearerToken, body });\n }\n\n async claimTask(bearerToken: string, body: Record<string, any>): Promise<any> {\n return this.http.request('POST', '/sdk/task/claim', { bearerToken, body });\n }\n}\n","import { MissingBaseUrlError, NotAuthenticatedError } from '../errors';\nimport type {\n EmitGameEventResponse,\n GameAssetDto,\n GameEventKey,\n GameOwnedAssetDto,\n GameStoreDto,\n GameStoreOfferPurchaseResultDto,\n PagingResponse,\n RecordGameplayActionRequest,\n TaskListQuery,\n} from './LooplaySDK.types';\nimport { ServiceClient, type TrackingAuth } from './service-client';\nimport type { ServiceClientOptions } from './service-client';\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n\n // Fallback UUID v4 for environments without crypto.randomUUID. Not\n // cryptographically strong, but only used as a non-sensitive tracking id.\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\ninterface CachedTrackingSession {\n trackingSessionId: string;\n anonTrackingToken: string;\n expiresAtMs: number;\n}\n\n/** Signed `attemptToken` for the current play attempt — required on every play/match/action call. */\ninterface CachedGameplayAttempt {\n gameKey: string;\n attemptId: string;\n attemptToken: string;\n expiresAtMs: number;\n}\n\nexport interface ApiClientOptions extends Omit<ServiceClientOptions, 'baseUrl'> {\n baseUrl?: string;\n getAccessToken?: () => Promise<string | undefined>;\n /**\n * Resolves an anonymous device id, used for the tracking endpoints\n * (`trackPlay`/`trackMatch`/`emit`) when `getAccessToken` yields nothing.\n * Anonymous tracking is analytics-only — see `TrackingAuth`.\n */\n getAnonymousId?: () => string | undefined;\n}\n\n/**\n * High-level API client that injects user auth automatically.\n */\nexport class ApiClient {\n private readonly raw: ServiceClient;\n private readonly getAccessToken?: () => Promise<string | undefined>;\n private readonly getAnonymousId?: () => string | undefined;\n private trackingSession?: CachedTrackingSession;\n private trackingSessionPromise?: Promise<CachedTrackingSession | undefined>;\n private currentAttemptId?: string;\n private gameplayAttempt?: CachedGameplayAttempt;\n private gameplayAttemptPromise?: Promise<CachedGameplayAttempt>;\n\n constructor(options: ApiClientOptions) {\n if (!options.baseUrl) throw new MissingBaseUrlError();\n\n this.raw = new ServiceClient({\n baseUrl: options.baseUrl,\n fetch: options.fetch,\n defaultHeaders: options.defaultHeaders,\n });\n\n this.getAccessToken = options.getAccessToken;\n this.getAnonymousId = options.getAnonymousId;\n }\n\n /** Expose the underlying route-level client (requires manual bearerToken/gameKey passing). */\n unsafeRaw(): ServiceClient {\n return this.raw;\n }\n\n private async requireToken(): Promise<string> {\n const token = await this.getAccessToken?.();\n if (!token) throw new NotAuthenticatedError();\n return token;\n }\n\n /**\n * Resolves auth for the tracking endpoints: a real bearer token if logged\n * in, otherwise an anonymous device id. Throws only when neither is\n * available — anonymous tracking still requires *some* identity. Also\n * attaches a `trackingSessionId`/`anonTrackingToken` (bootstrapped and\n * cached lazily) — the backend's tracking contract expects these even for\n * logged-in callers, for unified tracing.\n */\n private async resolveTrackingAuth(): Promise<TrackingAuth> {\n const bearerToken = await this.getAccessToken?.();\n const anonId = bearerToken ? undefined : this.getAnonymousId?.();\n\n if (!bearerToken && !anonId) throw new NotAuthenticatedError();\n\n const session = await this.ensureTrackingSession(anonId);\n\n return {\n bearerToken,\n anonId,\n trackingSessionId: session?.trackingSessionId,\n anonTrackingToken: session?.anonTrackingToken,\n };\n }\n\n /**\n * Bootstraps (and caches until near expiry) the platform tracking\n * session. Swallows failures — tracking calls still work without it,\n * just without unified session tracing.\n */\n private async ensureTrackingSession(\n anonId?: string\n ): Promise<CachedTrackingSession | undefined> {\n const now = Date.now();\n if (this.trackingSession && this.trackingSession.expiresAtMs > now + 30_000) {\n return this.trackingSession;\n }\n\n if (!this.trackingSessionPromise) {\n this.trackingSessionPromise = this.raw\n .bootstrapTrackingSession(anonId)\n .then((dto) => {\n this.trackingSession = {\n trackingSessionId: dto.trackingSessionId,\n anonTrackingToken: dto.trackingToken,\n expiresAtMs: new Date(dto.expiresAt).getTime(),\n };\n return this.trackingSession;\n })\n .catch(() => undefined)\n .finally(() => {\n this.trackingSessionPromise = undefined;\n });\n }\n\n return this.trackingSessionPromise;\n }\n\n /**\n * Signs (and caches until near expiry) the `attemptToken` required by\n * `trackPlay`/`trackMatch`/`emit` for the current attempt — does not\n * swallow failures, unlike `ensureTrackingSession`, since these calls\n * are rejected outright without a valid token.\n */\n private async ensureGameplayAttempt(\n gameKey: string,\n auth: TrackingAuth,\n attemptId: string\n ): Promise<CachedGameplayAttempt> {\n const now = Date.now();\n if (\n this.gameplayAttempt &&\n this.gameplayAttempt.gameKey === gameKey &&\n this.gameplayAttempt.attemptId === attemptId &&\n this.gameplayAttempt.expiresAtMs > now + 10_000\n ) {\n return this.gameplayAttempt;\n }\n\n if (!this.gameplayAttemptPromise) {\n this.gameplayAttemptPromise = this.raw\n .startGameplayAttempt(gameKey, auth, { attemptId })\n .then((dto) => {\n this.gameplayAttempt = {\n gameKey,\n attemptId: dto.attemptId,\n attemptToken: dto.attemptToken,\n expiresAtMs: new Date(dto.expiresAt).getTime(),\n };\n return this.gameplayAttempt;\n })\n .finally(() => {\n this.gameplayAttemptPromise = undefined;\n });\n }\n\n return this.gameplayAttemptPromise;\n }\n\n /**\n * Call once when the game view opens, before any play/match tracking.\n * `gameKey` is the game's public `appId`.\n */\n async trackView(gameKey: string): Promise<boolean> {\n const auth = await this.resolveTrackingAuth();\n return this.raw.recordGameView(gameKey, auth);\n }\n\n /**\n * Starts a new play attempt and returns its id. Call when the user\n * presses Play; `trackPlay`/`trackMatch`/`emit` reuse this id (and the\n * signed `attemptToken` backing it) across calls until the next\n * `startAttempt()`, auto-starting one on first use if none was started.\n */\n startAttempt(): string {\n this.currentAttemptId = generateId();\n this.gameplayAttempt = undefined;\n return this.currentAttemptId;\n }\n\n async trackPlay(gameKey: string, playTimeSeconds?: number): Promise<boolean> {\n const auth = await this.resolveTrackingAuth();\n const attemptId = this.currentAttemptId ?? this.startAttempt();\n const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);\n return this.raw.completeGameplayAttempt(gameKey, auth, { playTimeSeconds, attemptId, attemptToken });\n }\n\n async trackMatch(\n gameKey: string,\n body: { matchId: string; matchDurationSeconds: number; isCompleted?: boolean; isWin?: boolean }\n ): Promise<boolean> {\n const auth = await this.resolveTrackingAuth();\n const attemptId = this.currentAttemptId ?? this.startAttempt();\n const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);\n return this.raw.completeGameplayMatch(gameKey, auth, { ...body, attemptId, attemptToken });\n }\n\n async emit(\n gameKey: string,\n actionCode: GameEventKey | string,\n opts?: { value?: number; refId?: string; payload?: Record<string, unknown> }\n ): Promise<EmitGameEventResponse> {\n const auth = await this.resolveTrackingAuth();\n const attemptId = this.currentAttemptId ?? this.startAttempt();\n const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);\n const body: RecordGameplayActionRequest = {\n attemptId,\n attemptToken,\n actionCode,\n value: opts?.value,\n refId: opts?.refId,\n payload: opts?.payload,\n };\n return this.raw.recordGameplayAction(gameKey, auth, body);\n }\n\n /** Fetches the storefront (sections + offers) for the integration's game. Read-only, no auth required. */\n async getStore(gameKey: string): Promise<GameStoreDto> {\n return this.raw.getStore(gameKey);\n }\n\n /** Public catalog of purchasable/ownable assets for the game. Read-only, no auth required. */\n async listGameAssets(gameKey: string): Promise<GameAssetDto[]> {\n return this.raw.listGameAssets(gameKey);\n }\n\n /** Assets the authenticated player already owns for this game. */\n async listMyGameAssets(gameKey: string): Promise<GameOwnedAssetDto[]> {\n const token = await this.requireToken();\n return this.raw.listMyGameAssets(token, gameKey);\n }\n\n /** Purchase history for the authenticated player in the game's store. */\n async listMyStorePurchases(gameKey: string): Promise<GameStoreOfferPurchaseResultDto[]> {\n const token = await this.requireToken();\n return this.raw.listMyStorePurchases(token, gameKey);\n }\n\n // There is no purchaseStoreOffer() here by design — spending coin balance\n // requires the creator+game secret pair, which must be called from the\n // game's own backend, never from this client-side SDK. Implement a\n // checkout endpoint on your backend that calls gbs-service directly.\n\n async getMyProfile(): Promise<any> {\n const token = await this.requireToken();\n return this.raw.getMyProfile(token);\n }\n\n async getMyBalance(): Promise<any> {\n const token = await this.requireToken();\n return this.raw.getMyBalance(token);\n }\n\n async getBalances(query?: Record<string, any>): Promise<any> {\n const token = await this.requireToken();\n return this.raw.getBalances(token, query);\n }\n\n async getBalanceHistory(query?: Record<string, any>): Promise<PagingResponse<any>> {\n const token = await this.requireToken();\n return this.raw.getBalanceHistory(token, query);\n }\n\n async listReferrals(query?: Record<string, any>): Promise<PagingResponse<any>> {\n const token = await this.requireToken();\n return this.raw.listReferrals(token, query);\n }\n\n async setReferral(body: Record<string, any>): Promise<boolean> {\n const token = await this.requireToken();\n return this.raw.setReferral(token, body);\n }\n\n async listTasks(query?: TaskListQuery): Promise<any[]> {\n const token = await this.requireToken();\n return this.raw.listTasks(token, query);\n }\n\n async listFinishedTasks(query?: TaskListQuery): Promise<any[]> {\n const token = await this.requireToken();\n return this.raw.listFinishedTasks(token, query);\n }\n\n async startTask(body: Record<string, any>): Promise<any> {\n const token = await this.requireToken();\n return this.raw.startTask(token, body);\n }\n\n async claimTask(body: Record<string, any>): Promise<any> {\n const token = await this.requireToken();\n return this.raw.claimTask(token, body);\n }\n}\n","import type { LooplayInitParams, LooplaySDKCreateOptions } from './types';\nimport { LooplayAuth } from './auth/looplay-auth';\nimport { ApiClient } from './apps/api-client';\nimport type { EmitGameEventResponse, GameEventKey } from './apps/LooplaySDK.types';\nimport { MissingBaseUrlError, NotInitializedError } from './errors';\n\nexport class LooplaySDK {\n readonly auth?: LooplayAuth;\n readonly api?: ApiClient;\n\n private gameId?: string;\n private initialized = false;\n\n constructor(options: LooplaySDKCreateOptions = {}) {\n const { auth, baseUrl, fetch, defaultHeaders } = options;\n\n if (auth) {\n this.auth = new LooplayAuth(auth);\n }\n\n if (baseUrl) {\n this.api = new ApiClient({\n baseUrl,\n fetch,\n defaultHeaders,\n getAccessToken: async () => this.auth?.getAccessToken(),\n });\n }\n }\n\n async init(params: LooplayInitParams): Promise<void> {\n this.gameId = params.gameId;\n await this.auth?.init();\n await this.verifyGameId(params);\n this.initialized = true;\n }\n\n async getAccessToken(): Promise<string | undefined> {\n return this.auth?.getAccessToken();\n }\n\n getGameId(): string | undefined {\n return this.gameId;\n }\n\n async getMyProfile(): Promise<any> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n return this.api.getMyProfile();\n }\n\n /** Call once when the game view opens, before any play/match tracking. */\n async trackView(): Promise<boolean> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.trackView(this.gameId);\n }\n\n /** Starts a new play attempt; call when the user presses Play. Returns the attempt id. */\n startAttempt(): string {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n return this.api.startAttempt();\n }\n\n async trackPlay(playTimeSeconds?: number): Promise<boolean> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.trackPlay(this.gameId, playTimeSeconds);\n }\n\n /**\n * Report that a match/round ended.\n * Maps to PLAY_MATCH (and WIN_MATCH when isWin=true).\n * matchId is the idempotency anchor — safe to call twice with the same matchId.\n */\n async trackMatch(\n matchId: string,\n opts: { durationSeconds: number; isCompleted?: boolean; isWin?: boolean }\n ): Promise<boolean> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.trackMatch(this.gameId, {\n matchId,\n matchDurationSeconds: opts.durationSeconds,\n isCompleted: opts.isCompleted,\n isWin: opts.isWin,\n });\n }\n\n /**\n * Emit a custom action event (CUSTOM_ACTION metric).\n * Use GameEventKey for platform-defined codes; custom strings for game-specific quests.\n * Pass refId to make the call idempotent — same refId = same event, dedup-safe.\n */\n async emit(\n actionCode: GameEventKey | string,\n opts?: { value?: number; refId?: string; payload?: Record<string, unknown> }\n ): Promise<EmitGameEventResponse> {\n this.assertInitialized();\n if (!this.api) throw new MissingBaseUrlError();\n if (!this.gameId) throw new NotInitializedError();\n return this.api.emit(this.gameId, actionCode, opts);\n }\n\n private assertInitialized(): void {\n if (!this.initialized) throw new NotInitializedError();\n }\n\n private async verifyGameId(params: LooplayInitParams): Promise<void> {\n if ((params.verifyMode ?? 'none') === 'none') return;\n // strict: resolves gameId (the game's public appId) via a read-only,\n // unauthenticated call — throws if the game doesn't exist or isn't LIVE.\n if (!this.api) throw new MissingBaseUrlError();\n await this.api.listGameAssets(params.gameId);\n }\n}\n","export function decodeJwtClaims(token: string | undefined): Record<string, unknown> | undefined {\n if (!token) return undefined;\n const parts = token.split('.');\n if (parts.length < 2) return undefined;\n\n try {\n const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const padded = payload + '==='.slice((payload.length + 3) % 4);\n if (typeof (globalThis as any).atob !== 'function') return undefined;\n\n const json = (globalThis as any).atob(padded) as string;\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\nexport function decodeJwtExpMs(token: string | undefined): number | undefined {\n const exp = decodeJwtClaims(token)?.exp;\n return typeof exp === 'number' ? exp * 1000 : undefined;\n}\n","import { LooplaySDKError } from '../../errors';\nimport { ServiceClient, type ServiceClientOptions } from '../../apps/service-client';\nimport { decodeJwtExpMs } from '../jwt';\nimport type {\n AuthLoginParams,\n AuthLoginResult,\n AuthProvider,\n LooplaySession,\n LooplayUser\n} from '../types';\n\nexport interface TelegramAuthLoginParams extends AuthLoginParams {\n telegramInitData?: string;\n}\n\nexport interface TelegramAuthProviderOptions extends ServiceClientOptions {\n /**\n * Optional override for how to read Telegram init data.\n * If omitted, provider will try `params.telegramInitData` then `globalThis.Telegram.WebApp.initData`.\n */\n getTelegramInitData?: () => string | undefined;\n\n /**\n * Optional mapper from backend user payload to LooplayUser.\n * If omitted, provider will try common fields like id/_id/userId/telegramId.\n */\n mapUser?: (rawUser: Record<string, any>) => LooplayUser;\n}\n\nclass MissingTelegramInitDataError extends LooplaySDKError {\n constructor() {\n super('Missing Telegram init data');\n this.name = 'MissingTelegramInitDataError';\n }\n}\n\nclass MissingRefreshTokenError extends LooplaySDKError {\n constructor() {\n super('Missing refresh token');\n this.name = 'MissingRefreshTokenError';\n }\n}\n\nexport class TelegramAuthProvider implements AuthProvider {\n readonly id = 'telegram';\n\n private readonly client: ServiceClient;\n private readonly getTelegramInitData?: () => string | undefined;\n private readonly mapUser?: (rawUser: Record<string, any>) => LooplayUser;\n\n private session: LooplaySession | null = null;\n\n constructor(options: TelegramAuthProviderOptions) {\n this.client = new ServiceClient(options);\n this.getTelegramInitData = options.getTelegramInitData;\n this.mapUser = options.mapUser;\n }\n\n onSessionChanged = (session: LooplaySession | null): void => {\n this.session = session;\n };\n\n async login(params?: TelegramAuthLoginParams): Promise<AuthLoginResult> {\n const initData =\n params?.telegramInitData ??\n this.getTelegramInitData?.() ??\n (globalThis as any)?.Telegram?.WebApp?.initData;\n\n if (!initData) throw new MissingTelegramInitDataError();\n\n const result = await this.client.telegramLogin(initData);\n\n const rawUser = (result.user ?? {}) as Record<string, any>;\n const user = this.mapUser ? this.mapUser(rawUser) : this.defaultMapUser(rawUser);\n\n const session: LooplaySession = {\n user,\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: decodeJwtExpMs(result.accessToken)\n };\n\n this.session = session;\n return { session };\n }\n\n async refresh(): Promise<Pick<LooplaySession, 'accessToken' | 'refreshToken' | 'expiresAt'>> {\n const currentSession = this.session;\n const refreshToken = currentSession?.refreshToken;\n if (!currentSession || !refreshToken) throw new MissingRefreshTokenError();\n\n const result = await this.client.refresh(refreshToken);\n\n const next = {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: decodeJwtExpMs(result.accessToken)\n } satisfies Pick<LooplaySession, 'accessToken' | 'refreshToken' | 'expiresAt'>;\n\n this.session = { ...currentSession, ...next };\n return next;\n }\n\n async logout(): Promise<void> {\n const refreshToken = this.session?.refreshToken;\n\n if (refreshToken) {\n try {\n await this.client.logout(refreshToken);\n } catch {\n // best-effort\n }\n }\n\n this.session = null;\n }\n\n private defaultMapUser(raw: Record<string, any>): LooplayUser {\n const id = raw.id ?? raw._id ?? raw.userId ?? raw.telegramId;\n if (id === undefined || id === null || String(id).trim() === '') {\n throw new LooplaySDKError('Missing user id from auth response');\n }\n\n return {\n id: String(id),\n displayName: raw.displayName ?? raw.username ?? raw.name,\n avatarUrl: raw.avatarUrl ?? raw.photoUrl ?? raw.avatar\n };\n }\n}\n","import { ApiClient } from \"../apps/api-client\";\nimport type {\n EmitGameEventResponse,\n GameEventKey,\n} from \"../apps/LooplaySDK.types\";\nimport type {\n EmitGameEventOptions,\n LooplayIframeAuthListener,\n LooplayIframeAuthOptions,\n LooplayIframeAuthState,\n RequestAdsOptions,\n TrackMatchOptions,\n} from \"./types\";\n\nconst DEFAULT_ANON_ID_STORAGE_KEY = \"looplay:anon-id\";\nconst DEFAULT_ANONYMOUS_FALLBACK_TIMEOUT_MS = 4000;\nconst DEFAULT_REQUEST_ADS_TIMEOUT_MS = 5000;\n\nconst INITIAL_STATE: LooplayIframeAuthState = {\n jwt: null,\n gameId: null,\n apiUrl: null,\n anonymousId: null,\n origin: null,\n receivedAt: null,\n};\n\ntype LooplayIframeAuthMessage =\n | {\n type: \"LOOPLAY_AUTH\";\n jwt?: unknown;\n gameId?: unknown;\n apiUrl?: unknown;\n }\n | {\n type: \"LOOPLAY_ADS_WATCHED\";\n requestId?: unknown;\n }\n | {\n type: \"LOOPLAY_ADS_UNAVAILABLE\";\n requestId?: unknown;\n };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst toOptionalString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null;\n const normalized = value.trim();\n return normalized ? normalized : null;\n};\n\nfunction generateAnonymousId(): string {\n if (\n typeof crypto !== \"undefined\" &&\n typeof crypto.randomUUID === \"function\"\n ) {\n return crypto.randomUUID();\n }\n\n // Fallback UUID v4 for environments without crypto.randomUUID (older\n // browsers, non-secure contexts). Not cryptographically strong, but only\n // used as an anonymous, non-sensitive device identifier.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction readStoredAnonymousId(key: string): string | null {\n if (typeof window === \"undefined\" || !window.localStorage) return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStoredAnonymousId(key: string, value: string): void {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Private browsing, storage full, or blocked — the id just won't\n // persist across reloads; tracking for this session still works.\n }\n}\n\n/**\n * Handles auth for games that may run in two contexts:\n *\n * 1. Embedded as an iframe inside the Looplay web app — the host page\n * pushes `{ type: 'LOOPLAY_AUTH', jwt?, gameId, apiUrl }` via postMessage\n * in response to a `LOOPLAY_AUTH_REQUEST`. Tracking calls use the real\n * user's bearer token when available, otherwise fall back to anonymous\n * tracking (analytics-only, not eligible for quest/reward).\n * 2. Standalone — published anywhere else, or the parent never responds.\n * Falls back to an anonymous, locally-generated device id. Tracking\n * still works (the backend records it for analytics), but never feeds\n * the quest/reward engine.\n *\n * Either way, `appId` (issued to the creator after registering the game)\n * is required to track — see `LooplayIframeAuthOptions.appId`.\n */\nexport class LooplayIframeAuth {\n private state: LooplayIframeAuthState = INITIAL_STATE;\n private initialized = false;\n private lifecycleTrackingInitialized = false;\n private lastGameStartedKey: string | null = null;\n private client: ApiClient | null = null;\n private readonly listeners = new Set<LooplayIframeAuthListener>();\n private readonly options: LooplayIframeAuthOptions;\n private messageListener: ((event: MessageEvent<unknown>) => void) | null =\n null;\n private anonymousFallbackTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingAdsRequests = new Map<\n string,\n {\n resolve: (value: boolean) => void;\n timer: ReturnType<typeof setTimeout> | null;\n }\n >();\n private requestAdsSequence = 0;\n private adsRequestInFlight = false;\n private adsRequestsSent = 0;\n private lastAdsRequestAt = 0;\n private autoAdsActionCount = 0;\n\n constructor(options: LooplayIframeAuthOptions = {}) {\n this.options = options;\n }\n\n getState(): LooplayIframeAuthState {\n return this.state;\n }\n\n getJwt(): string | null {\n return this.state.jwt;\n }\n\n getApiUrl(): string | null {\n return this.options.apiUrl ?? this.state.apiUrl;\n }\n\n /** Resolves to the configured `appId`, falling back to whatever the parent pushed as `gameId`. */\n getGameId(): string | null {\n return this.options.appId ?? this.state.gameId;\n }\n\n getAnonymousId(): string | null {\n return this.state.anonymousId;\n }\n\n /** True once authenticated (bearer) or anonymous tracking is ready, given a configured `apiUrl`/`appId`. */\n canTrack(): boolean {\n if (!this.getApiUrl() || !this.getGameId()) return false;\n return Boolean(this.state.jwt || this.state.anonymousId);\n }\n\n subscribe(listener: LooplayIframeAuthListener): () => void {\n this.listeners.add(listener);\n listener(this.state);\n return () => this.listeners.delete(listener);\n }\n\n requestAuth(): void {\n if (typeof window === \"undefined\" || window.parent === window) {\n this.debug(\"standalone mode detected; auth request skipped\");\n return;\n }\n\n this.debug(\"requesting auth from parent\", {\n targetOrigin: this.resolveParentOrigin() ?? \"*\",\n });\n window.parent.postMessage(\n { type: \"LOOPLAY_AUTH_REQUEST\" },\n this.resolveParentOrigin() ?? \"*\"\n );\n }\n\n /**\n * Requests the parent host to show a rewarded ad.\n * In a Telegram Mini App setup, the game sends this request from the iframe\n * and the parent page (the Mini App shell) bridges it to Adsgram, then\n * responds with `LOOPLAY_ADS_WATCHED` after the ad finishes.\n */\n requestAds(options: RequestAdsOptions = {}): Promise<boolean> {\n if (typeof window === \"undefined\" || window.parent === window) {\n this.debug(\"requestAds skipped; standalone mode detected\");\n return Promise.resolve(false);\n }\n\n if (this.adsRequestInFlight) {\n this.debug(\"requestAds skipped; another ad request is already in flight\");\n return Promise.resolve(false);\n }\n\n const parentOrigin = this.resolveParentOrigin() ?? \"*\";\n const requestId = `ads-${Date.now()}-${++this.requestAdsSequence}`;\n const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_ADS_TIMEOUT_MS;\n\n this.adsRequestInFlight = true;\n this.adsRequestsSent += 1;\n this.lastAdsRequestAt = Date.now();\n this.debug(\"requesting rewarded ad from parent\", {\n requestId,\n targetOrigin: parentOrigin,\n gameId: this.getGameId(),\n });\n\n return new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n this.pendingAdsRequests.delete(requestId);\n this.adsRequestInFlight = false;\n this.debug(\"requestAds timed out\", { requestId, timeoutMs });\n resolve(false);\n }, timeoutMs);\n\n this.pendingAdsRequests.set(requestId, { resolve, timer });\n\n window.parent.postMessage(\n {\n type: \"LOOPLAY_REQUEST_ADS\",\n requestId,\n gameId: this.getGameId(),\n },\n parentOrigin\n );\n });\n }\n\n /** Idempotent; attaches the postMessage listener (if embedded) and arms the anonymous fallback. */\n init(): void {\n if (this.initialized || typeof window === \"undefined\") return;\n this.initialized = true;\n\n const isEmbedded = window.parent !== window;\n this.debug(\"auth listener initialized\", {\n isIframe: isEmbedded,\n parentOrigin: this.resolveParentOrigin(),\n });\n\n if (!isEmbedded) {\n // No parent frame to push auth at all — go anonymous right away.\n this.activateAnonymousMode();\n return;\n }\n\n const parentOrigin = this.resolveParentOrigin();\n\n if (!parentOrigin) {\n // `document.referrer` can be empty (Referrer-Policy, browser privacy\n // settings, extensions), which silently disables the origin check\n // below and lets any window that is literally `window.parent` push\n // auth. Pass `parentOrigin` explicitly to keep this check effective.\n console.warn(\n \"[LooplaySDK] LooplayIframeAuth could not resolve a parent origin to verify postMessage \" +\n \"against (document.referrer is empty). Auth messages from window.parent will be accepted \" +\n \"regardless of origin. Pass `parentOrigin` explicitly to harden this.\"\n );\n }\n\n this.messageListener = (event: MessageEvent<unknown>) => {\n if (event.source !== window.parent) return;\n if (parentOrigin && event.origin !== parentOrigin) {\n this.debug(\"ignored message from unexpected origin\", {\n expectedOrigin: parentOrigin,\n receivedOrigin: event.origin,\n });\n return;\n }\n\n this.handleIncomingMessage(event.data, event.origin);\n };\n\n window.addEventListener(\"message\", this.messageListener);\n\n // The host page might be an older protocol version, slow, or simply not\n // respond — don't leave the game untrackable forever.\n this.anonymousFallbackTimer = setTimeout(() => {\n if (this.state.jwt) return;\n this.activateAnonymousMode();\n }, this.options.anonymousFallbackTimeoutMs ?? DEFAULT_ANONYMOUS_FALLBACK_TIMEOUT_MS);\n\n this.requestAuth();\n }\n\n /** Removes the postMessage listener, cancels timers, and clears subscribers; safe to call multiple times. */\n dispose(): void {\n if (typeof window !== \"undefined\" && this.messageListener) {\n window.removeEventListener(\"message\", this.messageListener);\n }\n if (this.anonymousFallbackTimer !== null) {\n clearTimeout(this.anonymousFallbackTimer);\n }\n for (const pending of this.pendingAdsRequests.values()) {\n if (pending.timer !== null) clearTimeout(pending.timer);\n pending.resolve(false);\n }\n this.pendingAdsRequests.clear();\n\n this.messageListener = null;\n this.anonymousFallbackTimer = null;\n this.initialized = false;\n this.lifecycleTrackingInitialized = false;\n this.listeners.clear();\n }\n\n /** Lazily builds (and rebuilds on auth change) the `ApiClient` bound to the current auth mode. */\n getClient(): ApiClient {\n if (this.client) return this.client;\n\n const baseUrl = this.getApiUrl();\n\n this.debug(\"creating sdk ApiClient\", { baseUrl });\n this.client = new ApiClient({\n baseUrl: baseUrl ?? undefined,\n getAccessToken: async () => this.getJwt() ?? undefined,\n getAnonymousId: () => this.getAnonymousId() ?? undefined,\n });\n\n return this.client;\n }\n\n /** Call once when the game view opens, before any play/match tracking. */\n trackView(): Promise<boolean> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"trackView skipped; auth is not ready\", {\n hasGameId: Boolean(gameId),\n });\n return Promise.resolve(false);\n }\n\n this.debug(\"trackView dispatched\", { gameId });\n return this.getClient().trackView(gameId);\n }\n\n /** Starts a new play attempt; call when the user presses Play. Returns the attempt id. */\n startAttempt(): string {\n return this.getClient().startAttempt();\n }\n\n trackPlay(playTimeSeconds?: number): Promise<boolean> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"trackPlay skipped; auth is not ready\", {\n hasGameId: Boolean(gameId),\n });\n return Promise.resolve(false);\n }\n\n this.debug(\"trackPlay dispatched\", { gameId, playTimeSeconds });\n return this.getClient().trackPlay(gameId, playTimeSeconds);\n }\n\n trackMatch(matchId: string, opts: TrackMatchOptions): Promise<boolean> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"trackMatch skipped; auth is not ready\", { matchId });\n return Promise.resolve(false);\n }\n\n this.debug(\"trackMatch dispatched\", { gameId, matchId, ...opts });\n const result = this.getClient().trackMatch(gameId, {\n matchId,\n matchDurationSeconds: opts.durationSeconds,\n isCompleted: opts.isCompleted,\n isWin: opts.isWin,\n });\n void result\n .then((ok) => {\n if (ok) this.recordAutoRequestAdsActivity();\n })\n .catch(() => undefined);\n return result;\n }\n\n emitGameEvent(\n actionCode: GameEventKey | string,\n opts?: EmitGameEventOptions\n ): Promise<EmitGameEventResponse | null> {\n const gameId = this.getGameId();\n if (!this.canTrack() || !gameId) {\n this.debug(\"emitGameEvent skipped; auth is not ready\", { actionCode });\n return Promise.resolve(null);\n }\n\n this.debug(\"emitGameEvent dispatched\", { gameId, actionCode, ...opts });\n return this.getClient().emit(gameId, actionCode, opts);\n }\n\n /** Idempotent; auto-emits GAME_STARTED exactly once per distinct auth session (bearer or anonymous). */\n initLifecycleTracking(): void {\n if (this.lifecycleTrackingInitialized || typeof window === \"undefined\")\n return;\n this.lifecycleTrackingInitialized = true;\n this.debug(\"lifecycle tracking initialized\");\n\n this.subscribe(() => {\n const key = this.getTrackingKey();\n if (!key || this.lastGameStartedKey === key) return;\n\n this.lastGameStartedKey = key;\n this.debug(\"auto GAME_STARTED dispatch\");\n void this.emitGameEvent(\"GAME_STARTED\");\n });\n }\n\n private getTrackingKey(): string | null {\n const gameId = this.getGameId();\n const apiUrl = this.getApiUrl();\n const identity = this.state.jwt ?? this.state.anonymousId;\n if (!gameId || !apiUrl || !identity) return null;\n return `${apiUrl}\\n${gameId}\\n${identity}`;\n }\n\n /** Generates (or restores) a persisted anonymous device id and activates anonymous tracking. */\n private activateAnonymousMode(): void {\n if (this.state.anonymousId) return;\n\n const key =\n this.options.anonymousIdStorageKey ?? DEFAULT_ANON_ID_STORAGE_KEY;\n const anonymousId = readStoredAnonymousId(key) ?? generateAnonymousId();\n writeStoredAnonymousId(key, anonymousId);\n\n this.lastGameStartedKey = null;\n this.setState({ ...this.state, anonymousId, receivedAt: Date.now() });\n this.debug(\"anonymous tracking mode active\", { anonymousId });\n }\n\n private handleAuthMessage(\n data: Record<string, unknown>,\n origin: string\n ): void {\n if (this.anonymousFallbackTimer !== null) {\n clearTimeout(this.anonymousFallbackTimer);\n this.anonymousFallbackTimer = null;\n }\n\n const jwt = toOptionalString(data.jwt);\n const gameId = toOptionalString(data.gameId);\n const apiUrl = toOptionalString(data.apiUrl);\n const resolvedGameId = gameId ?? this.getGameId();\n const resolvedApiUrl = apiUrl ?? this.getApiUrl();\n\n if (!resolvedGameId || !resolvedApiUrl) {\n this.lastGameStartedKey = null;\n this.setState({\n ...this.state,\n jwt: null,\n gameId: null,\n apiUrl: null,\n origin,\n receivedAt: Date.now(),\n });\n this.debug(\n \"auth message missing game identity; falling back to anonymous tracking\"\n );\n this.activateAnonymousMode();\n return;\n }\n\n if (!jwt) {\n this.lastGameStartedKey = null;\n this.setState({\n ...this.state,\n jwt: null,\n gameId: resolvedGameId,\n apiUrl: resolvedApiUrl,\n origin,\n receivedAt: Date.now(),\n });\n this.debug(\n \"auth message missing jwt; falling back to anonymous tracking\",\n {\n gameId: resolvedGameId,\n apiUrl: resolvedApiUrl,\n }\n );\n this.activateAnonymousMode();\n return;\n }\n\n this.lastGameStartedKey = null;\n this.setState({\n jwt,\n gameId,\n apiUrl,\n anonymousId: null,\n origin,\n receivedAt: Date.now(),\n });\n }\n\n private handleRequestAdsResult(data: LooplayIframeAuthMessage): void {\n if (data.type !== \"LOOPLAY_ADS_WATCHED\") return;\n\n const requestId = toOptionalString(data.requestId);\n if (!requestId) return;\n\n const pending = this.pendingAdsRequests.get(requestId);\n if (!pending) return;\n\n this.pendingAdsRequests.delete(requestId);\n if (pending.timer !== null) clearTimeout(pending.timer);\n\n this.adsRequestInFlight = false;\n this.debug(\"requestAds watched message received\", { requestId });\n // TODO: call API to grant reward after the rewarded ad has been watched.\n pending.resolve(true);\n }\n\n private handleRequestAdsUnavailable(data: LooplayIframeAuthMessage): void {\n if (data.type !== \"LOOPLAY_ADS_UNAVAILABLE\") return;\n\n const requestId = toOptionalString(data.requestId);\n if (!requestId) return;\n\n const pending = this.pendingAdsRequests.get(requestId);\n if (!pending) return;\n\n this.pendingAdsRequests.delete(requestId);\n if (pending.timer !== null) clearTimeout(pending.timer);\n\n this.adsRequestInFlight = false;\n this.debug(\"requestAds unavailable message received\", { requestId });\n pending.resolve(false);\n }\n\n private handleIncomingMessage(data: unknown, origin: string): void {\n if (!isRecord(data)) return;\n\n if (data.type === \"LOOPLAY_AUTH\") {\n this.handleAuthMessage(data, origin);\n return;\n }\n\n if (data.type === \"LOOPLAY_ADS_WATCHED\") {\n this.handleRequestAdsResult(data as LooplayIframeAuthMessage);\n return;\n }\n\n if (data.type === \"LOOPLAY_ADS_UNAVAILABLE\") {\n this.handleRequestAdsUnavailable(data as LooplayIframeAuthMessage);\n return;\n }\n }\n\n private setState(next: LooplayIframeAuthState): void {\n this.state = next;\n this.client = null;\n this.debug(\"auth state updated\", {\n gameId: this.getGameId(),\n apiUrl: this.getApiUrl(),\n origin: next.origin,\n hasJwt: Boolean(next.jwt),\n hasAnonymousId: Boolean(next.anonymousId),\n });\n this.listeners.forEach((listener) => listener(next));\n }\n\n private resolveParentOrigin(): string | null {\n if (this.options.parentOrigin) return this.options.parentOrigin;\n if (typeof document === \"undefined\" || !document.referrer) return null;\n\n try {\n return new URL(document.referrer).origin;\n } catch {\n return null;\n }\n }\n\n private debug(message: string, details?: Record<string, unknown>): void {\n this.options.onDebug?.(message, details);\n }\n\n private recordAutoRequestAdsActivity(): void {\n this.autoAdsActionCount += 1;\n\n const config = this.options.autoRequestAds;\n if (!config?.enabled) return;\n if (this.adsRequestInFlight) return;\n if (!this.canTrack()) return;\n\n const now = Date.now();\n if (config.cooldownMs && now - this.lastAdsRequestAt < config.cooldownMs) {\n return;\n }\n\n if (config.maxRequestsPerSession && this.adsRequestsSent >= config.maxRequestsPerSession) {\n return;\n }\n\n const threshold = config.afterActionCount ?? 0;\n const shouldRequest = threshold > 0 && this.autoAdsActionCount % threshold === 0;\n\n if (!shouldRequest) return;\n\n const chance = config.chance ?? 1;\n if (chance <= 0 || Math.random() > chance) {\n this.debug(\"auto requestAds skipped by chance\", {\n actionCount: this.autoAdsActionCount,\n chance,\n });\n return;\n }\n\n this.debug(\"auto requestAds triggered\", {\n actionCount: this.autoAdsActionCount,\n });\n\n void this.requestAds();\n }\n}\n"]}
|