@buoy-gg/license 7.0.50 → 7.0.51
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/lib/commonjs/sessionManager.js +1 -1
- package/lib/module/sessionManager.js +1 -1
- package/lib/typescript/commonjs/headless.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +1 -1
- package/lib/typescript/commonjs/sessionManager.d.ts +25 -0
- package/lib/typescript/module/headless.d.ts +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.web.d.ts +1 -1
- package/lib/typescript/module/sessionManager.d.ts +25 -0
- package/lib/web/chunks/LicenseManager-GJQ6MGWU.mjs +1 -0
- package/lib/web/chunks/chunk-FPGC7BWV.mjs +73 -0
- package/lib/web/index.mjs +1 -1
- package/package.json +1 -1
- package/lib/web/chunks/LicenseManager-YKUYIOKJ.mjs +0 -1
- package/lib/web/chunks/chunk-AC36O63C.mjs +0 -73
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
5
5
|
* (https://buoy.gg/terms), not an OSS license.
|
|
6
|
-
*/const SESSION_STORAGE_PREFIX=exports.SESSION_STORAGE_PREFIX="@buoy/session:";const SESSION_OFFLINE_GRACE_MS=exports.SESSION_OFFLINE_GRACE_MS=7*24*60*60*1e3;const SESSION_REFRESH_LEAD_MS=exports.SESSION_REFRESH_LEAD_MS=6*60*60*1e3;const SESSION_RETRY_MS=exports.SESSION_RETRY_MS=5*60*1e3;const SESSION_CALL_TIMEOUT_MS=exports.SESSION_CALL_TIMEOUT_MS=15e3;const DEFAULT_SIGNIN_ORIGIN=exports.DEFAULT_SIGNIN_ORIGIN="https://buoy.gg";const MESSAGE_TYPE="buoy:signin";const POPUP_TIMEOUT_MS=10*60*1e3;class SessionError extends Error{constructor(e,t){super(t);this.code=e;this.name="SessionError"}}exports.SessionError=SessionError;function browserDeps(){const s=globalThis;return{fetch:(e,t)=>fetch(e,t),storage:_storage.getLicenseStorage,now:_realNow.realNow,random:e=>{const t=new Uint8Array(e);if(!s.crypto?.getRandomValues)throw new SessionError("not_configured","This platform has no secure random source.");return s.crypto.getRandomValues(t)},sha256:async e=>{if(!s.crypto?.subtle)throw new SessionError("not_configured","Sign-in needs a secure page (https or localhost).");return new Uint8Array(await s.crypto.subtle.digest("SHA-256",e))},openPopup:()=>{if(!s.window?.open)return null;const e=480;const t=640;const i=Math.max(0,(s.window.screenX??0)+((s.window.outerWidth??e)-e)/2);const n=Math.max(0,(s.window.screenY??0)+((s.window.outerHeight??t)-t)/2);return s.window.open("about:blank","buoy-signin",`popup,width=${e},height=${t},left=${i},top=${n}`)},onMessage:e=>{if(!s.window?.addEventListener)return()=>{};const t=i=>e({origin:i.origin,data:i.data});s.window.addEventListener("message",t);return()=>s.window?.removeEventListener("message",t)},withLock:(e,t)=>s.navigator?.locks?s.navigator.locks.request(e,t):t(),watch:e=>{const t=s.window;if(!t?.addEventListener)return()=>{};const i=r=>e.storage(r.key);const n=()=>{if(t.document?.visibilityState!=="hidden")e.wake()};t.addEventListener("storage",i);t.addEventListener("online",e.wake);t.document?.addEventListener?.("visibilitychange",n);return()=>{t.removeEventListener("storage",i);t.removeEventListener("online",e.wake);t.document?.removeEventListener?.("visibilitychange",n)}},setTimer:(e,t)=>setTimeout(e,Math.min(t,2147483647)),clearTimer:e=>clearTimeout(e)}}function defaultAudience(){const s=globalThis.location;if(!s?.origin||!/^https?:$/.test(s.protocol??""))return void 0;return s.origin}class SessionManagerImpl{signinOrigin=DEFAULT_SIGNIN_ORIGIN;current=null;claims=null;listeners=new Set;timer=null;refreshing=null;loaded=null;unwatch=null;limitTimer=null;epoch=0;constructor(e){this.deps=e??browserDeps();this.audience=defaultAudience()}get origin(){return this.signinOrigin}configure(e){if(e.audience!==void 0)this.audience=e.audience;if(e.signinOrigin!==void 0)this.signinOrigin=e.signinOrigin.replace(/\/$/,"");this.reset()}__setDeps(e){this.deps=e;this.reset()}reset(){this.unwatch?.();this.unwatch=null;this.epoch+=1;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;this.current=null;this.claims=null;this.loaded=null;this.refreshing=null}get storageKey(){return SESSION_STORAGE_PREFIX+(this.audience??"")}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}notify(){for(const e of[...this.listeners]){try{e()}catch(t){console.warn("[Buoy] sign-in listener error:",t)}}}getAccessToken(){const e=this.getStatus();return e.state==="active"&&!e.grace?this.current?.accessToken??null:null}getStatus(){const e=this.claims;if(!e)return{state:"signed_out"};const t=e.exp*1e3;const i=this.deps.now();if(i<(e.iat-_sessionToken.SESSION_CLOCK_SKEW_SECONDS)*1e3)return{state:"signed_out"};if(i<t)return{state:"active",claims:e,grace:false,validUntil:t};if(i<t+SESSION_OFFLINE_GRACE_MS){return{state:"active",claims:e,grace:true,validUntil:t+SESSION_OFFLINE_GRACE_MS}}return{state:"signed_out"}}load(){if(!this.loaded)this.loaded=this.loadOnce();return this.loaded}async loadOnce(){if(!this.audience)return;this.unwatch??=this.deps.watch?.({storage:t=>{if(t===null||t===this.storageKey)void this.sync()},wake:()=>{if(this.claims)void this.refresh()}})??null;const e=await this.read();if(e&&this.adopt(e)){this.notify();this.schedule()}}async sync(){if(!this.audience)return;const e=await this.read();if(!e){if(this.current)this.clearLocal();return}if(e.refreshToken!==this.current?.refreshToken&&this.adopt(e)){this.notify();this.schedule()}}async read(){try{const e=await(await this.deps.storage()).getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t?.v===1&&typeof t.accessToken==="string"&&typeof t.refreshToken==="string"?t:null}catch{return null}}async write(e){const t=await this.deps.storage();if(e)await t.setItem(this.storageKey,JSON.stringify(e));else await t.removeItem(this.storageKey)}adopt(e){if(!this.audience)return false;const t=(0,_sessionToken.verifySessionToken)(e.accessToken,{audience:this.audience,ignoreTime:true,publicKeyHex:this.deps.publicKeyHex});const i=t.ok?t.claims:null;if(!i)return false;this.current=e;this.claims=i;return true}armLimit(){if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(!this.claims)return;const e=this.claims.exp*1e3;const t=this.deps.now();const i=t<e?e:t<e+SESSION_OFFLINE_GRACE_MS?e+SESSION_OFFLINE_GRACE_MS:null;if(i===null)return;this.limitTimer=this.deps.setTimer(()=>{this.limitTimer=null;this.notify();this.armLimit()},i-t)}call(e,t){return new Promise((i,n)=>{const r=this.deps.setTimer(()=>n(new Error("timeout")),SESSION_CALL_TIMEOUT_MS);this.deps.fetch(e,t).then(o=>{this.deps.clearTimer(r);i(o)},o=>{this.deps.clearTimer(r);n(o)})})}schedule(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(!this.claims)return;const e=this.claims.exp*1e3-SESSION_REFRESH_LEAD_MS-this.deps.now();this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh()},Math.max(0,e))}refresh(e=false){if(!this.refreshing){this.refreshing=this.deps.withLock(this.lockName,()=>this.refreshLocked(e)).catch(()=>this.retryLater()).finally(()=>{this.refreshing=null})}return this.refreshing}get lockName(){return`buoy-session:${this.audience??""}`}async refreshLocked(e){const t=this.epoch;const i=await this.read();if(!i){if(this.current)this.clearLocal();return}if(i.refreshToken!==this.current?.refreshToken){if(this.adopt(i)){this.notify();this.schedule();return}}if(!this.claims)return;if(!e&&this.claims.exp*1e3-this.deps.now()>SESSION_REFRESH_LEAD_MS){this.schedule();return}let n;let r;try{const o=await this.call(`${this.signinOrigin}/api/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:i.refreshToken,audience:this.audience})});n=o.status;r=await o.json().catch(()=>null)}catch{this.retryLater();return}if(t!==this.epoch)return;if(n===200&&r&&typeof r.access_token==="string"&&typeof r.refresh_token==="string"){const o={v:1,accessToken:r.access_token,refreshToken:r.refresh_token};if(this.adopt(o)){await this.write(o);this.notify();this.schedule();return}}if(n===400||n===401||n===403){await this.write(null);this.clearLocal();return}this.retryLater()}retryLater(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh(true)},SESSION_RETRY_MS);this.notify()}clearLocal(){if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;this.current=null;this.claims=null;this.notify()}async signIn(){if(!this.audience)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const e=this.deps.openPopup();if(!e)throw new SessionError("popup_blocked","The browser blocked the sign-in window. Allow pop-ups for this site and try again.");try{return await this.signInWith((t,i)=>new Promise((n,r)=>{e.location.href=t;const o=this.deps.now();let h=null;const c=l=>{if(h!==null)this.deps.clearTimer(h);a();l()};const a=this.deps.onMessage(l=>{if(l.origin!==this.signinOrigin)return;const d=l.data;if(!d||d.type!==MESSAGE_TYPE||d.state!==i)return;if(typeof d.code==="string")c(()=>n(d.code));else c(()=>r(new SessionError("access_denied","Sign-in was cancelled.")))});const u=()=>{if(e.closed)return c(()=>r(new SessionError("popup_closed","The sign-in window was closed.")));if(this.deps.now()-o>POPUP_TIMEOUT_MS)return c(()=>r(new SessionError("timeout","Sign-in took too long.")));h=this.deps.setTimer(u,500)};h=this.deps.setTimer(u,500)}))}finally{try{if(!e.closed)e.close()}catch{}}}async signInWith(e){const t=this.audience;if(!t)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const i=(0,_sessionToken.base64urlEncode)(this.deps.random(32));const n=(0,_sessionToken.base64urlEncode)(this.deps.random(16));const r=(0,_sessionToken.base64urlEncode)(await this.deps.sha256(new TextEncoder().encode(i)));const o=`${this.signinOrigin}/auth/authorize?audience=${encodeURIComponent(t)}&code_challenge=${r}&code_challenge_method=S256&state=${n}`;const h=await e(o,n);let c;try{c=await this.call(`${this.signinOrigin}/api/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:h,code_verifier:i,audience:t})})}catch{throw new SessionError("network","Buoy could not be reached. Check your connection and try again.")}const a=await c.json().catch(()=>null);if(c.status!==200||!a||typeof a.access_token!=="string"||typeof a.refresh_token!=="string"){const l=a&&typeof a.error_description==="string"?a.error_description:"Buoy did not accept this sign-in.";throw new SessionError(c.status===503?"unavailable":"rejected",l)}const u={v:1,accessToken:a.access_token,refreshToken:a.refresh_token};this.epoch+=1;if(!this.adopt(u))throw new SessionError("rejected","Buoy sent a sign-in this app could not check.");await this.write(u);await this.load();this.notify();this.schedule();return this.getStatus()}async signOut(){this.epoch+=1;const e=this.current;this.clearLocal();let t=e;try{await this.deps.withLock(this.lockName,async()=>{t=await this.read()??e;await this.write(null)})}catch{await this.write(null).catch(()=>{})}if(!t)return;try{await this.call(`${this.signinOrigin}/api/auth/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:t.refreshToken})})}catch{}}}exports.SessionManagerImpl=SessionManagerImpl;const SessionManager=exports.SessionManager=new SessionManagerImpl;
|
|
6
|
+
*/const SESSION_STORAGE_PREFIX=exports.SESSION_STORAGE_PREFIX="@buoy/session:";const SESSION_OFFLINE_GRACE_MS=exports.SESSION_OFFLINE_GRACE_MS=7*24*60*60*1e3;const SESSION_REFRESH_LEAD_MS=exports.SESSION_REFRESH_LEAD_MS=6*60*60*1e3;const SESSION_RETRY_MS=exports.SESSION_RETRY_MS=5*60*1e3;const SESSION_CALL_TIMEOUT_MS=exports.SESSION_CALL_TIMEOUT_MS=15e3;const DEFAULT_SIGNIN_ORIGIN=exports.DEFAULT_SIGNIN_ORIGIN="https://buoy.gg";const MESSAGE_TYPE="buoy:signin";const POPUP_TIMEOUT_MS=10*60*1e3;class SessionError extends Error{constructor(e,t){super(t);this.code=e;this.name="SessionError"}}exports.SessionError=SessionError;function browserDeps(){const o=globalThis;return{fetch:(e,t)=>fetch(e,t),storage:_storage.getLicenseStorage,now:_realNow.realNow,random:e=>{const t=new Uint8Array(e);if(!o.crypto?.getRandomValues)throw new SessionError("not_configured","This platform has no secure random source.");return o.crypto.getRandomValues(t)},sha256:async e=>{if(!o.crypto?.subtle)throw new SessionError("not_configured","Sign-in needs a secure page (https or localhost).");return new Uint8Array(await o.crypto.subtle.digest("SHA-256",e))},openPopup:()=>{if(!o.window?.open)return null;const e=480;const t=640;const i=Math.max(0,(o.window.screenX??0)+((o.window.outerWidth??e)-e)/2);const r=Math.max(0,(o.window.screenY??0)+((o.window.outerHeight??t)-t)/2);return o.window.open("about:blank","buoy-signin",`popup,width=${e},height=${t},left=${i},top=${r}`)},onMessage:e=>{if(!o.window?.addEventListener)return()=>{};const t=i=>e({origin:i.origin,data:i.data});o.window.addEventListener("message",t);return()=>o.window?.removeEventListener("message",t)},withLock:(e,t)=>o.navigator?.locks?o.navigator.locks.request(e,t):t(),watch:e=>{const t=o.window;if(!t?.addEventListener)return()=>{};const i=n=>e.storage(n.key);const r=()=>{if(t.document?.visibilityState!=="hidden")e.wake()};t.addEventListener("storage",i);t.addEventListener("online",e.wake);t.document?.addEventListener?.("visibilitychange",r);return()=>{t.removeEventListener("storage",i);t.removeEventListener("online",e.wake);t.document?.removeEventListener?.("visibilitychange",r)}},setTimer:(e,t)=>setTimeout(e,Math.min(t,2147483647)),clearTimer:e=>clearTimeout(e)}}function defaultAudience(){const o=globalThis.location;if(!o?.origin||!/^https?:$/.test(o.protocol??""))return void 0;return o.origin}class SessionManagerImpl{signinOrigin=DEFAULT_SIGNIN_ORIGIN;current=null;claims=null;listeners=new Set;timer=null;refreshing=null;loaded=null;unwatch=null;limitTimer=null;epoch=0;constructor(e){this.deps=e??browserDeps();this.audience=defaultAudience()}get origin(){return this.signinOrigin}configure(e){if(e.audience!==void 0)this.audience=e.audience;if(e.signinOrigin!==void 0)this.signinOrigin=e.signinOrigin.replace(/\/$/,"");this.reset()}__setDeps(e){this.deps=e;this.reset()}reset(){this.unwatch?.();this.unwatch=null;this.epoch+=1;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;this.current=null;this.claims=null;this.loaded=null;this.refreshing=null}get storageKey(){return SESSION_STORAGE_PREFIX+(this.audience??"")}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}notify(){for(const e of[...this.listeners]){try{e()}catch(t){console.warn("[Buoy] sign-in listener error:",t)}}}getAccessToken(){const e=this.getStatus();return e.state==="active"&&!e.grace?this.current?.accessToken??null:null}getStatus(){const e=this.claims;if(!e)return{state:"signed_out"};const t=e.exp*1e3;const i=this.deps.now();if(i<(e.iat-_sessionToken.SESSION_CLOCK_SKEW_SECONDS)*1e3)return{state:"signed_out"};if(i<t)return{state:"active",claims:e,grace:false,validUntil:t};if(i<t+SESSION_OFFLINE_GRACE_MS){return{state:"active",claims:e,grace:true,validUntil:t+SESSION_OFFLINE_GRACE_MS}}return{state:"signed_out"}}load(){if(!this.loaded)this.loaded=this.loadOnce();return this.loaded}async loadOnce(){if(!this.audience)return;this.unwatch??=this.deps.watch?.({storage:t=>{if(t===null||t===this.storageKey)void this.sync()},wake:()=>{if(this.claims)void this.refresh()}})??null;const e=await this.read();if(e&&this.adopt(e)){this.notify();this.schedule()}}async sync(){if(!this.audience)return;const e=await this.read();if(!e){if(this.current)this.clearLocal();return}if(e.refreshToken!==this.current?.refreshToken&&this.adopt(e)){this.notify();this.schedule()}}async read(){try{const e=await(await this.deps.storage()).getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t?.v===1&&typeof t.accessToken==="string"&&typeof t.refreshToken==="string"?t:null}catch{return null}}async write(e){const t=await this.deps.storage();if(e)await t.setItem(this.storageKey,JSON.stringify(e));else await t.removeItem(this.storageKey)}adopt(e){if(!this.audience)return false;const t=(0,_sessionToken.verifySessionToken)(e.accessToken,{audience:this.audience,ignoreTime:true,publicKeyHex:this.deps.publicKeyHex});const i=t.ok?t.claims:null;if(!i)return false;this.current=e;this.claims=i;return true}armLimit(){if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(!this.claims)return;const e=this.claims.exp*1e3;const t=this.deps.now();const i=t<e?e:t<e+SESSION_OFFLINE_GRACE_MS?e+SESSION_OFFLINE_GRACE_MS:null;if(i===null)return;this.limitTimer=this.deps.setTimer(()=>{this.limitTimer=null;this.notify();this.armLimit()},i-t)}call(e,t){return new Promise((i,r)=>{const n=this.deps.setTimer(()=>r(new Error("timeout")),SESSION_CALL_TIMEOUT_MS);this.deps.fetch(e,t).then(s=>{this.deps.clearTimer(n);i(s)},s=>{this.deps.clearTimer(n);r(s)})})}schedule(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(!this.claims)return;const e=this.claims.exp*1e3-SESSION_REFRESH_LEAD_MS-this.deps.now();this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh()},Math.max(0,e))}refresh(e=false){if(!this.refreshing){this.refreshing=this.deps.withLock(this.lockName,()=>this.refreshLocked(e)).catch(()=>this.retryLater()).finally(()=>{this.refreshing=null})}return this.refreshing}get lockName(){return`buoy-session:${this.audience??""}`}async refreshLocked(e){const t=this.epoch;const i=await this.read();if(!i){if(this.current)this.clearLocal();return}if(i.refreshToken!==this.current?.refreshToken){if(this.adopt(i)){this.notify();this.schedule();return}}if(!this.claims)return;if(!e&&this.claims.exp*1e3-this.deps.now()>SESSION_REFRESH_LEAD_MS){this.schedule();return}let r;let n;try{const s=await this.call(`${this.signinOrigin}/api/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:i.refreshToken,audience:this.audience})});r=s.status;n=await s.json().catch(()=>null)}catch{this.retryLater();return}if(t!==this.epoch)return;if(r===200&&n&&typeof n.access_token==="string"&&typeof n.refresh_token==="string"){const s={v:1,accessToken:n.access_token,refreshToken:n.refresh_token};if(this.adopt(s)){await this.write(s);this.notify();this.schedule();return}}if(r===400||r===401||r===403){await this.write(null);this.clearLocal();return}this.retryLater()}retryLater(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh(true)},SESSION_RETRY_MS);this.notify()}clearLocal(){if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;this.current=null;this.claims=null;this.notify()}async signIn(){if(!this.audience)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const e=this.deps.openPopup();if(!e)throw new SessionError("popup_blocked","The browser blocked the sign-in window. Allow pop-ups for this site and try again.");try{return await this.signInWith((t,i)=>new Promise((r,n)=>{e.location.href=t;const s=this.deps.now();let l=null;const h=u=>{if(l!==null)this.deps.clearTimer(l);a();u()};const a=this.deps.onMessage(u=>{if(u.origin!==this.signinOrigin)return;const d=u.data;if(!d||d.type!==MESSAGE_TYPE||d.state!==i)return;if(typeof d.code==="string")h(()=>r(d.code));else h(()=>n(new SessionError("access_denied","Sign-in was cancelled.")))});const c=()=>{if(e.closed)return h(()=>n(new SessionError("popup_closed","The sign-in window was closed.")));if(this.deps.now()-s>POPUP_TIMEOUT_MS)return h(()=>n(new SessionError("timeout","Sign-in took too long.")));l=this.deps.setTimer(c,500)};l=this.deps.setTimer(c,500)}))}finally{try{if(!e.closed)e.close()}catch{}}}async signInWith(e){const t=this.audience;if(!t)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const i=(0,_sessionToken.base64urlEncode)(this.deps.random(32));const r=(0,_sessionToken.base64urlEncode)(this.deps.random(16));const n=(0,_sessionToken.base64urlEncode)(await this.deps.sha256(new TextEncoder().encode(i)));const s=`${this.signinOrigin}/auth/authorize?audience=${encodeURIComponent(t)}&code_challenge=${n}&code_challenge_method=S256&state=${r}`;const l=await e(s,r);let h;try{h=await this.call(`${this.signinOrigin}/api/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:l,code_verifier:i,audience:t})})}catch{throw new SessionError("network","Buoy could not be reached. Check your connection and try again.")}const a=await h.json().catch(()=>null);if(h.status!==200||!a||typeof a.access_token!=="string"||typeof a.refresh_token!=="string"){const c=a&&typeof a.error_description==="string"?a.error_description:"Buoy did not accept this sign-in.";throw new SessionError(h.status===503?"unavailable":"rejected",c)}return this.finishSignIn(a.access_token,a.refresh_token)}async finishSignIn(e,t){const i={v:1,accessToken:e,refreshToken:t};this.epoch+=1;if(!this.adopt(i))throw new SessionError("rejected","Buoy sent a sign-in this app could not check.");await this.write(i);await this.load();this.notify();this.schedule();return this.getStatus()}async signInWithCode(e,t={}){const i=this.audience;if(!i)throw new SessionError("not_configured","Sign-in with a code needs this app's id (app:com.example.app).");const r=(a,c)=>this.call(`${this.signinOrigin}/api/auth/device/${a}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)});let n;try{n=await r("start",{audience:i})}catch{throw new SessionError("network","Buoy could not be reached. Check your connection and try again.")}const s=await n.json().catch(()=>null);if(n.status!==200||!s||typeof s.device_code!=="string"||typeof s.user_code!=="string"||typeof s.verification_uri!=="string"||typeof s.verification_uri_complete!=="string"){const a=s&&typeof s.error_description==="string"?s.error_description:"Buoy could not start a sign-in.";throw new SessionError(n.status===503?"unavailable":"rejected",a)}const l=this.deps.now()+(Number(s.expires_in)||600)*1e3;let h=Math.max(5,Number(s.interval)||5)*1e3;e({userCode:s.user_code,verificationUri:s.verification_uri,verificationUriComplete:s.verification_uri_complete,expiresAt:l});for(;;){await this.wait(h,t.signal);if(this.deps.now()>l)throw new SessionError("timeout","The code ran out. Start again for a new one.");let a;try{a=await r("poll",{device_code:s.device_code})}catch{continue}const c=await a.json().catch(()=>null);if(a.status===200&&c&&typeof c.access_token==="string"&&typeof c.refresh_token==="string"){return this.finishSignIn(c.access_token,c.refresh_token)}const u=c&&typeof c.error==="string"?c.error:"";if(u==="authorization_pending"||a.status>=500)continue;if(u==="slow_down"||a.status===429){h+=5e3;continue}if(u==="access_denied")throw new SessionError("access_denied","The sign-in was turned down on buoy.gg.");if(u==="expired_token")throw new SessionError("timeout","The code ran out. Start again for a new one.");const d=c&&typeof c.error_description==="string"?c.error_description:"Buoy did not accept this sign-in.";throw new SessionError("rejected",d)}}wait(e,t){return new Promise((i,r)=>{const n=()=>new SessionError("access_denied","Sign-in was cancelled.");if(t?.aborted)return r(n());const s=()=>{this.deps.clearTimer(l);r(n())};const l=this.deps.setTimer(()=>{t?.removeEventListener?.("abort",s);i()},e);t?.addEventListener?.("abort",s)})}async signOut(){this.epoch+=1;const e=this.current;this.clearLocal();let t=e;try{await this.deps.withLock(this.lockName,async()=>{t=await this.read()??e;await this.write(null)})}catch{await this.write(null).catch(()=>{})}if(!t)return;try{await this.call(`${this.signinOrigin}/api/auth/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:t.refreshToken})})}catch{}}}exports.SessionManagerImpl=SessionManagerImpl;const SessionManager=exports.SessionManager=new SessionManagerImpl;
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
5
5
|
* (https://buoy.gg/terms), not an OSS license.
|
|
6
|
-
*/import{getLicenseStorage as p}from"./storage";import{base64urlEncode as f,verifySessionToken as m,SESSION_CLOCK_SKEW_SECONDS as g}from"./sessionToken.js";import{realNow as w}from"./realNow.js";export const SESSION_STORAGE_PREFIX="@buoy/session:";export const SESSION_OFFLINE_GRACE_MS=7*24*60*60*1e3;export const SESSION_REFRESH_LEAD_MS=6*60*60*1e3;export const SESSION_RETRY_MS=5*60*1e3;export const SESSION_CALL_TIMEOUT_MS=15e3;export const DEFAULT_SIGNIN_ORIGIN="https://buoy.gg";const y="buoy:signin";const T=10*60*1e3;export class SessionError extends Error{constructor(e,t){super(t);this.code=e;this.name="SessionError"}}function S(){const s=globalThis;return{fetch:(e,t)=>fetch(e,t),storage:p,now:w,random:e=>{const t=new Uint8Array(e);if(!s.crypto?.getRandomValues)throw new SessionError("not_configured","This platform has no secure random source.");return s.crypto.getRandomValues(t)},sha256:async e=>{if(!s.crypto?.subtle)throw new SessionError("not_configured","Sign-in needs a secure page (https or localhost).");return new Uint8Array(await s.crypto.subtle.digest("SHA-256",e))},openPopup:()=>{if(!s.window?.open)return null;const e=480;const t=640;const i=Math.max(0,(s.window.screenX??0)+((s.window.outerWidth??e)-e)/2);const n=Math.max(0,(s.window.screenY??0)+((s.window.outerHeight??t)-t)/2);return s.window.open("about:blank","buoy-signin",`popup,width=${e},height=${t},left=${i},top=${n}`)},onMessage:e=>{if(!s.window?.addEventListener)return()=>{};const t=i=>e({origin:i.origin,data:i.data});s.window.addEventListener("message",t);return()=>s.window?.removeEventListener("message",t)},withLock:(e,t)=>s.navigator?.locks?s.navigator.locks.request(e,t):t(),watch:e=>{const t=s.window;if(!t?.addEventListener)return()=>{};const i=r=>e.storage(r.key);const n=()=>{if(t.document?.visibilityState!=="hidden")e.wake()};t.addEventListener("storage",i);t.addEventListener("online",e.wake);t.document?.addEventListener?.("visibilitychange",n);return()=>{t.removeEventListener("storage",i);t.removeEventListener("online",e.wake);t.document?.removeEventListener?.("visibilitychange",n)}},setTimer:(e,t)=>setTimeout(e,Math.min(t,2147483647)),clearTimer:e=>clearTimeout(e)}}function k(){const s=globalThis.location;if(!s?.origin||!/^https?:$/.test(s.protocol??""))return void 0;return s.origin}export class SessionManagerImpl{signinOrigin=DEFAULT_SIGNIN_ORIGIN;current=null;claims=null;listeners=new Set;timer=null;refreshing=null;loaded=null;unwatch=null;limitTimer=null;epoch=0;constructor(e){this.deps=e??S();this.audience=k()}get origin(){return this.signinOrigin}configure(e){if(e.audience!==void 0)this.audience=e.audience;if(e.signinOrigin!==void 0)this.signinOrigin=e.signinOrigin.replace(/\/$/,"");this.reset()}__setDeps(e){this.deps=e;this.reset()}reset(){this.unwatch?.();this.unwatch=null;this.epoch+=1;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;this.current=null;this.claims=null;this.loaded=null;this.refreshing=null}get storageKey(){return SESSION_STORAGE_PREFIX+(this.audience??"")}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}notify(){for(const e of[...this.listeners]){try{e()}catch(t){console.warn("[Buoy] sign-in listener error:",t)}}}getAccessToken(){const e=this.getStatus();return e.state==="active"&&!e.grace?this.current?.accessToken??null:null}getStatus(){const e=this.claims;if(!e)return{state:"signed_out"};const t=e.exp*1e3;const i=this.deps.now();if(i<(e.iat-g)*1e3)return{state:"signed_out"};if(i<t)return{state:"active",claims:e,grace:false,validUntil:t};if(i<t+SESSION_OFFLINE_GRACE_MS){return{state:"active",claims:e,grace:true,validUntil:t+SESSION_OFFLINE_GRACE_MS}}return{state:"signed_out"}}load(){if(!this.loaded)this.loaded=this.loadOnce();return this.loaded}async loadOnce(){if(!this.audience)return;this.unwatch??=this.deps.watch?.({storage:t=>{if(t===null||t===this.storageKey)void this.sync()},wake:()=>{if(this.claims)void this.refresh()}})??null;const e=await this.read();if(e&&this.adopt(e)){this.notify();this.schedule()}}async sync(){if(!this.audience)return;const e=await this.read();if(!e){if(this.current)this.clearLocal();return}if(e.refreshToken!==this.current?.refreshToken&&this.adopt(e)){this.notify();this.schedule()}}async read(){try{const e=await(await this.deps.storage()).getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t?.v===1&&typeof t.accessToken==="string"&&typeof t.refreshToken==="string"?t:null}catch{return null}}async write(e){const t=await this.deps.storage();if(e)await t.setItem(this.storageKey,JSON.stringify(e));else await t.removeItem(this.storageKey)}adopt(e){if(!this.audience)return false;const t=m(e.accessToken,{audience:this.audience,ignoreTime:true,publicKeyHex:this.deps.publicKeyHex});const i=t.ok?t.claims:null;if(!i)return false;this.current=e;this.claims=i;return true}armLimit(){if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(!this.claims)return;const e=this.claims.exp*1e3;const t=this.deps.now();const i=t<e?e:t<e+SESSION_OFFLINE_GRACE_MS?e+SESSION_OFFLINE_GRACE_MS:null;if(i===null)return;this.limitTimer=this.deps.setTimer(()=>{this.limitTimer=null;this.notify();this.armLimit()},i-t)}call(e,t){return new Promise((i,n)=>{const r=this.deps.setTimer(()=>n(new Error("timeout")),SESSION_CALL_TIMEOUT_MS);this.deps.fetch(e,t).then(o=>{this.deps.clearTimer(r);i(o)},o=>{this.deps.clearTimer(r);n(o)})})}schedule(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(!this.claims)return;const e=this.claims.exp*1e3-SESSION_REFRESH_LEAD_MS-this.deps.now();this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh()},Math.max(0,e))}refresh(e=false){if(!this.refreshing){this.refreshing=this.deps.withLock(this.lockName,()=>this.refreshLocked(e)).catch(()=>this.retryLater()).finally(()=>{this.refreshing=null})}return this.refreshing}get lockName(){return`buoy-session:${this.audience??""}`}async refreshLocked(e){const t=this.epoch;const i=await this.read();if(!i){if(this.current)this.clearLocal();return}if(i.refreshToken!==this.current?.refreshToken){if(this.adopt(i)){this.notify();this.schedule();return}}if(!this.claims)return;if(!e&&this.claims.exp*1e3-this.deps.now()>SESSION_REFRESH_LEAD_MS){this.schedule();return}let n;let r;try{const o=await this.call(`${this.signinOrigin}/api/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:i.refreshToken,audience:this.audience})});n=o.status;r=await o.json().catch(()=>null)}catch{this.retryLater();return}if(t!==this.epoch)return;if(n===200&&r&&typeof r.access_token==="string"&&typeof r.refresh_token==="string"){const o={v:1,accessToken:r.access_token,refreshToken:r.refresh_token};if(this.adopt(o)){await this.write(o);this.notify();this.schedule();return}}if(n===400||n===401||n===403){await this.write(null);this.clearLocal();return}this.retryLater()}retryLater(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh(true)},SESSION_RETRY_MS);this.notify()}clearLocal(){if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;this.current=null;this.claims=null;this.notify()}async signIn(){if(!this.audience)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const e=this.deps.openPopup();if(!e)throw new SessionError("popup_blocked","The browser blocked the sign-in window. Allow pop-ups for this site and try again.");try{return await this.signInWith((t,i)=>new Promise((n,r)=>{e.location.href=t;const o=this.deps.now();let c=null;const h=l=>{if(c!==null)this.deps.clearTimer(c);a();l()};const a=this.deps.onMessage(l=>{if(l.origin!==this.signinOrigin)return;const d=l.data;if(!d||d.type!==y||d.state!==i)return;if(typeof d.code==="string")h(()=>n(d.code));else h(()=>r(new SessionError("access_denied","Sign-in was cancelled.")))});const u=()=>{if(e.closed)return h(()=>r(new SessionError("popup_closed","The sign-in window was closed.")));if(this.deps.now()-o>T)return h(()=>r(new SessionError("timeout","Sign-in took too long.")));c=this.deps.setTimer(u,500)};c=this.deps.setTimer(u,500)}))}finally{try{if(!e.closed)e.close()}catch{}}}async signInWith(e){const t=this.audience;if(!t)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const i=f(this.deps.random(32));const n=f(this.deps.random(16));const r=f(await this.deps.sha256(new TextEncoder().encode(i)));const o=`${this.signinOrigin}/auth/authorize?audience=${encodeURIComponent(t)}&code_challenge=${r}&code_challenge_method=S256&state=${n}`;const c=await e(o,n);let h;try{h=await this.call(`${this.signinOrigin}/api/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:c,code_verifier:i,audience:t})})}catch{throw new SessionError("network","Buoy could not be reached. Check your connection and try again.")}const a=await h.json().catch(()=>null);if(h.status!==200||!a||typeof a.access_token!=="string"||typeof a.refresh_token!=="string"){const l=a&&typeof a.error_description==="string"?a.error_description:"Buoy did not accept this sign-in.";throw new SessionError(h.status===503?"unavailable":"rejected",l)}const u={v:1,accessToken:a.access_token,refreshToken:a.refresh_token};this.epoch+=1;if(!this.adopt(u))throw new SessionError("rejected","Buoy sent a sign-in this app could not check.");await this.write(u);await this.load();this.notify();this.schedule();return this.getStatus()}async signOut(){this.epoch+=1;const e=this.current;this.clearLocal();let t=e;try{await this.deps.withLock(this.lockName,async()=>{t=await this.read()??e;await this.write(null)})}catch{await this.write(null).catch(()=>{})}if(!t)return;try{await this.call(`${this.signinOrigin}/api/auth/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:t.refreshToken})})}catch{}}}export const SessionManager=new SessionManagerImpl;
|
|
6
|
+
*/import{getLicenseStorage as p}from"./storage";import{base64urlEncode as f,verifySessionToken as g,SESSION_CLOCK_SKEW_SECONDS as w}from"./sessionToken.js";import{realNow as m}from"./realNow.js";export const SESSION_STORAGE_PREFIX="@buoy/session:";export const SESSION_OFFLINE_GRACE_MS=7*24*60*60*1e3;export const SESSION_REFRESH_LEAD_MS=6*60*60*1e3;export const SESSION_RETRY_MS=5*60*1e3;export const SESSION_CALL_TIMEOUT_MS=15e3;export const DEFAULT_SIGNIN_ORIGIN="https://buoy.gg";const y="buoy:signin";const T=10*60*1e3;export class SessionError extends Error{constructor(e,t){super(t);this.code=e;this.name="SessionError"}}function _(){const o=globalThis;return{fetch:(e,t)=>fetch(e,t),storage:p,now:m,random:e=>{const t=new Uint8Array(e);if(!o.crypto?.getRandomValues)throw new SessionError("not_configured","This platform has no secure random source.");return o.crypto.getRandomValues(t)},sha256:async e=>{if(!o.crypto?.subtle)throw new SessionError("not_configured","Sign-in needs a secure page (https or localhost).");return new Uint8Array(await o.crypto.subtle.digest("SHA-256",e))},openPopup:()=>{if(!o.window?.open)return null;const e=480;const t=640;const i=Math.max(0,(o.window.screenX??0)+((o.window.outerWidth??e)-e)/2);const r=Math.max(0,(o.window.screenY??0)+((o.window.outerHeight??t)-t)/2);return o.window.open("about:blank","buoy-signin",`popup,width=${e},height=${t},left=${i},top=${r}`)},onMessage:e=>{if(!o.window?.addEventListener)return()=>{};const t=i=>e({origin:i.origin,data:i.data});o.window.addEventListener("message",t);return()=>o.window?.removeEventListener("message",t)},withLock:(e,t)=>o.navigator?.locks?o.navigator.locks.request(e,t):t(),watch:e=>{const t=o.window;if(!t?.addEventListener)return()=>{};const i=n=>e.storage(n.key);const r=()=>{if(t.document?.visibilityState!=="hidden")e.wake()};t.addEventListener("storage",i);t.addEventListener("online",e.wake);t.document?.addEventListener?.("visibilitychange",r);return()=>{t.removeEventListener("storage",i);t.removeEventListener("online",e.wake);t.document?.removeEventListener?.("visibilitychange",r)}},setTimer:(e,t)=>setTimeout(e,Math.min(t,2147483647)),clearTimer:e=>clearTimeout(e)}}function S(){const o=globalThis.location;if(!o?.origin||!/^https?:$/.test(o.protocol??""))return void 0;return o.origin}export class SessionManagerImpl{signinOrigin=DEFAULT_SIGNIN_ORIGIN;current=null;claims=null;listeners=new Set;timer=null;refreshing=null;loaded=null;unwatch=null;limitTimer=null;epoch=0;constructor(e){this.deps=e??_();this.audience=S()}get origin(){return this.signinOrigin}configure(e){if(e.audience!==void 0)this.audience=e.audience;if(e.signinOrigin!==void 0)this.signinOrigin=e.signinOrigin.replace(/\/$/,"");this.reset()}__setDeps(e){this.deps=e;this.reset()}reset(){this.unwatch?.();this.unwatch=null;this.epoch+=1;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;this.current=null;this.claims=null;this.loaded=null;this.refreshing=null}get storageKey(){return SESSION_STORAGE_PREFIX+(this.audience??"")}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}notify(){for(const e of[...this.listeners]){try{e()}catch(t){console.warn("[Buoy] sign-in listener error:",t)}}}getAccessToken(){const e=this.getStatus();return e.state==="active"&&!e.grace?this.current?.accessToken??null:null}getStatus(){const e=this.claims;if(!e)return{state:"signed_out"};const t=e.exp*1e3;const i=this.deps.now();if(i<(e.iat-w)*1e3)return{state:"signed_out"};if(i<t)return{state:"active",claims:e,grace:false,validUntil:t};if(i<t+SESSION_OFFLINE_GRACE_MS){return{state:"active",claims:e,grace:true,validUntil:t+SESSION_OFFLINE_GRACE_MS}}return{state:"signed_out"}}load(){if(!this.loaded)this.loaded=this.loadOnce();return this.loaded}async loadOnce(){if(!this.audience)return;this.unwatch??=this.deps.watch?.({storage:t=>{if(t===null||t===this.storageKey)void this.sync()},wake:()=>{if(this.claims)void this.refresh()}})??null;const e=await this.read();if(e&&this.adopt(e)){this.notify();this.schedule()}}async sync(){if(!this.audience)return;const e=await this.read();if(!e){if(this.current)this.clearLocal();return}if(e.refreshToken!==this.current?.refreshToken&&this.adopt(e)){this.notify();this.schedule()}}async read(){try{const e=await(await this.deps.storage()).getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t?.v===1&&typeof t.accessToken==="string"&&typeof t.refreshToken==="string"?t:null}catch{return null}}async write(e){const t=await this.deps.storage();if(e)await t.setItem(this.storageKey,JSON.stringify(e));else await t.removeItem(this.storageKey)}adopt(e){if(!this.audience)return false;const t=g(e.accessToken,{audience:this.audience,ignoreTime:true,publicKeyHex:this.deps.publicKeyHex});const i=t.ok?t.claims:null;if(!i)return false;this.current=e;this.claims=i;return true}armLimit(){if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;if(!this.claims)return;const e=this.claims.exp*1e3;const t=this.deps.now();const i=t<e?e:t<e+SESSION_OFFLINE_GRACE_MS?e+SESSION_OFFLINE_GRACE_MS:null;if(i===null)return;this.limitTimer=this.deps.setTimer(()=>{this.limitTimer=null;this.notify();this.armLimit()},i-t)}call(e,t){return new Promise((i,r)=>{const n=this.deps.setTimer(()=>r(new Error("timeout")),SESSION_CALL_TIMEOUT_MS);this.deps.fetch(e,t).then(s=>{this.deps.clearTimer(n);i(s)},s=>{this.deps.clearTimer(n);r(s)})})}schedule(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(!this.claims)return;const e=this.claims.exp*1e3-SESSION_REFRESH_LEAD_MS-this.deps.now();this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh()},Math.max(0,e))}refresh(e=false){if(!this.refreshing){this.refreshing=this.deps.withLock(this.lockName,()=>this.refreshLocked(e)).catch(()=>this.retryLater()).finally(()=>{this.refreshing=null})}return this.refreshing}get lockName(){return`buoy-session:${this.audience??""}`}async refreshLocked(e){const t=this.epoch;const i=await this.read();if(!i){if(this.current)this.clearLocal();return}if(i.refreshToken!==this.current?.refreshToken){if(this.adopt(i)){this.notify();this.schedule();return}}if(!this.claims)return;if(!e&&this.claims.exp*1e3-this.deps.now()>SESSION_REFRESH_LEAD_MS){this.schedule();return}let r;let n;try{const s=await this.call(`${this.signinOrigin}/api/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:i.refreshToken,audience:this.audience})});r=s.status;n=await s.json().catch(()=>null)}catch{this.retryLater();return}if(t!==this.epoch)return;if(r===200&&n&&typeof n.access_token==="string"&&typeof n.refresh_token==="string"){const s={v:1,accessToken:n.access_token,refreshToken:n.refresh_token};if(this.adopt(s)){await this.write(s);this.notify();this.schedule();return}}if(r===400||r===401||r===403){await this.write(null);this.clearLocal();return}this.retryLater()}retryLater(){this.armLimit();if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=this.deps.setTimer(()=>{this.timer=null;void this.refresh(true)},SESSION_RETRY_MS);this.notify()}clearLocal(){if(this.timer!==null)this.deps.clearTimer(this.timer);this.timer=null;if(this.limitTimer!==null)this.deps.clearTimer(this.limitTimer);this.limitTimer=null;this.current=null;this.claims=null;this.notify()}async signIn(){if(!this.audience)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const e=this.deps.openPopup();if(!e)throw new SessionError("popup_blocked","The browser blocked the sign-in window. Allow pop-ups for this site and try again.");try{return await this.signInWith((t,i)=>new Promise((r,n)=>{e.location.href=t;const s=this.deps.now();let l=null;const h=u=>{if(l!==null)this.deps.clearTimer(l);a();u()};const a=this.deps.onMessage(u=>{if(u.origin!==this.signinOrigin)return;const d=u.data;if(!d||d.type!==y||d.state!==i)return;if(typeof d.code==="string")h(()=>r(d.code));else h(()=>n(new SessionError("access_denied","Sign-in was cancelled.")))});const c=()=>{if(e.closed)return h(()=>n(new SessionError("popup_closed","The sign-in window was closed.")));if(this.deps.now()-s>T)return h(()=>n(new SessionError("timeout","Sign-in took too long.")));l=this.deps.setTimer(c,500)};l=this.deps.setTimer(c,500)}))}finally{try{if(!e.closed)e.close()}catch{}}}async signInWith(e){const t=this.audience;if(!t)throw new SessionError("not_configured","Sign-in needs a web page or a configured app id.");const i=f(this.deps.random(32));const r=f(this.deps.random(16));const n=f(await this.deps.sha256(new TextEncoder().encode(i)));const s=`${this.signinOrigin}/auth/authorize?audience=${encodeURIComponent(t)}&code_challenge=${n}&code_challenge_method=S256&state=${r}`;const l=await e(s,r);let h;try{h=await this.call(`${this.signinOrigin}/api/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:l,code_verifier:i,audience:t})})}catch{throw new SessionError("network","Buoy could not be reached. Check your connection and try again.")}const a=await h.json().catch(()=>null);if(h.status!==200||!a||typeof a.access_token!=="string"||typeof a.refresh_token!=="string"){const c=a&&typeof a.error_description==="string"?a.error_description:"Buoy did not accept this sign-in.";throw new SessionError(h.status===503?"unavailable":"rejected",c)}return this.finishSignIn(a.access_token,a.refresh_token)}async finishSignIn(e,t){const i={v:1,accessToken:e,refreshToken:t};this.epoch+=1;if(!this.adopt(i))throw new SessionError("rejected","Buoy sent a sign-in this app could not check.");await this.write(i);await this.load();this.notify();this.schedule();return this.getStatus()}async signInWithCode(e,t={}){const i=this.audience;if(!i)throw new SessionError("not_configured","Sign-in with a code needs this app's id (app:com.example.app).");const r=(a,c)=>this.call(`${this.signinOrigin}/api/auth/device/${a}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)});let n;try{n=await r("start",{audience:i})}catch{throw new SessionError("network","Buoy could not be reached. Check your connection and try again.")}const s=await n.json().catch(()=>null);if(n.status!==200||!s||typeof s.device_code!=="string"||typeof s.user_code!=="string"||typeof s.verification_uri!=="string"||typeof s.verification_uri_complete!=="string"){const a=s&&typeof s.error_description==="string"?s.error_description:"Buoy could not start a sign-in.";throw new SessionError(n.status===503?"unavailable":"rejected",a)}const l=this.deps.now()+(Number(s.expires_in)||600)*1e3;let h=Math.max(5,Number(s.interval)||5)*1e3;e({userCode:s.user_code,verificationUri:s.verification_uri,verificationUriComplete:s.verification_uri_complete,expiresAt:l});for(;;){await this.wait(h,t.signal);if(this.deps.now()>l)throw new SessionError("timeout","The code ran out. Start again for a new one.");let a;try{a=await r("poll",{device_code:s.device_code})}catch{continue}const c=await a.json().catch(()=>null);if(a.status===200&&c&&typeof c.access_token==="string"&&typeof c.refresh_token==="string"){return this.finishSignIn(c.access_token,c.refresh_token)}const u=c&&typeof c.error==="string"?c.error:"";if(u==="authorization_pending"||a.status>=500)continue;if(u==="slow_down"||a.status===429){h+=5e3;continue}if(u==="access_denied")throw new SessionError("access_denied","The sign-in was turned down on buoy.gg.");if(u==="expired_token")throw new SessionError("timeout","The code ran out. Start again for a new one.");const d=c&&typeof c.error_description==="string"?c.error_description:"Buoy did not accept this sign-in.";throw new SessionError("rejected",d)}}wait(e,t){return new Promise((i,r)=>{const n=()=>new SessionError("access_denied","Sign-in was cancelled.");if(t?.aborted)return r(n());const s=()=>{this.deps.clearTimer(l);r(n())};const l=this.deps.setTimer(()=>{t?.removeEventListener?.("abort",s);i()},e);t?.addEventListener?.("abort",s)})}async signOut(){this.epoch+=1;const e=this.current;this.clearLocal();let t=e;try{await this.deps.withLock(this.lockName,async()=>{t=await this.read()??e;await this.write(null)})}catch{await this.write(null).catch(()=>{})}if(!t)return;try{await this.call(`${this.signinOrigin}/api/auth/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:t.refreshToken})})}catch{}}}export const SessionManager=new SessionManagerImpl;
|
|
@@ -10,7 +10,7 @@ export type { CachedLicense, BuoyTier, KeygenValidationResponse } from "./types"
|
|
|
10
10
|
export { createNodeSession, fileSessionStorage, fileLock, receiveCodeOnLoopback, defaultSessionFile, DESKTOP_AUDIENCE, CLI_AUDIENCE, } from "./nodeSession";
|
|
11
11
|
export type { NodeSession, NodeSessionOptions, NodeSessionStorage } from "./nodeSession";
|
|
12
12
|
export { SessionError, SessionManagerImpl, SESSION_OFFLINE_GRACE_MS } from "./sessionManager";
|
|
13
|
-
export type { SessionStatus, SessionErrorCode } from "./sessionManager";
|
|
13
|
+
export type { SessionStatus, SessionErrorCode, CodePrompt } from "./sessionManager";
|
|
14
14
|
export { verifySessionToken, mintSessionToken, __setSessionTokenPublicKeyForTests } from "./sessionToken";
|
|
15
15
|
export type { SessionClaims } from "./sessionToken";
|
|
16
16
|
export { exchangeServiceKey, isServiceKey, normalizeLicenseKey, sameLicenseKey, SERVICE_KEY_PREFIX, SERVICE_AUDIENCE } from "./serviceKey";
|
|
@@ -34,7 +34,7 @@ export { useAccountAccess } from "./hooks";
|
|
|
34
34
|
export { resolveAccountAccess, type AccountAccess } from "./accountAccess";
|
|
35
35
|
export { LicenseTransportError, isTransientLicenseError } from "./api";
|
|
36
36
|
export { SessionManager, SessionError } from "./sessionManager";
|
|
37
|
-
export type { SessionStatus, SessionErrorCode, SessionConfig } from "./sessionManager";
|
|
37
|
+
export type { SessionStatus, SessionErrorCode, SessionConfig, CodePrompt } from "./sessionManager";
|
|
38
38
|
export type { SessionClaims, SessionTier } from "./sessionToken";
|
|
39
39
|
export { useBuoySession } from "./hooks";
|
|
40
40
|
export { isServiceKey, normalizeLicenseKey, sameLicenseKey } from "./serviceKey";
|
|
@@ -23,7 +23,7 @@ export { useAccountAccess } from "./hooks";
|
|
|
23
23
|
export { resolveAccountAccess, type AccountAccess } from "./accountAccess";
|
|
24
24
|
export { LicenseTransportError, isTransientLicenseError } from "./api";
|
|
25
25
|
export { SessionManager, SessionError } from "./sessionManager";
|
|
26
|
-
export type { SessionStatus, SessionErrorCode, SessionConfig } from "./sessionManager";
|
|
26
|
+
export type { SessionStatus, SessionErrorCode, SessionConfig, CodePrompt } from "./sessionManager";
|
|
27
27
|
export type { SessionClaims, SessionTier } from "./sessionToken";
|
|
28
28
|
export { useBuoySession } from "./hooks";
|
|
29
29
|
export { isServiceKey, normalizeLicenseKey, sameLicenseKey } from "./serviceKey";
|
|
@@ -26,6 +26,17 @@ export type SessionStatus = {
|
|
|
26
26
|
validUntil: number;
|
|
27
27
|
};
|
|
28
28
|
export type SessionErrorCode = "not_configured" | "popup_blocked" | "popup_closed" | "access_denied" | "timeout" | "rejected" | "unavailable" | "network";
|
|
29
|
+
/** What an app shows while it waits for a sign-in with a code. */
|
|
30
|
+
export interface CodePrompt {
|
|
31
|
+
/** "BCDF-GHJK": what the person types at verificationUri. */
|
|
32
|
+
userCode: string;
|
|
33
|
+
/** buoy.gg/activate */
|
|
34
|
+
verificationUri: string;
|
|
35
|
+
/** buoy.gg/activate?code=…, for a QR code. */
|
|
36
|
+
verificationUriComplete: string;
|
|
37
|
+
/** When the code runs out (ms since epoch). */
|
|
38
|
+
expiresAt: number;
|
|
39
|
+
}
|
|
29
40
|
export declare class SessionError extends Error {
|
|
30
41
|
code: SessionErrorCode;
|
|
31
42
|
constructor(code: SessionErrorCode, message: string);
|
|
@@ -162,6 +173,20 @@ export declare class SessionManagerImpl {
|
|
|
162
173
|
* `getCode` gets the buoy.gg URL to open and the state it must echo.
|
|
163
174
|
*/
|
|
164
175
|
signInWith(getCode: (url: string, state: string) => Promise<string>): Promise<SessionStatus>;
|
|
176
|
+
/** Saves a new token pair from buoy.gg and starts keeping it fresh. */
|
|
177
|
+
private finishSignIn;
|
|
178
|
+
/**
|
|
179
|
+
* Sign in with a code, for apps that can't open a popup: phones, TVs,
|
|
180
|
+
* native ports. `show` gets a short code and buoy.gg/activate (also as one
|
|
181
|
+
* link with the code, for a QR code). The person allows it there, signed
|
|
182
|
+
* in; this resolves once they do. buoy.gg does the PKCE work, so this needs
|
|
183
|
+
* no crypto on the device. Abort `signal` to stop waiting.
|
|
184
|
+
*/
|
|
185
|
+
signInWithCode(show: (prompt: CodePrompt) => void, options?: {
|
|
186
|
+
signal?: AbortSignal;
|
|
187
|
+
}): Promise<SessionStatus>;
|
|
188
|
+
/** Waits, or rejects as soon as `signal` aborts. */
|
|
189
|
+
private wait;
|
|
165
190
|
/** Sign out here and end the sign-in on buoy.gg. */
|
|
166
191
|
signOut(): Promise<void>;
|
|
167
192
|
}
|
|
@@ -10,7 +10,7 @@ export type { CachedLicense, BuoyTier, KeygenValidationResponse } from "./types"
|
|
|
10
10
|
export { createNodeSession, fileSessionStorage, fileLock, receiveCodeOnLoopback, defaultSessionFile, DESKTOP_AUDIENCE, CLI_AUDIENCE, } from "./nodeSession";
|
|
11
11
|
export type { NodeSession, NodeSessionOptions, NodeSessionStorage } from "./nodeSession";
|
|
12
12
|
export { SessionError, SessionManagerImpl, SESSION_OFFLINE_GRACE_MS } from "./sessionManager";
|
|
13
|
-
export type { SessionStatus, SessionErrorCode } from "./sessionManager";
|
|
13
|
+
export type { SessionStatus, SessionErrorCode, CodePrompt } from "./sessionManager";
|
|
14
14
|
export { verifySessionToken, mintSessionToken, __setSessionTokenPublicKeyForTests } from "./sessionToken";
|
|
15
15
|
export type { SessionClaims } from "./sessionToken";
|
|
16
16
|
export { exchangeServiceKey, isServiceKey, normalizeLicenseKey, sameLicenseKey, SERVICE_KEY_PREFIX, SERVICE_AUDIENCE } from "./serviceKey";
|
|
@@ -34,7 +34,7 @@ export { useAccountAccess } from "./hooks";
|
|
|
34
34
|
export { resolveAccountAccess, type AccountAccess } from "./accountAccess";
|
|
35
35
|
export { LicenseTransportError, isTransientLicenseError } from "./api";
|
|
36
36
|
export { SessionManager, SessionError } from "./sessionManager";
|
|
37
|
-
export type { SessionStatus, SessionErrorCode, SessionConfig } from "./sessionManager";
|
|
37
|
+
export type { SessionStatus, SessionErrorCode, SessionConfig, CodePrompt } from "./sessionManager";
|
|
38
38
|
export type { SessionClaims, SessionTier } from "./sessionToken";
|
|
39
39
|
export { useBuoySession } from "./hooks";
|
|
40
40
|
export { isServiceKey, normalizeLicenseKey, sameLicenseKey } from "./serviceKey";
|
|
@@ -23,7 +23,7 @@ export { useAccountAccess } from "./hooks";
|
|
|
23
23
|
export { resolveAccountAccess, type AccountAccess } from "./accountAccess";
|
|
24
24
|
export { LicenseTransportError, isTransientLicenseError } from "./api";
|
|
25
25
|
export { SessionManager, SessionError } from "./sessionManager";
|
|
26
|
-
export type { SessionStatus, SessionErrorCode, SessionConfig } from "./sessionManager";
|
|
26
|
+
export type { SessionStatus, SessionErrorCode, SessionConfig, CodePrompt } from "./sessionManager";
|
|
27
27
|
export type { SessionClaims, SessionTier } from "./sessionToken";
|
|
28
28
|
export { useBuoySession } from "./hooks";
|
|
29
29
|
export { isServiceKey, normalizeLicenseKey, sameLicenseKey } from "./serviceKey";
|
|
@@ -26,6 +26,17 @@ export type SessionStatus = {
|
|
|
26
26
|
validUntil: number;
|
|
27
27
|
};
|
|
28
28
|
export type SessionErrorCode = "not_configured" | "popup_blocked" | "popup_closed" | "access_denied" | "timeout" | "rejected" | "unavailable" | "network";
|
|
29
|
+
/** What an app shows while it waits for a sign-in with a code. */
|
|
30
|
+
export interface CodePrompt {
|
|
31
|
+
/** "BCDF-GHJK": what the person types at verificationUri. */
|
|
32
|
+
userCode: string;
|
|
33
|
+
/** buoy.gg/activate */
|
|
34
|
+
verificationUri: string;
|
|
35
|
+
/** buoy.gg/activate?code=…, for a QR code. */
|
|
36
|
+
verificationUriComplete: string;
|
|
37
|
+
/** When the code runs out (ms since epoch). */
|
|
38
|
+
expiresAt: number;
|
|
39
|
+
}
|
|
29
40
|
export declare class SessionError extends Error {
|
|
30
41
|
code: SessionErrorCode;
|
|
31
42
|
constructor(code: SessionErrorCode, message: string);
|
|
@@ -162,6 +173,20 @@ export declare class SessionManagerImpl {
|
|
|
162
173
|
* `getCode` gets the buoy.gg URL to open and the state it must echo.
|
|
163
174
|
*/
|
|
164
175
|
signInWith(getCode: (url: string, state: string) => Promise<string>): Promise<SessionStatus>;
|
|
176
|
+
/** Saves a new token pair from buoy.gg and starts keeping it fresh. */
|
|
177
|
+
private finishSignIn;
|
|
178
|
+
/**
|
|
179
|
+
* Sign in with a code, for apps that can't open a popup: phones, TVs,
|
|
180
|
+
* native ports. `show` gets a short code and buoy.gg/activate (also as one
|
|
181
|
+
* link with the code, for a QR code). The person allows it there, signed
|
|
182
|
+
* in; this resolves once they do. buoy.gg does the PKCE work, so this needs
|
|
183
|
+
* no crypto on the device. Abort `signal` to stop waiting.
|
|
184
|
+
*/
|
|
185
|
+
signInWithCode(show: (prompt: CodePrompt) => void, options?: {
|
|
186
|
+
signal?: AbortSignal;
|
|
187
|
+
}): Promise<SessionStatus>;
|
|
188
|
+
/** Waits, or rejects as soon as `signal` aborts. */
|
|
189
|
+
private wait;
|
|
165
190
|
/** Sign out here and end the sign-in on buoy.gg. */
|
|
166
191
|
signOut(): Promise<void>;
|
|
167
192
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{m as e}from"./chunk-FPGC7BWV.mjs";export{e as LicenseManager};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";var p=process.env.NODE_ENV!=="production";typeof window<"u"&&typeof globalThis.global>"u"&&(globalThis.global=globalThis);var L=new Map,Ee={async getItem(e){try{return localStorage.getItem(e)??L.get(e)??null}catch{return L.get(e)??null}},async setItem(e,t){L.set(e,t);try{localStorage.setItem(e,t)}catch{}},async removeItem(e){L.delete(e);try{localStorage.removeItem(e)}catch{}}};async function y(){return Ee}var I={accountId:"b0eba0e8-e904-4906-b644-38f9aab57bed",productId:"bf67cb43-eeef-40ca-b308-e6e1b22c0eaa",apiUrl:"https://buoy.gg/api/license"},ke="3b546fc717a62683f3236c70167899a271bee3ffc04df6ec1eea60a394a2f1e9",be="36abba0267f7f379d992a5b6df641daab4190bcc9e639e36f1378e3c1ac52746",R={free:"394e3351-1249-4f73-807a-703408604c0e",solo:"fa5d3c8f-5379-40f1-b63e-ab8a974b2f28",business:"690c2212-8e54-426f-b4b4-002ee1c0ae2b"},N=720*60*60*1e3,xe=1440*60*1e3,M="buoy-gg-license",V="buoy-gg-device-fingerprint",Le=3600*1e3,Ie=[5*1e3,20*1e3,60*1e3,300*1e3,900*1e3],Re=60*1e3,q=3600*1e3,Ne=1440*60*1e3,Ce=50,Pe=N,D="buoy-gg-license-hold";function h(){let e=globalThis.__buoyRealNow;return typeof e=="function"?e():Date.now()}var{apiUrl:Z}=I,T=class extends Error{constructor(e,t,i=null){super(e);this.isTransient=true;this.name="LicenseTransportError",this.status=t,this.retryAfterMs=i}};function Se(e){let t=e?.status;return t===429||t===402}function Ke(e,t,i){return e.status===402?t:e.retryAfterMs??i}function Oe(e){let t=s=>{try{return e.headers?.get?.(s)??null}catch{return null}},i=t("Retry-After");if(i){let s=Number(i);if(Number.isFinite(s)&&s>=0)return s*1e3;let n=Date.parse(i);if(Number.isFinite(n))return Math.max(0,n-h())}let r=Number(t("X-RateLimit-Reset"));return Number.isFinite(r)&&r>0?Math.max(0,r*1e3-h()):null}function Q(e){return e instanceof T||e instanceof TypeError?true:typeof e=="object"&&e!==null&&e.isTransient===true}async function Me(e){try{return(await e.json()).errors?.[0]?.detail||`HTTP ${e.status}`}catch{return`HTTP ${e.status}`}}function ee(){return{"Content-Type":"application/vnd.api+json",Accept:"application/vnd.api+json"}}async function Ve(e,t){let i=`${Z}/validate`,r={meta:{key:e}},s;try{s=await fetch(i,{signal:t,method:"POST",headers:ee(),body:JSON.stringify(r)})}catch(n){throw new T(n instanceof Error?n.message:"Network request failed",null)}if(!s.ok)throw new T(await Me(s),s.status,s.status===429?Oe(s):null);try{let n=await s.json();if(!n||typeof n!="object"||typeof n.meta?.valid!="boolean"||typeof n.meta?.code!="string"||!n.meta.code)throw new Error("Missing license verdict");return n}catch{throw new T("License server returned an unreadable response",s.status)}}async function De(e){let t=typeof AbortController=="function"?new AbortController:null,i;try{return await Promise.race([Ve(e,t?.signal),new Promise((r,s)=>{i=setTimeout(()=>{s(new T("License verification timed out. Please retry.",null)),t?.abort()},15e3)})])}finally{i&&clearTimeout(i)}}function Ue(e){if(!e.data)return false;let t=e.data.relationships?.product?.data?.id;return t&&t!==I.productId?false:e.meta.valid&&e.meta.code==="VALID"?true:["OVERDUE","NO_MACHINE","NO_MACHINES","TOO_MANY_MACHINES","TOO_MANY_CORES","TOO_MANY_PROCESSES","FINGERPRINT_SCOPE_MISMATCH","HEARTBEAT_NOT_STARTED","HEARTBEAT_DEAD"].includes(e.meta.code)}function Fe(e){let t=e.data?.relationships?.machines?.meta?.count;return typeof t=="number"&&Number.isFinite(t)?t:0}async function te(e,t,i,r,s){let n=`${Z}/machines`,c=await fetch(n,{method:"POST",headers:ee(),body:JSON.stringify({licenseId:e,licenseKey:t,fingerprint:i,name:r,metadata:s||{}})});if(!c.ok){let o=await c.json(),a=o.errors?.[0]?.code;if(a==="FINGERPRINT_TAKEN")throw new Error("FINGERPRINT_TAKEN");if(a==="MACHINE_LIMIT_EXCEEDED")throw new Error("MACHINE_LIMIT_EXCEEDED");let l=o.errors?.[0]?.detail||`HTTP ${c.status}`;throw new Error(l)}return c.json()}function $e(e){let{meta:t,data:i}=e;return{valid:t.valid,code:t.code,detail:t.detail,metadata:i?.attributes?.metadata}}function je(e){return["NOT_FOUND","SUSPENDED","EXPIRED","BANNED","PRODUCT_SCOPE_MISMATCH"].includes(e)}function U(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function F(){if(typeof p<"u")return p;try{if(typeof navigator<"u"&&/electron/i.test(navigator.userAgent))return true}catch{}return typeof process<"u"&&process.env.NODE_ENV!=="production"}function ie(){if(typeof navigator>"u")return"unknown";let e=navigator.userAgent.toLowerCase(),t=navigator.platform?.toLowerCase()||"";return e.includes("electron")?t.includes("mac")||e.includes("mac")?"electron-macos":t.includes("win")||e.includes("win")?"electron-windows":t.includes("linux")||e.includes("linux")?"electron-linux":"electron":t.includes("mac")||e.includes("mac")?"macos":t.includes("win")||e.includes("win")?"windows":t.includes("linux")||e.includes("linux")?"linux":/iphone|ipad|ipod/.test(e)?"ios-web":e.includes("android")?"android-web":"web"}function He(){if(typeof navigator>"u")return"unknown";let e=navigator.userAgent,t=e.match(/Mac OS X ([\d_.]+)/);if(t)return t[1].replace(/_/g,".");let i=e.match(/Windows NT ([\d.]+)/);return i?{"10.0":"10/11","6.3":"8.1","6.2":"8","6.1":"7"}[i[1]]||i[1]:e.includes("Linux")?"Linux":"unknown"}function re(){if(typeof navigator>"u")return{name:"unknown",version:"unknown"};let e=navigator.userAgent,t=e.match(/Electron\/([\d.]+)/);if(t)return{name:"Electron",version:t[1]};let i=e.match(/Chrome\/([\d.]+)/);if(i&&!e.includes("Edg"))return{name:"Chrome",version:i[1]};let r=e.match(/Edg\/([\d.]+)/);if(r)return{name:"Edge",version:r[1]};let s=e.match(/Firefox\/([\d.]+)/);if(s)return{name:"Firefox",version:s[1]};let n=e.match(/Version\/([\d.]+).*Safari/);return n?{name:"Safari",version:n[1]}:{name:"unknown",version:"unknown"}}function se(){let e={platform:ie(),osVersion:He(),isSimulator:"false"},t=re();e.browser=t.name,e.browserVersion=t.version,typeof window<"u"&&window.screen&&(e.screenResolution=`${window.screen.width}x${window.screen.height}`),typeof navigator<"u"&&navigator.language&&(e.language=navigator.language);try{e.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone}catch{}return e}function $(){let e=ie(),t=re();if(e.startsWith("electron")){let i=e.replace("electron-","").replace("electron","Desktop");return`${i.charAt(0).toUpperCase()+i.slice(1)} App`}return t.name!=="unknown"?`${t.name} Browser`:"Web Browser"}var Be=["v1"],ze="buoy.gg/pro:9f3c1d7a4b2e:license-cache";function ne(e,t){let i=t>>>0;for(let r=0;r<e.length;r++)i^=e.charCodeAt(r),i=Math.imul(i,16777619)>>>0;return i>>>0}function Ge(e,t){let i=[t,e.licenseKey,e.licenseId,e.isPro?"1":"0",e.expiresAt??"",String(e.cachedAt)];return t!=="v1"&&i.push(e.tier??""),i.push(ze),i.join("\0")}function ae(e,t){let i=Ge(e,t),r=ne(i,2166136261),s=ne(i,2135587861);return`${t}.${r.toString(16)}${s.toString(16)}`}function oe(e){return ae(e,"v2")}function le(e){return{...e,sig:oe(e)}}function A(e){return!e||typeof e.sig!="string"||e.sig.length===0?false:e.sig===oe(e)?true:Be.some(t=>e.sig.startsWith(`${t}.`)&&e.sig===ae(e,t))}import Je from"tweetnacl";var Ye=1,Xe=1,We="buoy-free-token-v1:",qe=22,Ze=64,ce=/^BUOY1-([A-Z2-7]+)-([A-Z2-7]+)$/;function he(e){return ce.test(e.trim().toUpperCase())}var Qe="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";function ue(e){let t=[],i=0,r=0;for(let s of e){let n=Qe.indexOf(s);if(n===-1)return null;r=r<<5|n,i+=5,i>=8&&(t.push(r>>>i-8&255),i-=8)}return new Uint8Array(t)}function et(e){let t=e.trim().toLowerCase(),i=new Uint8Array(t.length/2);for(let r=0;r<i.length;r+=1)i[r]=parseInt(t.slice(r*2,r*2+2),16);return i}function tt(e){let t="";for(let i of e)t+=i.toString(16).padStart(2,"0");return t}function it(e){if(typeof TextEncoder<"u")return new TextEncoder().encode(e);let t=new Uint8Array(e.length);for(let i=0;i<e.length;i+=1)t[i]=e.charCodeAt(i)&255;return t}function rt(e,t){let i=new Uint8Array(e.length+t.length);return i.set(e,0),i.set(t,e.length),i}function st(e){return rt(it(We),e)}var nt=ke;function at(e,t=nt){let i=ce.exec(e.trim().toUpperCase());if(!i)return null;let r=ue(i[1]),s=ue(i[2]);if(!r||!s||r.length!==qe||s.length!==Ze||r[0]!==Ye||r[1]!==Xe)return null;try{if(!Je.sign.detached.verify(st(r),s,et(t)))return null}catch{return null}return{tier:"free",issuedAt:((r[2]<<24>>>0)+(r[3]<<16)+(r[4]<<8)+r[5])*1e3,accountHash:tt(r.slice(6,22))}}function ot(e){let t=e.relationships?.policy?.data?.id;return t===R.free?"free":t===R.solo||t===R.business?"pro":e.attributes?.metadata?.plan==="free"?"free":"pro"}function lt(e){if(!e)return false;let t=e.relationships?.product?.data?.id;if(t)return t===I.productId;let i=e.relationships?.policy?.data?.id;return Object.values(R).some(r=>r===i)}function de(e,t=h()){let i=e.verifiedAt!==null&&Number.isFinite(e.verifiedAt)&&e.verifiedAt<=t?e.verifiedAt+N:null;return e.initialized&&e.key&&e.tierSource!=="none"&&i!==null&&t<i?{status:"admitted",reason:null,validUntil:i}:i!==null&&t>=i?{status:"locked",reason:"verification_expired",validUntil:i}:!e.initialized||e.validating?{status:"initializing",reason:"checking",validUntil:i}:{status:"locked",reason:i!==null&&t>=i?"verification_expired":"account_required",validUntil:i}}function ct(e){if(typeof window>"u"||typeof window.addEventListener!="function")return()=>{};let t=()=>{document.hidden||e()};return window.addEventListener("focus",e),document.addEventListener("visibilitychange",t),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",t)}}import ht from"tweetnacl";var fe="buoys1",ut="buoy-session-v1:",pe=1,j=60,dt=64,ft=["free","pro","business"],m="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function H(e){let t="",i=0;for(;i+2<e.length;i+=3){let s=e[i]<<16|e[i+1]<<8|e[i+2];t+=m[s>>>18&63]+m[s>>>12&63]+m[s>>>6&63]+m[s&63]}let r=e.length-i;if(r===1){let s=e[i]<<16;t+=m[s>>>18&63]+m[s>>>12&63]}else if(r===2){let s=e[i]<<16|e[i+1]<<8;t+=m[s>>>18&63]+m[s>>>12&63]+m[s>>>6&63]}return t}function ge(e){if(!/^[A-Za-z0-9_-]*$/.test(e)||e.length%4===1)return null;let t=[],i=0,r=0;for(let s of e)r=r<<6|m.indexOf(s),i+=6,i>=8&&(t.push(r>>>i-8&255),i-=8);return new Uint8Array(t)}function pt(e){if(typeof TextEncoder<"u")return new TextEncoder().encode(e);let t=unescape(encodeURIComponent(e)),i=new Uint8Array(t.length);for(let r=0;r<t.length;r+=1)i[r]=t.charCodeAt(r);return i}function gt(e){try{if(typeof TextDecoder<"u")return new TextDecoder("utf-8",{fatal:true}).decode(e);let t="";for(let i of e)t+=String.fromCharCode(i);return decodeURIComponent(escape(t))}catch{return null}}function mt(e){let t=e.trim().toLowerCase(),i=new Uint8Array(t.length/2);for(let r=0;r<i.length;r+=1)i[r]=parseInt(t.slice(r*2,r*2+2),16);return i}function yt(e){return pt(ut+e)}function _(e,t=256){return typeof e=="string"&&e.length>0&&e.length<=t}function me(e){return typeof e=="number"&&Number.isInteger(e)&&e>0}function wt(e){if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return!_(t.sid)||!_(t.sub)||!_(t.aud,512)||t.team!==null&&!_(t.team)||!ft.includes(t.tier)||t.role!==void 0&&!_(t.role,64)||t.tools!==void 0&&(!Array.isArray(t.tools)||t.tools.length>256||!t.tools.every(i=>_(i,64)))||!me(t.iat)||!me(t.exp)||t.exp<=t.iat?null:{v:pe,sid:t.sid,sub:t.sub,team:t.team,tier:t.tier,...t.role!==void 0?{role:t.role}:{},...t.tools!==void 0?{tools:[...t.tools]}:{},aud:t.aud,iat:t.iat,exp:t.exp}}var vt=be;function ye(e){return e.startsWith(`${fe}.`)}function B(e,t){let i=t.publicKeyHex??vt;if(!i)return{ok:false,reason:"no-key"};let r=typeof e=="string"?e.split("."):[];if(r.length!==3||r[0]!==fe)return{ok:false,reason:"malformed"};let[,s,n]=r,c=ge(s),o=ge(n);if(!c||!o||o.length!==dt)return{ok:false,reason:"malformed"};try{if(!ht.sign.detached.verify(yt(s),o,mt(i)))return{ok:false,reason:"signature"}}catch{return{ok:false,reason:"signature"}}let a=gt(c),l;try{l=a===null?null:JSON.parse(a)}catch{return{ok:false,reason:"malformed"}}if(!l||typeof l!="object"||l.v!==pe)return{ok:false,reason:l&&typeof l=="object"?"version":"malformed"};let u=wt(l);if(!u)return{ok:false,reason:"claims"};if(u.aud!==t.audience)return{ok:false,reason:"audience"};if(t.ignoreTime)return{ok:true,claims:u};let f=Math.floor((t.nowMs??h())/1e3);return f>=u.exp+j?{ok:false,reason:"expired"}:u.iat>f+j?{ok:false,reason:"not-yet-valid"}:{ok:true,claims:u}}var Tt="@buoy/session:",C=10080*60*1e3,we=360*60*1e3,At=300*1e3,_t=15e3,Et="https://buoy.gg",kt="buoy:signin",bt=600*1e3,d=class extends Error{constructor(e,t){super(t);this.code=e;this.name="SessionError"}};function xt(){let e=globalThis;return{fetch:(t,i)=>fetch(t,i),storage:y,now:h,random:t=>{let i=new Uint8Array(t);if(!e.crypto?.getRandomValues)throw new d("not_configured","This platform has no secure random source.");return e.crypto.getRandomValues(i)},sha256:async t=>{if(!e.crypto?.subtle)throw new d("not_configured","Sign-in needs a secure page (https or localhost).");return new Uint8Array(await e.crypto.subtle.digest("SHA-256",t))},openPopup:()=>{if(!e.window?.open)return null;let t=480,i=640,r=Math.max(0,(e.window.screenX??0)+((e.window.outerWidth??t)-t)/2),s=Math.max(0,(e.window.screenY??0)+((e.window.outerHeight??i)-i)/2);return e.window.open("about:blank","buoy-signin",`popup,width=${t},height=${i},left=${r},top=${s}`)},onMessage:t=>{if(!e.window?.addEventListener)return()=>{};let i=r=>t({origin:r.origin,data:r.data});return e.window.addEventListener("message",i),()=>e.window?.removeEventListener("message",i)},withLock:(t,i)=>e.navigator?.locks?e.navigator.locks.request(t,i):i(),watch:t=>{let i=e.window;if(!i?.addEventListener)return()=>{};let r=n=>t.storage(n.key),s=()=>{i.document?.visibilityState!=="hidden"&&t.wake()};return i.addEventListener("storage",r),i.addEventListener("online",t.wake),i.document?.addEventListener?.("visibilitychange",s),()=>{i.removeEventListener("storage",r),i.removeEventListener("online",t.wake),i.document?.removeEventListener?.("visibilitychange",s)}},setTimer:(t,i)=>setTimeout(t,Math.min(i,2147483647)),clearTimer:t=>clearTimeout(t)}}function Lt(){let e=globalThis.location;if(!(!e?.origin||!/^https?:$/.test(e.protocol??"")))return e.origin}var It=class{constructor(e){this.signinOrigin=Et;this.current=null;this.claims=null;this.listeners=new Set;this.timer=null;this.refreshing=null;this.loaded=null;this.unwatch=null;this.limitTimer=null;this.epoch=0;this.deps=e??xt(),this.audience=Lt()}get origin(){return this.signinOrigin}configure(e){e.audience!==void 0&&(this.audience=e.audience),e.signinOrigin!==void 0&&(this.signinOrigin=e.signinOrigin.replace(/\/$/,"")),this.reset()}__setDeps(e){this.deps=e,this.reset()}reset(){this.unwatch?.(),this.unwatch=null,this.epoch+=1,this.limitTimer!==null&&this.deps.clearTimer(this.limitTimer),this.limitTimer=null,this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=null,this.current=null,this.claims=null,this.loaded=null,this.refreshing=null}get storageKey(){return Tt+(this.audience??"")}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){for(let e of[...this.listeners])try{e()}catch(t){console.warn("[Buoy] sign-in listener error:",t)}}getAccessToken(){let e=this.getStatus();return e.state==="active"&&!e.grace?this.current?.accessToken??null:null}getStatus(){let e=this.claims;if(!e)return{state:"signed_out"};let t=e.exp*1e3,i=this.deps.now();return i<(e.iat-j)*1e3?{state:"signed_out"}:i<t?{state:"active",claims:e,grace:false,validUntil:t}:i<t+C?{state:"active",claims:e,grace:true,validUntil:t+C}:{state:"signed_out"}}load(){return this.loaded||(this.loaded=this.loadOnce()),this.loaded}async loadOnce(){if(!this.audience)return;this.unwatch??(this.unwatch=this.deps.watch?.({storage:t=>{(t===null||t===this.storageKey)&&this.sync()},wake:()=>{this.claims&&this.refresh()}})??null);let e=await this.read();e&&this.adopt(e)&&(this.notify(),this.schedule())}async sync(){if(!this.audience)return;let e=await this.read();if(!e){this.current&&this.clearLocal();return}e.refreshToken!==this.current?.refreshToken&&this.adopt(e)&&(this.notify(),this.schedule())}async read(){try{let e=await(await this.deps.storage()).getItem(this.storageKey);if(!e)return null;let t=JSON.parse(e);return t?.v===1&&typeof t.accessToken=="string"&&typeof t.refreshToken=="string"?t:null}catch{return null}}async write(e){let t=await this.deps.storage();e?await t.setItem(this.storageKey,JSON.stringify(e)):await t.removeItem(this.storageKey)}adopt(e){if(!this.audience)return false;let t=B(e.accessToken,{audience:this.audience,ignoreTime:true,publicKeyHex:this.deps.publicKeyHex}),i=t.ok?t.claims:null;return i?(this.current=e,this.claims=i,true):false}armLimit(){if(this.limitTimer!==null&&this.deps.clearTimer(this.limitTimer),this.limitTimer=null,!this.claims)return;let e=this.claims.exp*1e3,t=this.deps.now(),i=t<e?e:t<e+C?e+C:null;i!==null&&(this.limitTimer=this.deps.setTimer(()=>{this.limitTimer=null,this.notify(),this.armLimit()},i-t))}call(e,t){return new Promise((i,r)=>{let s=this.deps.setTimer(()=>r(new Error("timeout")),_t);this.deps.fetch(e,t).then(n=>{this.deps.clearTimer(s),i(n)},n=>{this.deps.clearTimer(s),r(n)})})}schedule(){if(this.armLimit(),this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=null,!this.claims)return;let e=this.claims.exp*1e3-we-this.deps.now();this.timer=this.deps.setTimer(()=>{this.timer=null,this.refresh()},Math.max(0,e))}refresh(e=false){return this.refreshing||(this.refreshing=this.deps.withLock(this.lockName,()=>this.refreshLocked(e)).catch(()=>this.retryLater()).finally(()=>{this.refreshing=null})),this.refreshing}get lockName(){return`buoy-session:${this.audience??""}`}async refreshLocked(e){let t=this.epoch,i=await this.read();if(!i){this.current&&this.clearLocal();return}if(i.refreshToken!==this.current?.refreshToken&&this.adopt(i)){this.notify(),this.schedule();return}if(!this.claims)return;if(!e&&this.claims.exp*1e3-this.deps.now()>we){this.schedule();return}let r,s;try{let n=await this.call(`${this.signinOrigin}/api/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:i.refreshToken,audience:this.audience})});r=n.status,s=await n.json().catch(()=>null)}catch{this.retryLater();return}if(t===this.epoch){if(r===200&&s&&typeof s.access_token=="string"&&typeof s.refresh_token=="string"){let n={v:1,accessToken:s.access_token,refreshToken:s.refresh_token};if(this.adopt(n)){await this.write(n),this.notify(),this.schedule();return}}if(r===400||r===401||r===403){await this.write(null),this.clearLocal();return}this.retryLater()}}retryLater(){this.armLimit(),this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=this.deps.setTimer(()=>{this.timer=null,this.refresh(true)},At),this.notify()}clearLocal(){this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=null,this.limitTimer!==null&&this.deps.clearTimer(this.limitTimer),this.limitTimer=null,this.current=null,this.claims=null,this.notify()}async signIn(){if(!this.audience)throw new d("not_configured","Sign-in needs a web page or a configured app id.");let e=this.deps.openPopup();if(!e)throw new d("popup_blocked","The browser blocked the sign-in window. Allow pop-ups for this site and try again.");try{return await this.signInWith((t,i)=>new Promise((r,s)=>{e.location.href=t;let n=this.deps.now(),c=null,o=u=>{c!==null&&this.deps.clearTimer(c),a(),u()},a=this.deps.onMessage(u=>{if(u.origin!==this.signinOrigin)return;let f=u.data;!f||f.type!==kt||f.state!==i||(typeof f.code=="string"?o(()=>r(f.code)):o(()=>s(new d("access_denied","Sign-in was cancelled."))))}),l=()=>{if(e.closed)return o(()=>s(new d("popup_closed","The sign-in window was closed.")));if(this.deps.now()-n>bt)return o(()=>s(new d("timeout","Sign-in took too long.")));c=this.deps.setTimer(l,500)};c=this.deps.setTimer(l,500)}))}finally{try{e.closed||e.close()}catch{}}}async signInWith(e){let t=this.audience;if(!t)throw new d("not_configured","Sign-in needs a web page or a configured app id.");let i=H(this.deps.random(32)),r=H(this.deps.random(16)),s=H(await this.deps.sha256(new TextEncoder().encode(i))),n=`${this.signinOrigin}/auth/authorize?audience=${encodeURIComponent(t)}&code_challenge=${s}&code_challenge_method=S256&state=${r}`,c=await e(n,r),o;try{o=await this.call(`${this.signinOrigin}/api/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:c,code_verifier:i,audience:t})})}catch{throw new d("network","Buoy could not be reached. Check your connection and try again.")}let a=await o.json().catch(()=>null);if(o.status!==200||!a||typeof a.access_token!="string"||typeof a.refresh_token!="string"){let l=a&&typeof a.error_description=="string"?a.error_description:"Buoy did not accept this sign-in.";throw new d(o.status===503?"unavailable":"rejected",l)}return this.finishSignIn(a.access_token,a.refresh_token)}async finishSignIn(e,t){let i={v:1,accessToken:e,refreshToken:t};if(this.epoch+=1,!this.adopt(i))throw new d("rejected","Buoy sent a sign-in this app could not check.");return await this.write(i),await this.load(),this.notify(),this.schedule(),this.getStatus()}async signInWithCode(e,t={}){let i=this.audience;if(!i)throw new d("not_configured","Sign-in with a code needs this app's id (app:com.example.app).");let r=(a,l)=>this.call(`${this.signinOrigin}/api/auth/device/${a}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)}),s;try{s=await r("start",{audience:i})}catch{throw new d("network","Buoy could not be reached. Check your connection and try again.")}let n=await s.json().catch(()=>null);if(s.status!==200||!n||typeof n.device_code!="string"||typeof n.user_code!="string"||typeof n.verification_uri!="string"||typeof n.verification_uri_complete!="string"){let a=n&&typeof n.error_description=="string"?n.error_description:"Buoy could not start a sign-in.";throw new d(s.status===503?"unavailable":"rejected",a)}let c=this.deps.now()+(Number(n.expires_in)||600)*1e3,o=Math.max(5,Number(n.interval)||5)*1e3;for(e({userCode:n.user_code,verificationUri:n.verification_uri,verificationUriComplete:n.verification_uri_complete,expiresAt:c});;){if(await this.wait(o,t.signal),this.deps.now()>c)throw new d("timeout","The code ran out. Start again for a new one.");let a;try{a=await r("poll",{device_code:n.device_code})}catch{continue}let l=await a.json().catch(()=>null);if(a.status===200&&l&&typeof l.access_token=="string"&&typeof l.refresh_token=="string")return this.finishSignIn(l.access_token,l.refresh_token);let u=l&&typeof l.error=="string"?l.error:"";if(u==="authorization_pending"||a.status>=500)continue;if(u==="slow_down"||a.status===429){o+=5e3;continue}if(u==="access_denied")throw new d("access_denied","The sign-in was turned down on buoy.gg.");if(u==="expired_token")throw new d("timeout","The code ran out. Start again for a new one.");let f=l&&typeof l.error_description=="string"?l.error_description:"Buoy did not accept this sign-in.";throw new d("rejected",f)}}wait(e,t){return new Promise((i,r)=>{let s=()=>new d("access_denied","Sign-in was cancelled.");if(t?.aborted)return r(s());let n=()=>{this.deps.clearTimer(c),r(s())},c=this.deps.setTimer(()=>{t?.removeEventListener?.("abort",n),i()},e);t?.addEventListener?.("abort",n)})}async signOut(){this.epoch+=1;let e=this.current;this.clearLocal();let t=e;try{await this.deps.withLock(this.lockName,async()=>{t=await this.read()??e,await this.write(null)})}catch{await this.write(null).catch(()=>{})}if(t)try{await this.call(`${this.signinOrigin}/api/auth/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:t.refreshToken})})}catch{}}},E=new It;var Rt="bsk1_",Nt="service",Ct=15e3;function z(e){return e.startsWith(Rt)}function b(e){let t=e.trim();return z(t)||ye(t)?t:t.toUpperCase()}function Pt(e,t){return b(e??"")===b(t??"")}async function St(e,t){let i=t.fetch??((a,l)=>fetch(a,l)),r=typeof AbortController<"u"?new AbortController:void 0,s=setTimeout(()=>r?.abort(),Ct),n,c;try{let a=await i(`${t.origin.replace(/\/$/,"")}/api/auth/service-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:e}),signal:r?.signal});n=a.status,c=await a.json().catch(()=>null)}catch{return{ok:false,reason:"network",detail:"Buoy could not be reached to check the bot key."}}finally{clearTimeout(s)}if(n>=500||n===429)return{ok:false,reason:"network",detail:"Buoy is busy. The bot key will be checked again soon."};if(n!==200||typeof c?.access_token!="string")return{ok:false,reason:"rejected",detail:typeof c?.error_description=="string"?c.error_description:"This bot key was not accepted."};let o=B(c.access_token,{audience:Nt,nowMs:h(),publicKeyHex:t.publicKeyHex});return o.ok?{ok:true,claims:o.claims}:{ok:false,reason:"rejected",detail:"Buoy sent a token this app could not check."}}var Kt="dev";function ve(e){return ye(e)}function Te(e,t=h()){let i=B(e,{audience:Kt,ignoreTime:true});return i.ok?t>=i.claims.exp*1e3?{ok:false,reason:"expired",message:"Your dev token ran out. Run `npx buoy login` again."}:{ok:true,claims:i.claims}:{ok:false,reason:"invalid",message:"This dev token is not valid. Run `npx buoy login` again."}}var Ot="This is a dev token. It only works in dev builds. A live build turns on Buoy with Sign in with Buoy.";async function w(e,t){let i;try{return await Promise.race([e,new Promise(r=>{i=setTimeout(()=>r(t),15e3)})])}finally{i&&clearTimeout(i)}}function k(e){return e.tier==="pro"||e.tier==="free"?e.tier:e.isPro?"pro":"free"}var Mt=class{constructor(){this.state={isInitialized:false,isValidating:false,isPro:false,tier:"free",tierSource:"none",licenseKey:null,error:null,errorKind:null,expiresAt:null};this.listeners=new Set;this.revalidationTimer=null;this.cachedLicense=null;this.fingerprintPromise=null;this.initPromise=null;this.generation=0;this.persistence=Promise.resolve();this.verifiedAt=null;this.expiryTimer=null;this.removeForegroundListener=null;this.sessionUnsubscribe=null;this.retryTimer=null;this.retryAttempt=0;this.deviceRegistrationCallback=null;this.pendingRegistration=null}async initialize(){if(this.initPromise)return this.initPromise;if(!this.state.isInitialized)return this.initPromise=this._doInitialize(),this.initPromise}async _doInitialize(){if(this.state.isInitialized)return;let e=this.generation;this.sessionUnsubscribe||(this.sessionUnsubscribe=E.subscribe(()=>this.emit({type:"validated",isPro:this.isPro()}))),await w(E.load().catch(()=>{}),void 0);try{let t=await w(this.loadCachedLicense(),null);if(e!==this.generation){this.state.isInitialized=true,this.emit({type:"initialized",isPro:this.state.isPro}),this.startRevalidationTimer();return}if(t&&(this.configuredKey===void 0||t.licenseKey===this.configuredKey)){let i=A(t);this.cachedLicense=t,this.state.licenseKey=t.licenseKey,i&&this.isCacheValid(t)?this.applyTier(k(t)):this.lock(),this.state.expiresAt=t.expiresAt?new Date(t.expiresAt):null,i&&this.isCacheValid(t)&&this.setVerifiedAt(t.cachedAt),this.configuredKey===void 0&&t.licenseKey&&!(i&&this.isCacheValid(t)&&this.isCacheFresh(t))&&this.validateInBackground(t.licenseKey)}this.state.isInitialized=true,this.emit({type:"initialized",isPro:this.state.isPro}),this.startRevalidationTimer()}catch(t){console.warn("[React Buoy] License initialization failed:",t),this.state.isInitialized=true,this.emit({type:"initialized",isPro:false})}}async configure(e){if(e===void 0)return await this.initialize(),this.hasAccountAccess();let t=b(e),i=++this.generation;return this.cancelRetry(),this.configuredKey=t||null,this.state.licenseKey!==this.configuredKey&&this.lock(),this.state.licenseKey=this.configuredKey,this.state.isValidating=!!t,this.emit({type:"validating",isPro:this.isPro()}),await this.initialize(),i!==this.generation?false:t?this.setLicenseKey(t):(await this.clearLicense(),false)}async setLicenseKey(e,t){if(!e||e.trim().length===0)return this.state.error="License key is required",this.state.errorKind="verdict",false;let i=b(e),r=++this.generation;if(this.cancelRetry(),t?.isRetry||(this.retryAttempt=0),this.state.licenseKey!==i&&this.lock(),this.state.licenseKey=i,this.state.isValidating=true,this.state.error=null,this.state.errorKind=null,this.emit({type:"validating",isPro:this.isPro()}),he(i))return this.admitToken(i,r);if(z(i))return this.admitServiceKey(i,r);if(ve(i))return this.admitDevToken(i);if(!this.cachedLicense){let a=await w(this.loadCachedLicense(),null);if(r!==this.generation)return false;a?.licenseKey===i&&(this.cachedLicense=a,A(a)&&this.isCacheValid(a)&&(this.applyTier(k(a)),this.state.expiresAt=a.expiresAt?new Date(a.expiresAt):null,this.setVerifiedAt(a.cachedAt),this.emit({type:"validating",isPro:this.isPro()})))}let s=this.cachedLicense;if(!t?.force&&s&&s.valid&&s.licenseKey===i&&s.fingerprint&&A(s)&&this.isCacheValid(s)&&this.isCacheFresh(s))return this.applyTier(k(s)),this.state.licenseKey=i,this.state.isValidating=false,this.state.error=null,this.state.errorKind=null,this.state.expiresAt=s.expiresAt?new Date(s.expiresAt):null,this.setVerifiedAt(s.cachedAt),this.emit({type:"validated",isPro:this.state.isPro}),true;let n=await w(this.loadHold(),null);if(r!==this.generation)return false;let c=(n?.backoffUntil??0)-h();if(c>0)return this.standDown(i,r,"License server is rate limited; retrying later.",c);if(!t?.force&&n?.key===i&&n.verdictAt!==void 0&&h()-n.verdictAt>=0&&h()-n.verdictAt<Ne)return this.lock(),this.state.isValidating=false,this.state.error=n.detail||"License could not be verified",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),false;let o;try{o=await De(i)}catch(a){let l=a instanceof Error?a.message:"Validation failed";if(r!==this.generation)return false;if(Q(a)){if(Se(a)){let u=Math.min(Ke(a,q,Re)*2**this.retryAttempt,q);return this.retryAttempt+=1,await this.saveHold({...this.hold,backoffUntil:h()+u}),r!==this.generation?false:this.standDown(i,r,l,u)}return this.state.isValidating=false,this.cachedLicense&&this.cachedLicense.licenseKey===i&&A(this.cachedLicense)&&this.isCacheValid(this.cachedLicense)?(this.applyTier(k(this.cachedLicense)),this.state.error=null,this.state.errorKind=null,this.state.expiresAt=this.cachedLicense.expiresAt?new Date(this.cachedLicense.expiresAt):null,this.setVerifiedAt(this.cachedLicense.cachedAt),this.keyTier()!=="pro"&&this.scheduleRetry(i),this.emit({type:"validated",isPro:this.state.isPro}),true):(this.state.error=l,this.state.errorKind="transport",this.scheduleRetry(i),this.emit({type:"error",isPro:this.state.isPro,error:l}),false)}return this.state.isValidating=false,this.state.error=l,this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:l}),false}try{if(r!==this.generation)return false;let a=$e(o),l=o.data?.relationships?.product?.data?.id;if((!l||l===I.productId)&&a.code==="EXPIRED"&&(lt(o.data)||this.cachedLicense?.licenseKey===i&&A(this.cachedLicense)&&this.isCacheValid(this.cachedLicense))){let O=h(),W=await this.saveCache({licenseKey:i,licenseId:o.data?.id??"",valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:O,lastValidatedAt:O,fingerprint:this.cachedLicense?.licenseKey===i?this.cachedLicense.fingerprint:void 0},r);return r!==this.generation||!W?false:(this.cachedLicense=W,this.applyTier("free"),this.state.isValidating=false,this.state.expiresAt=null,this.setVerifiedAt(O),this.emit({type:"validated",isPro:false}),true)}if(je(a.code)||!Ue(o))return this.lock(),this.state.isValidating=false,this.state.error=a.detail||"License could not be verified",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),await this.clearCachedLicense(),await this.saveHold({key:i,code:a.code,detail:this.state.error,verdictAt:h()}),false;if(!o.data)return this.lock(),this.state.isValidating=false,this.state.error=a.detail||"License not found",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),await this.clearCachedLicense(),false;let u=ot(o.data),f=o.data.id,P=h(),G=Fe(o)>=Ce,J=F(),Ae=J&&!G,_e=u==="free"&&(G||!J),v=this.cachedLicense?.fingerprint,x=this.cachedLicense?.deviceName,Y=false;if(!v){if(v=await w(this.getOrCreateDeviceFingerprint(),U()),r!==this.generation)return false;x=$(),Y=Ae}let S=false;Y&&(this.deviceRegistrationCallback?(this.pendingRegistration={licenseId:f,licenseKey:i,fingerprint:v},this.deviceRegistrationCallback({fingerprint:v,defaultName:x}),S=true):this.registerDeviceAsync(f,i,v,x));let K={licenseKey:i,licenseId:f,valid:true,isPro:u==="pro",tier:u,expiresAt:o.data.attributes.expiry??null,cachedAt:P,lastValidatedAt:P,fingerprint:S?void 0:v,deviceName:S?void 0:x,quiet:_e||void 0},X=await this.saveCache(K,r);return r!==this.generation||!X||(this.cachedLicense=X,this.applyTier(u),this.state.isValidating=false,this.state.expiresAt=K.expiresAt?new Date(K.expiresAt):null,this.setVerifiedAt(P),this.hold&&await this.saveHold(null),r!==this.generation)?false:(this.retryAttempt=0,this.emit({type:"validated",isPro:this.state.isPro}),true)}catch(a){let l=a instanceof Error?a.message:"Validation failed";return r!==this.generation||(this.state.isValidating=false,this.state.error=l,this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:l})),false}}scheduleRetry(e){this.cancelRetry();let t=Ie[this.retryAttempt];if(t===void 0)return;this.retryAttempt+=1;let i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null,i===this.generation&&(this.state.isValidating||this.setLicenseKey(e,{force:true,isRetry:true}))},t),typeof p<"u"&&p&&(globalThis.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer)}async admitServiceKey(e,t){let i=await St(e,{origin:E.origin});if(t!==this.generation)return false;if(!i.ok)return this.lock(),this.state.isValidating=false,this.state.error=i.detail,this.state.errorKind=i.reason==="network"?"transport":"verdict",this.emit({type:"error",isPro:false,error:i.detail}),i.reason==="network"&&this.scheduleRetry(e),false;let r=i.claims.exp*1e3;return this.applyTier(i.claims.tier==="free"?"free":"pro"),this.state.isValidating=false,this.state.expiresAt=new Date(r),this.setVerifiedAt(h()),this.retryAttempt=0,this.scheduleRetryIn(e,Math.max(6e4,r-3600*1e3-h())),this.emit({type:"validated",isPro:this.isPro()}),true}admitDevToken(e){let t=Te(e);if(!t.ok||!F()){let r=t.ok?Ot:t.message;return this.lock(),this.state.isValidating=false,this.state.error=r,this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:r}),false}let i=h();return this.applyTier(t.claims.tier==="free"?"free":"pro"),this.state.isValidating=false,this.state.expiresAt=new Date(t.claims.exp*1e3),this.setVerifiedAt(Math.min(i,t.claims.iat*1e3)),this.retryAttempt=0,this.emit({type:"validated",isPro:this.isPro()}),true}async admitToken(e,t){let i=at(e);if(t!==this.generation)return false;if(!i)return this.lock(),this.state.isValidating=false,this.state.error="This key is not valid. Check that it was copied completely.",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),await this.clearCachedLicense(),false;let r=h(),s=this.cachedLicense?.fingerprint??await w(this.getOrCreateDeviceFingerprint(),U());if(t!==this.generation)return false;let n=await this.saveCache({licenseKey:e,licenseId:`token:${i.accountHash}`,valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:r,lastValidatedAt:r,fingerprint:s,deviceName:this.cachedLicense?.deviceName??$(),quiet:true},t);return t!==this.generation||!n?false:(this.cachedLicense=n,this.applyTier("free"),this.state.isValidating=false,this.state.expiresAt=null,this.setVerifiedAt(r),this.retryAttempt=0,this.emit({type:"validated",isPro:false}),true)}standDown(e,t,i,r){if(t!==this.generation)return false;this.state.isValidating=false;let s=this.cachedLicense;return s&&s.licenseKey===e&&A(s)&&this.isCacheValid(s)?(this.applyTier(k(s)),this.state.error=null,this.state.errorKind=null,this.state.expiresAt=s.expiresAt?new Date(s.expiresAt):null,this.setVerifiedAt(s.cachedAt),this.keyTier()!=="pro"&&this.scheduleRetryIn(e,r),this.emit({type:"validated",isPro:this.state.isPro}),true):(this.state.error=i,this.state.errorKind="transport",this.scheduleRetryIn(e,r),this.emit({type:"error",isPro:this.state.isPro,error:i}),false)}scheduleRetryIn(e,t){this.cancelRetry();let i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null,i===this.generation&&(this.state.isValidating||this.setLicenseKey(e,{force:true,isRetry:true}))},Math.max(1,Math.min(t,2147483647))),typeof p<"u"&&p&&(g.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer)}cancelRetry(){this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=null)}async clearLicense(){this.generation+=1,this.cancelRetry(),this.retryAttempt=0,this.configuredKey=null,this.state.licenseKey=null,this.lock(),this.state.expiresAt=null,this.state.error=null,this.state.errorKind=null,this.cachedLicense=null,this.pendingRegistration=null,this.state.isValidating=false,this.emit({type:"deactivated",isPro:false}),await this.clearCachedLicense(),this.hold?.verdictAt!==void 0&&await this.saveHold(this.hold.backoffUntil?{backoffUntil:this.hold.backoffUntil}:null)}applyTier(e){this.state.tier=e,this.state.tierSource="key",this.state.isPro=e==="pro"}lock(){this.state.tier="free",this.state.tierSource="none",this.state.isPro=false,this.verifiedAt=null,this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=null}getTier(){return this.credential().tier}credential(){let e=this.keyTier(),t=this.sessionTier();return t!==null&&(!this.hasKeyAccess()||t==="pro"&&e!=="pro")?{source:"session",tier:t}:{source:this.state.tierSource,tier:e}}keyTier(){return!this.hasKeyAccess()||this.state.expiresAt&&h()>=this.state.expiresAt.getTime()?"free":this.state.tier}sessionTier(){let e=E.getStatus();return e.state!=="active"?null:e.claims.tier==="free"?"free":"pro"}isPro(){return this.getTier()==="pro"}hasEntitlement(e){return this.isPro()}getState(){let{source:e,tier:t}=this.credential();return{...this.state,tier:t,tierSource:e,isPro:t==="pro",access:this.getAccountAccess()}}getAccountAccess(){let e=this.getKeyAccess();if(e.status==="admitted")return e;let t=E.getStatus();return t.state==="active"?{status:"admitted",reason:null,validUntil:t.validUntil}:e}getKeyAccess(){return de({initialized:this.state.isInitialized,validating:this.state.isValidating,key:this.state.licenseKey,tierSource:this.state.tierSource,verifiedAt:this.verifiedAt})}hasAccountAccess(){return this.getAccountAccess().status==="admitted"}hasKeyAccess(){return this.getKeyAccess().status==="admitted"}setVerifiedAt(e){this.verifiedAt=e,this.expiryTimer&&clearTimeout(this.expiryTimer);let t=e+N,i=this.state.expiresAt?.getTime(),r=(i&&i>h()?Math.min(t,i):t)-h();this.expiryTimer=setTimeout(()=>{this.expiryTimer=null,this.verifiedAt===e&&(this.emit({type:"validated",isPro:this.isPro()}),h()<t&&this.setVerifiedAt(e))},Math.max(1,Math.min(r,2147483647))),typeof p<"u"&&p&&(g.__LICENSE_MANAGER_EXPIRY_TIMER__=this.expiryTimer)}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}onDeviceRegistrationNeeded(e){return this.deviceRegistrationCallback=e,()=>{this.deviceRegistrationCallback=null}}async completeDeviceRegistration(e){if(!this.pendingRegistration){console.warn("[Buoy] No pending device registration");return}let{licenseId:t,licenseKey:i,fingerprint:r}=this.pendingRegistration,s=this.generation,n=e.trim()||$(),c=se();try{await te(t,i,r,n,c)}catch(o){let a=o instanceof Error?o.message:String(o);if(!(a==="FINGERPRINT_TAKEN"||a==="MACHINE_LIMIT_EXCEEDED"))throw console.warn("[Buoy] Device registration failed:",a),o}if(s===this.generation){if(this.cachedLicense?.licenseKey===i){let o=await this.saveCache({...this.cachedLicense,fingerprint:r,deviceName:n},s);if(s!==this.generation)return;o&&(this.cachedLicense=o)}this.pendingRegistration=null}}emit(e){[...this.listeners].forEach(t=>{try{t(e)}catch(i){console.warn("[React Buoy] License listener error:",i)}})}isCacheValid(e){let t=h()-e.cachedAt;return!(!e.valid||!Number.isFinite(e.cachedAt)||t<0||t>=N)}isCacheFresh(e){let t=this.isQuiet(e)?Pe:Le;return h()-(e.lastValidatedAt??0)<t}isQuiet(e){return e.quiet!==void 0?e.quiet:k(e)==="free"&&!F()}async loadHold(){if(this.hold!==void 0)return this.hold;try{let e=await(await y()).getItem(D),t=e?JSON.parse(e):null;this.hold=t&&typeof t=="object"?t:null}catch{this.hold=null}return this.hold}async saveHold(e){this.hold=e;try{let t=await y();e?await t.setItem(D,JSON.stringify(e)):await t.removeItem(D)}catch{}}async persistCache(e){let t=le(e);return await(await y()).setItem(M,JSON.stringify(t)),t}saveCache(e,t){let i=this.persistence.catch(()=>{}).then(async()=>{if(t!==this.generation)return null;let r=await this.persistCache(e);return t===this.generation?r:null});return this.persistence=i,w(i,le(e))}async loadCachedLicense(){try{await this.persistence.catch(()=>{});let e=await(await y()).getItem(M);return e?JSON.parse(e):null}catch(e){return console.warn("[Buoy] Error loading cached license:",e),null}}getOrCreateDeviceFingerprint(){return this.fingerprintPromise||(this.fingerprintPromise=(async()=>{let e=await y(),t=await e.getItem(V);if(t)return t;let i=this.cachedLicense?.fingerprint||U();return await e.setItem(V,i),i})()),this.fingerprintPromise}async clearCachedLicense(){this.cachedLicense=null;let e=this.persistence.catch(()=>{}).then(async()=>{await(await y()).removeItem(M)});this.persistence=e,await w(e.catch(()=>{}),void 0)}registerDeviceAsync(e,t,i,r){let s=se();te(e,t,i,r,s).catch(n=>{n.message!=="FINGERPRINT_TAKEN"&&n.message!=="MACHINE_LIMIT_EXCEEDED"&&console.warn("[Buoy] Device registration failed (non-blocking):",n.message)})}validateInBackground(e){this.setLicenseKey(e,{force:true,isRetry:true})}refreshAccess(){this.emit({type:"validated",isPro:this.isPro()})}startRevalidationTimer(){this.removeForegroundListener||(this.removeForegroundListener=ct(()=>this.refreshAccess()),typeof p<"u"&&p&&(g.__LICENSE_MANAGER_FOREGROUND_OFF__=this.removeForegroundListener)),this.revalidationTimer&&clearInterval(this.revalidationTimer),this.revalidationTimer=setInterval(()=>{if(!this.state.licenseKey||this.state.isValidating)return;let e=this.cachedLicense;e&&e.licenseKey===this.state.licenseKey&&this.isCacheFresh(e)||this.validateInBackground(this.state.licenseKey)},xe),typeof p<"u"&&p&&(globalThis.__LICENSE_MANAGER_TIMER__=this.revalidationTimer)}stopRevalidationTimer(){this.removeForegroundListener?.(),this.removeForegroundListener=null,this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=null,this.revalidationTimer&&(clearInterval(this.revalidationTimer),this.revalidationTimer=null),this.cancelRetry()}async forceReregister(){this.cachedLicense&&(this.cachedLicense.fingerprint=void 0,this.cachedLicense.deviceName=void 0),this.fingerprintPromise=null,await this.clearCachedLicense();try{await(await y()).removeItem(V)}catch{}}},Vt=new Mt,g=globalThis;typeof p<"u"&&p&&(g.__LICENSE_MANAGER_FOREGROUND_OFF__?.(),g.__LICENSE_MANAGER_FOREGROUND_OFF__=void 0,g.__LICENSE_MANAGER_EXPIRY_TIMER__&&clearTimeout(g.__LICENSE_MANAGER_EXPIRY_TIMER__),g.__LICENSE_MANAGER_TIMER__&&clearInterval(g.__LICENSE_MANAGER_TIMER__),g.__LICENSE_MANAGER_RETRY_TIMER__&&(clearTimeout(g.__LICENSE_MANAGER_RETRY_TIMER__),g.__LICENSE_MANAGER_RETRY_TIMER__=void 0));export{h as a,T as b,Q as c,he as d,de as e,d as f,E as g,z as h,b as i,Pt as j,ve as k,Te as l,Vt as m};/*!
|
|
2
|
+
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
5
|
+
* (https://buoy.gg/terms), not an OSS license.
|
|
6
|
+
*
|
|
7
|
+
* This file configures the technological measure that controls access to paid
|
|
8
|
+
* Buoy Pro features. Modifying, patching, aliasing, stubbing, or otherwise
|
|
9
|
+
* altering it — including repointing `apiUrl` at a stand-in license server — so
|
|
10
|
+
* that Pro features unlock without a valid license is a breach of the EULA and,
|
|
11
|
+
* in the United States, circumvention of an access control under 17 U.S.C.
|
|
12
|
+
* § 1201, which applies whether or not any code is copied.
|
|
13
|
+
*
|
|
14
|
+
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
15
|
+
* including a free one (`npx buoy login`). Buy a key at
|
|
16
|
+
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
17
|
+
*//*!
|
|
18
|
+
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
19
|
+
*
|
|
20
|
+
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
21
|
+
* (https://buoy.gg/terms), not an OSS license.
|
|
22
|
+
*
|
|
23
|
+
* This file is part of the technological measure that controls access to paid
|
|
24
|
+
* Buoy Pro features. Modifying, patching, aliasing, stubbing, or otherwise
|
|
25
|
+
* altering it so that Pro features unlock without a valid license is a breach
|
|
26
|
+
* of the EULA and, in the United States, circumvention of an access control
|
|
27
|
+
* under 17 U.S.C. § 1201 — which applies whether or not any code is copied.
|
|
28
|
+
*
|
|
29
|
+
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
30
|
+
* including a free one (`npx buoy login`). Buy a key at
|
|
31
|
+
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
32
|
+
*//*!
|
|
33
|
+
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
34
|
+
*
|
|
35
|
+
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
36
|
+
* (https://buoy.gg/terms), not an OSS license.
|
|
37
|
+
*//*!
|
|
38
|
+
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
39
|
+
*
|
|
40
|
+
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
41
|
+
* (https://buoy.gg/terms), not an OSS license.
|
|
42
|
+
*
|
|
43
|
+
* This file is part of the technological measure that controls access to paid
|
|
44
|
+
* Buoy Pro features: it decides which tier a validated key grants. Modifying,
|
|
45
|
+
* patching, aliasing, stubbing, or otherwise altering it so that Pro features
|
|
46
|
+
* unlock without a valid license is a breach of the EULA and, in the United
|
|
47
|
+
* States, circumvention of an access control under 17 U.S.C. § 1201 — which
|
|
48
|
+
* applies whether or not any code is copied.
|
|
49
|
+
*
|
|
50
|
+
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
51
|
+
* including a free one (`npx buoy login`). Buy a key at
|
|
52
|
+
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
53
|
+
*//*!
|
|
54
|
+
* PROPRIETARY — Buoy license enforcement. NOT open source.
|
|
55
|
+
*
|
|
56
|
+
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
57
|
+
* (https://buoy.gg/terms), not an OSS license.
|
|
58
|
+
*//*!
|
|
59
|
+
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
60
|
+
*
|
|
61
|
+
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
62
|
+
* (https://buoy.gg/terms), not an OSS license.
|
|
63
|
+
*
|
|
64
|
+
* This file is the technological measure that controls access to paid Buoy Pro
|
|
65
|
+
* features. Modifying, patching, aliasing, stubbing, or otherwise altering it
|
|
66
|
+
* so that Pro features unlock without a valid license is a breach of the EULA
|
|
67
|
+
* and, in the United States, circumvention of an access control under
|
|
68
|
+
* 17 U.S.C. § 1201 — which applies whether or not any code is copied.
|
|
69
|
+
*
|
|
70
|
+
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
71
|
+
* including a free one (`npx buoy login`). Buy a key at
|
|
72
|
+
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
73
|
+
*/
|
package/lib/web/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use client";import{a as m,b as k,c as A,d as P,e as I,f as h,g as r,h as K,i as z,j as E,k as d,l as b,m as n}from"./chunks/chunk-
|
|
1
|
+
"use client";import{a as m,b as k,c as A,d as P,e as I,f as h,g as r,h as K,i as z,j as E,k as d,l as b,m as n}from"./chunks/chunk-FPGC7BWV.mjs";import{useEffect as p,useState as w}from"react";function o(s=new Date(m())){let e=s.getDay();return e===0||e===6}function f(){let[s,e]=w(()=>o());return p(()=>{let a=setInterval(()=>{let t=o();e(i=>i===t?i:t)},6e4);return()=>clearInterval(a)},[]),s}function g(s,e){return e&&s}import{useState as y,useEffect as L,useCallback as l}from"react";function c(){let[s,e]=y(n.getState());L(()=>{let i=n.subscribe(u=>{e(n.getState())});return n.initialize().then(()=>{e(n.getState())}),i},[]);let a=l(async i=>{let u=await n.setLicenseKey(i,{force:true});return e(n.getState()),u},[]),t=l(async()=>{await n.clearLicense(),e(n.getState())},[]);return{...s,setLicenseKey:a,clearLicense:t}}function T(){return c().access}function v(){let{tier:s,isInitialized:e}=c();return e?s:"free"}function F(s){let{isPro:e,isInitialized:a,isValidating:t}=c();return{hasAccess:e,isPro:e,isLoading:!a||t}}function M(){let{isPro:s,isInitialized:e,access:a}=c(),t=g(a.status==="admitted",f());return e&&s||t}function W(){let{isPro:s,isInitialized:e,access:a}=c(),t=e&&s,i=g(a.status==="admitted",f())&&!t;return{isPro:t||i,isLicensed:t,isWeekendFree:i,reason:t?"licensed":i?"weekend":null}}function B(){let[s,e]=y(()=>r.getStatus());L(()=>{let i=()=>e(r.getStatus()),u=r.subscribe(i);return r.load().then(i,i),u},[]);let a=l(()=>r.signIn(),[]),t=l(()=>r.signOut(),[]);return{status:s,signIn:a,signOut:t}}function C(){let s=r.getAccessToken();if(s)return{token:s,origin:r.origin};let e=n.getState().licenseKey;return e&&d(e)&&n.hasAccountAccess()&&b(e).ok?{token:e,origin:r.origin}:null}async function D(s){let{LicenseManager:e}=await import("./chunks/LicenseManager-GJQ6MGWU.mjs");return await e.initialize(),e.setLicenseKey(s)}function S(){return g(n.hasAccountAccess(),o())}function x(){return n.isPro()?true:S()}function O(s){return n.hasEntitlement(s)?true:S()}export{n as LicenseManager,k as LicenseTransportError,h as SessionError,r as SessionManager,C as getSyncCredential,O as hasEntitlement,P as isBuoyToken,d as isDevToken,x as isPro,K as isServiceKey,A as isTransientLicenseError,o as isWeekendFreePro,z as normalizeLicenseKey,I as resolveAccountAccess,E as sameLicenseKey,D as setLicenseKey,T as useAccountAccess,B as useBuoySession,F as useFeatureAccess,M as useIsPro,c as useLicense,W as useProAccess,v as useTier,f as useWeekendFreePro};/*!
|
|
2
2
|
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use client";import{m as e}from"./chunk-AC36O63C.mjs";export{e as LicenseManager};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use client";var d=process.env.NODE_ENV!=="production";typeof window<"u"&&typeof globalThis.global>"u"&&(globalThis.global=globalThis);var L=new Map,Ee={async getItem(e){try{return localStorage.getItem(e)??L.get(e)??null}catch{return L.get(e)??null}},async setItem(e,t){L.set(e,t);try{localStorage.setItem(e,t)}catch{}},async removeItem(e){L.delete(e);try{localStorage.removeItem(e)}catch{}}};async function g(){return Ee}var I={accountId:"b0eba0e8-e904-4906-b644-38f9aab57bed",productId:"bf67cb43-eeef-40ca-b308-e6e1b22c0eaa",apiUrl:"https://buoy.gg/api/license"},ke="3b546fc717a62683f3236c70167899a271bee3ffc04df6ec1eea60a394a2f1e9",be="36abba0267f7f379d992a5b6df641daab4190bcc9e639e36f1378e3c1ac52746",R={free:"394e3351-1249-4f73-807a-703408604c0e",solo:"fa5d3c8f-5379-40f1-b63e-ab8a974b2f28",business:"690c2212-8e54-426f-b4b4-002ee1c0ae2b"},N=720*60*60*1e3,xe=1440*60*1e3,M="buoy-gg-license",V="buoy-gg-device-fingerprint",Le=3600*1e3,Ie=[5*1e3,20*1e3,60*1e3,300*1e3,900*1e3],Re=60*1e3,q=3600*1e3,Ne=1440*60*1e3,Pe=50,Ce=N,D="buoy-gg-license-hold";function h(){let e=globalThis.__buoyRealNow;return typeof e=="function"?e():Date.now()}var{apiUrl:Z}=I,T=class extends Error{constructor(e,t,i=null){super(e);this.isTransient=true;this.name="LicenseTransportError",this.status=t,this.retryAfterMs=i}};function Se(e){let t=e?.status;return t===429||t===402}function Ke(e,t,i){return e.status===402?t:e.retryAfterMs??i}function Oe(e){let t=s=>{try{return e.headers?.get?.(s)??null}catch{return null}},i=t("Retry-After");if(i){let s=Number(i);if(Number.isFinite(s)&&s>=0)return s*1e3;let n=Date.parse(i);if(Number.isFinite(n))return Math.max(0,n-h())}let r=Number(t("X-RateLimit-Reset"));return Number.isFinite(r)&&r>0?Math.max(0,r*1e3-h()):null}function Q(e){return e instanceof T||e instanceof TypeError?true:typeof e=="object"&&e!==null&&e.isTransient===true}async function Me(e){try{return(await e.json()).errors?.[0]?.detail||`HTTP ${e.status}`}catch{return`HTTP ${e.status}`}}function ee(){return{"Content-Type":"application/vnd.api+json",Accept:"application/vnd.api+json"}}async function Ve(e,t){let i=`${Z}/validate`,r={meta:{key:e}},s;try{s=await fetch(i,{signal:t,method:"POST",headers:ee(),body:JSON.stringify(r)})}catch(n){throw new T(n instanceof Error?n.message:"Network request failed",null)}if(!s.ok)throw new T(await Me(s),s.status,s.status===429?Oe(s):null);try{let n=await s.json();if(!n||typeof n!="object"||typeof n.meta?.valid!="boolean"||typeof n.meta?.code!="string"||!n.meta.code)throw new Error("Missing license verdict");return n}catch{throw new T("License server returned an unreadable response",s.status)}}async function De(e){let t=typeof AbortController=="function"?new AbortController:null,i;try{return await Promise.race([Ve(e,t?.signal),new Promise((r,s)=>{i=setTimeout(()=>{s(new T("License verification timed out. Please retry.",null)),t?.abort()},15e3)})])}finally{i&&clearTimeout(i)}}function Ue(e){if(!e.data)return false;let t=e.data.relationships?.product?.data?.id;return t&&t!==I.productId?false:e.meta.valid&&e.meta.code==="VALID"?true:["OVERDUE","NO_MACHINE","NO_MACHINES","TOO_MANY_MACHINES","TOO_MANY_CORES","TOO_MANY_PROCESSES","FINGERPRINT_SCOPE_MISMATCH","HEARTBEAT_NOT_STARTED","HEARTBEAT_DEAD"].includes(e.meta.code)}function Fe(e){let t=e.data?.relationships?.machines?.meta?.count;return typeof t=="number"&&Number.isFinite(t)?t:0}async function te(e,t,i,r,s){let n=`${Z}/machines`,c=await fetch(n,{method:"POST",headers:ee(),body:JSON.stringify({licenseId:e,licenseKey:t,fingerprint:i,name:r,metadata:s||{}})});if(!c.ok){let o=await c.json(),a=o.errors?.[0]?.code;if(a==="FINGERPRINT_TAKEN")throw new Error("FINGERPRINT_TAKEN");if(a==="MACHINE_LIMIT_EXCEEDED")throw new Error("MACHINE_LIMIT_EXCEEDED");let l=o.errors?.[0]?.detail||`HTTP ${c.status}`;throw new Error(l)}return c.json()}function $e(e){let{meta:t,data:i}=e;return{valid:t.valid,code:t.code,detail:t.detail,metadata:i?.attributes?.metadata}}function He(e){return["NOT_FOUND","SUSPENDED","EXPIRED","BANNED","PRODUCT_SCOPE_MISMATCH"].includes(e)}function U(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function F(){if(typeof d<"u")return d;try{if(typeof navigator<"u"&&/electron/i.test(navigator.userAgent))return true}catch{}return typeof process<"u"&&process.env.NODE_ENV!=="production"}function ie(){if(typeof navigator>"u")return"unknown";let e=navigator.userAgent.toLowerCase(),t=navigator.platform?.toLowerCase()||"";return e.includes("electron")?t.includes("mac")||e.includes("mac")?"electron-macos":t.includes("win")||e.includes("win")?"electron-windows":t.includes("linux")||e.includes("linux")?"electron-linux":"electron":t.includes("mac")||e.includes("mac")?"macos":t.includes("win")||e.includes("win")?"windows":t.includes("linux")||e.includes("linux")?"linux":/iphone|ipad|ipod/.test(e)?"ios-web":e.includes("android")?"android-web":"web"}function je(){if(typeof navigator>"u")return"unknown";let e=navigator.userAgent,t=e.match(/Mac OS X ([\d_.]+)/);if(t)return t[1].replace(/_/g,".");let i=e.match(/Windows NT ([\d.]+)/);return i?{"10.0":"10/11","6.3":"8.1","6.2":"8","6.1":"7"}[i[1]]||i[1]:e.includes("Linux")?"Linux":"unknown"}function re(){if(typeof navigator>"u")return{name:"unknown",version:"unknown"};let e=navigator.userAgent,t=e.match(/Electron\/([\d.]+)/);if(t)return{name:"Electron",version:t[1]};let i=e.match(/Chrome\/([\d.]+)/);if(i&&!e.includes("Edg"))return{name:"Chrome",version:i[1]};let r=e.match(/Edg\/([\d.]+)/);if(r)return{name:"Edge",version:r[1]};let s=e.match(/Firefox\/([\d.]+)/);if(s)return{name:"Firefox",version:s[1]};let n=e.match(/Version\/([\d.]+).*Safari/);return n?{name:"Safari",version:n[1]}:{name:"unknown",version:"unknown"}}function se(){let e={platform:ie(),osVersion:je(),isSimulator:"false"},t=re();e.browser=t.name,e.browserVersion=t.version,typeof window<"u"&&window.screen&&(e.screenResolution=`${window.screen.width}x${window.screen.height}`),typeof navigator<"u"&&navigator.language&&(e.language=navigator.language);try{e.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone}catch{}return e}function $(){let e=ie(),t=re();if(e.startsWith("electron")){let i=e.replace("electron-","").replace("electron","Desktop");return`${i.charAt(0).toUpperCase()+i.slice(1)} App`}return t.name!=="unknown"?`${t.name} Browser`:"Web Browser"}var Be=["v1"],ze="buoy.gg/pro:9f3c1d7a4b2e:license-cache";function ne(e,t){let i=t>>>0;for(let r=0;r<e.length;r++)i^=e.charCodeAt(r),i=Math.imul(i,16777619)>>>0;return i>>>0}function Ge(e,t){let i=[t,e.licenseKey,e.licenseId,e.isPro?"1":"0",e.expiresAt??"",String(e.cachedAt)];return t!=="v1"&&i.push(e.tier??""),i.push(ze),i.join("\0")}function ae(e,t){let i=Ge(e,t),r=ne(i,2166136261),s=ne(i,2135587861);return`${t}.${r.toString(16)}${s.toString(16)}`}function oe(e){return ae(e,"v2")}function le(e){return{...e,sig:oe(e)}}function A(e){return!e||typeof e.sig!="string"||e.sig.length===0?false:e.sig===oe(e)?true:Be.some(t=>e.sig.startsWith(`${t}.`)&&e.sig===ae(e,t))}import Ye from"tweetnacl";var Je=1,Xe=1,We="buoy-free-token-v1:",qe=22,Ze=64,ce=/^BUOY1-([A-Z2-7]+)-([A-Z2-7]+)$/;function he(e){return ce.test(e.trim().toUpperCase())}var Qe="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";function ue(e){let t=[],i=0,r=0;for(let s of e){let n=Qe.indexOf(s);if(n===-1)return null;r=r<<5|n,i+=5,i>=8&&(t.push(r>>>i-8&255),i-=8)}return new Uint8Array(t)}function et(e){let t=e.trim().toLowerCase(),i=new Uint8Array(t.length/2);for(let r=0;r<i.length;r+=1)i[r]=parseInt(t.slice(r*2,r*2+2),16);return i}function tt(e){let t="";for(let i of e)t+=i.toString(16).padStart(2,"0");return t}function it(e){if(typeof TextEncoder<"u")return new TextEncoder().encode(e);let t=new Uint8Array(e.length);for(let i=0;i<e.length;i+=1)t[i]=e.charCodeAt(i)&255;return t}function rt(e,t){let i=new Uint8Array(e.length+t.length);return i.set(e,0),i.set(t,e.length),i}function st(e){return rt(it(We),e)}var nt=ke;function at(e,t=nt){let i=ce.exec(e.trim().toUpperCase());if(!i)return null;let r=ue(i[1]),s=ue(i[2]);if(!r||!s||r.length!==qe||s.length!==Ze||r[0]!==Je||r[1]!==Xe)return null;try{if(!Ye.sign.detached.verify(st(r),s,et(t)))return null}catch{return null}return{tier:"free",issuedAt:((r[2]<<24>>>0)+(r[3]<<16)+(r[4]<<8)+r[5])*1e3,accountHash:tt(r.slice(6,22))}}function ot(e){let t=e.relationships?.policy?.data?.id;return t===R.free?"free":t===R.solo||t===R.business?"pro":e.attributes?.metadata?.plan==="free"?"free":"pro"}function lt(e){if(!e)return false;let t=e.relationships?.product?.data?.id;if(t)return t===I.productId;let i=e.relationships?.policy?.data?.id;return Object.values(R).some(r=>r===i)}function de(e,t=h()){let i=e.verifiedAt!==null&&Number.isFinite(e.verifiedAt)&&e.verifiedAt<=t?e.verifiedAt+N:null;return e.initialized&&e.key&&e.tierSource!=="none"&&i!==null&&t<i?{status:"admitted",reason:null,validUntil:i}:i!==null&&t>=i?{status:"locked",reason:"verification_expired",validUntil:i}:!e.initialized||e.validating?{status:"initializing",reason:"checking",validUntil:i}:{status:"locked",reason:i!==null&&t>=i?"verification_expired":"account_required",validUntil:i}}function ct(e){if(typeof window>"u"||typeof window.addEventListener!="function")return()=>{};let t=()=>{document.hidden||e()};return window.addEventListener("focus",e),document.addEventListener("visibilitychange",t),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",t)}}import ht from"tweetnacl";var fe="buoys1",ut="buoy-session-v1:",pe=1,H=60,dt=64,ft=["free","pro","business"],y="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function j(e){let t="",i=0;for(;i+2<e.length;i+=3){let s=e[i]<<16|e[i+1]<<8|e[i+2];t+=y[s>>>18&63]+y[s>>>12&63]+y[s>>>6&63]+y[s&63]}let r=e.length-i;if(r===1){let s=e[i]<<16;t+=y[s>>>18&63]+y[s>>>12&63]}else if(r===2){let s=e[i]<<16|e[i+1]<<8;t+=y[s>>>18&63]+y[s>>>12&63]+y[s>>>6&63]}return t}function me(e){if(!/^[A-Za-z0-9_-]*$/.test(e)||e.length%4===1)return null;let t=[],i=0,r=0;for(let s of e)r=r<<6|y.indexOf(s),i+=6,i>=8&&(t.push(r>>>i-8&255),i-=8);return new Uint8Array(t)}function pt(e){if(typeof TextEncoder<"u")return new TextEncoder().encode(e);let t=unescape(encodeURIComponent(e)),i=new Uint8Array(t.length);for(let r=0;r<t.length;r+=1)i[r]=t.charCodeAt(r);return i}function mt(e){try{if(typeof TextDecoder<"u")return new TextDecoder("utf-8",{fatal:true}).decode(e);let t="";for(let i of e)t+=String.fromCharCode(i);return decodeURIComponent(escape(t))}catch{return null}}function yt(e){let t=e.trim().toLowerCase(),i=new Uint8Array(t.length/2);for(let r=0;r<i.length;r+=1)i[r]=parseInt(t.slice(r*2,r*2+2),16);return i}function gt(e){return pt(ut+e)}function _(e,t=256){return typeof e=="string"&&e.length>0&&e.length<=t}function ye(e){return typeof e=="number"&&Number.isInteger(e)&&e>0}function wt(e){if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return!_(t.sid)||!_(t.sub)||!_(t.aud,512)||t.team!==null&&!_(t.team)||!ft.includes(t.tier)||t.role!==void 0&&!_(t.role,64)||t.tools!==void 0&&(!Array.isArray(t.tools)||t.tools.length>256||!t.tools.every(i=>_(i,64)))||!ye(t.iat)||!ye(t.exp)||t.exp<=t.iat?null:{v:pe,sid:t.sid,sub:t.sub,team:t.team,tier:t.tier,...t.role!==void 0?{role:t.role}:{},...t.tools!==void 0?{tools:[...t.tools]}:{},aud:t.aud,iat:t.iat,exp:t.exp}}var vt=be;function ge(e){return e.startsWith(`${fe}.`)}function B(e,t){let i=t.publicKeyHex??vt;if(!i)return{ok:false,reason:"no-key"};let r=typeof e=="string"?e.split("."):[];if(r.length!==3||r[0]!==fe)return{ok:false,reason:"malformed"};let[,s,n]=r,c=me(s),o=me(n);if(!c||!o||o.length!==dt)return{ok:false,reason:"malformed"};try{if(!ht.sign.detached.verify(gt(s),o,yt(i)))return{ok:false,reason:"signature"}}catch{return{ok:false,reason:"signature"}}let a=mt(c),l;try{l=a===null?null:JSON.parse(a)}catch{return{ok:false,reason:"malformed"}}if(!l||typeof l!="object"||l.v!==pe)return{ok:false,reason:l&&typeof l=="object"?"version":"malformed"};let u=wt(l);if(!u)return{ok:false,reason:"claims"};if(u.aud!==t.audience)return{ok:false,reason:"audience"};if(t.ignoreTime)return{ok:true,claims:u};let f=Math.floor((t.nowMs??h())/1e3);return f>=u.exp+H?{ok:false,reason:"expired"}:u.iat>f+H?{ok:false,reason:"not-yet-valid"}:{ok:true,claims:u}}var Tt="@buoy/session:",P=10080*60*1e3,we=360*60*1e3,At=300*1e3,_t=15e3,Et="https://buoy.gg",kt="buoy:signin",bt=600*1e3,p=class extends Error{constructor(e,t){super(t);this.code=e;this.name="SessionError"}};function xt(){let e=globalThis;return{fetch:(t,i)=>fetch(t,i),storage:g,now:h,random:t=>{let i=new Uint8Array(t);if(!e.crypto?.getRandomValues)throw new p("not_configured","This platform has no secure random source.");return e.crypto.getRandomValues(i)},sha256:async t=>{if(!e.crypto?.subtle)throw new p("not_configured","Sign-in needs a secure page (https or localhost).");return new Uint8Array(await e.crypto.subtle.digest("SHA-256",t))},openPopup:()=>{if(!e.window?.open)return null;let t=480,i=640,r=Math.max(0,(e.window.screenX??0)+((e.window.outerWidth??t)-t)/2),s=Math.max(0,(e.window.screenY??0)+((e.window.outerHeight??i)-i)/2);return e.window.open("about:blank","buoy-signin",`popup,width=${t},height=${i},left=${r},top=${s}`)},onMessage:t=>{if(!e.window?.addEventListener)return()=>{};let i=r=>t({origin:r.origin,data:r.data});return e.window.addEventListener("message",i),()=>e.window?.removeEventListener("message",i)},withLock:(t,i)=>e.navigator?.locks?e.navigator.locks.request(t,i):i(),watch:t=>{let i=e.window;if(!i?.addEventListener)return()=>{};let r=n=>t.storage(n.key),s=()=>{i.document?.visibilityState!=="hidden"&&t.wake()};return i.addEventListener("storage",r),i.addEventListener("online",t.wake),i.document?.addEventListener?.("visibilitychange",s),()=>{i.removeEventListener("storage",r),i.removeEventListener("online",t.wake),i.document?.removeEventListener?.("visibilitychange",s)}},setTimer:(t,i)=>setTimeout(t,Math.min(i,2147483647)),clearTimer:t=>clearTimeout(t)}}function Lt(){let e=globalThis.location;if(!(!e?.origin||!/^https?:$/.test(e.protocol??"")))return e.origin}var It=class{constructor(e){this.signinOrigin=Et;this.current=null;this.claims=null;this.listeners=new Set;this.timer=null;this.refreshing=null;this.loaded=null;this.unwatch=null;this.limitTimer=null;this.epoch=0;this.deps=e??xt(),this.audience=Lt()}get origin(){return this.signinOrigin}configure(e){e.audience!==void 0&&(this.audience=e.audience),e.signinOrigin!==void 0&&(this.signinOrigin=e.signinOrigin.replace(/\/$/,"")),this.reset()}__setDeps(e){this.deps=e,this.reset()}reset(){this.unwatch?.(),this.unwatch=null,this.epoch+=1,this.limitTimer!==null&&this.deps.clearTimer(this.limitTimer),this.limitTimer=null,this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=null,this.current=null,this.claims=null,this.loaded=null,this.refreshing=null}get storageKey(){return Tt+(this.audience??"")}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){for(let e of[...this.listeners])try{e()}catch(t){console.warn("[Buoy] sign-in listener error:",t)}}getAccessToken(){let e=this.getStatus();return e.state==="active"&&!e.grace?this.current?.accessToken??null:null}getStatus(){let e=this.claims;if(!e)return{state:"signed_out"};let t=e.exp*1e3,i=this.deps.now();return i<(e.iat-H)*1e3?{state:"signed_out"}:i<t?{state:"active",claims:e,grace:false,validUntil:t}:i<t+P?{state:"active",claims:e,grace:true,validUntil:t+P}:{state:"signed_out"}}load(){return this.loaded||(this.loaded=this.loadOnce()),this.loaded}async loadOnce(){if(!this.audience)return;this.unwatch??(this.unwatch=this.deps.watch?.({storage:t=>{(t===null||t===this.storageKey)&&this.sync()},wake:()=>{this.claims&&this.refresh()}})??null);let e=await this.read();e&&this.adopt(e)&&(this.notify(),this.schedule())}async sync(){if(!this.audience)return;let e=await this.read();if(!e){this.current&&this.clearLocal();return}e.refreshToken!==this.current?.refreshToken&&this.adopt(e)&&(this.notify(),this.schedule())}async read(){try{let e=await(await this.deps.storage()).getItem(this.storageKey);if(!e)return null;let t=JSON.parse(e);return t?.v===1&&typeof t.accessToken=="string"&&typeof t.refreshToken=="string"?t:null}catch{return null}}async write(e){let t=await this.deps.storage();e?await t.setItem(this.storageKey,JSON.stringify(e)):await t.removeItem(this.storageKey)}adopt(e){if(!this.audience)return false;let t=B(e.accessToken,{audience:this.audience,ignoreTime:true,publicKeyHex:this.deps.publicKeyHex}),i=t.ok?t.claims:null;return i?(this.current=e,this.claims=i,true):false}armLimit(){if(this.limitTimer!==null&&this.deps.clearTimer(this.limitTimer),this.limitTimer=null,!this.claims)return;let e=this.claims.exp*1e3,t=this.deps.now(),i=t<e?e:t<e+P?e+P:null;i!==null&&(this.limitTimer=this.deps.setTimer(()=>{this.limitTimer=null,this.notify(),this.armLimit()},i-t))}call(e,t){return new Promise((i,r)=>{let s=this.deps.setTimer(()=>r(new Error("timeout")),_t);this.deps.fetch(e,t).then(n=>{this.deps.clearTimer(s),i(n)},n=>{this.deps.clearTimer(s),r(n)})})}schedule(){if(this.armLimit(),this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=null,!this.claims)return;let e=this.claims.exp*1e3-we-this.deps.now();this.timer=this.deps.setTimer(()=>{this.timer=null,this.refresh()},Math.max(0,e))}refresh(e=false){return this.refreshing||(this.refreshing=this.deps.withLock(this.lockName,()=>this.refreshLocked(e)).catch(()=>this.retryLater()).finally(()=>{this.refreshing=null})),this.refreshing}get lockName(){return`buoy-session:${this.audience??""}`}async refreshLocked(e){let t=this.epoch,i=await this.read();if(!i){this.current&&this.clearLocal();return}if(i.refreshToken!==this.current?.refreshToken&&this.adopt(i)){this.notify(),this.schedule();return}if(!this.claims)return;if(!e&&this.claims.exp*1e3-this.deps.now()>we){this.schedule();return}let r,s;try{let n=await this.call(`${this.signinOrigin}/api/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:i.refreshToken,audience:this.audience})});r=n.status,s=await n.json().catch(()=>null)}catch{this.retryLater();return}if(t===this.epoch){if(r===200&&s&&typeof s.access_token=="string"&&typeof s.refresh_token=="string"){let n={v:1,accessToken:s.access_token,refreshToken:s.refresh_token};if(this.adopt(n)){await this.write(n),this.notify(),this.schedule();return}}if(r===400||r===401||r===403){await this.write(null),this.clearLocal();return}this.retryLater()}}retryLater(){this.armLimit(),this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=this.deps.setTimer(()=>{this.timer=null,this.refresh(true)},At),this.notify()}clearLocal(){this.timer!==null&&this.deps.clearTimer(this.timer),this.timer=null,this.limitTimer!==null&&this.deps.clearTimer(this.limitTimer),this.limitTimer=null,this.current=null,this.claims=null,this.notify()}async signIn(){if(!this.audience)throw new p("not_configured","Sign-in needs a web page or a configured app id.");let e=this.deps.openPopup();if(!e)throw new p("popup_blocked","The browser blocked the sign-in window. Allow pop-ups for this site and try again.");try{return await this.signInWith((t,i)=>new Promise((r,s)=>{e.location.href=t;let n=this.deps.now(),c=null,o=u=>{c!==null&&this.deps.clearTimer(c),a(),u()},a=this.deps.onMessage(u=>{if(u.origin!==this.signinOrigin)return;let f=u.data;!f||f.type!==kt||f.state!==i||(typeof f.code=="string"?o(()=>r(f.code)):o(()=>s(new p("access_denied","Sign-in was cancelled."))))}),l=()=>{if(e.closed)return o(()=>s(new p("popup_closed","The sign-in window was closed.")));if(this.deps.now()-n>bt)return o(()=>s(new p("timeout","Sign-in took too long.")));c=this.deps.setTimer(l,500)};c=this.deps.setTimer(l,500)}))}finally{try{e.closed||e.close()}catch{}}}async signInWith(e){let t=this.audience;if(!t)throw new p("not_configured","Sign-in needs a web page or a configured app id.");let i=j(this.deps.random(32)),r=j(this.deps.random(16)),s=j(await this.deps.sha256(new TextEncoder().encode(i))),n=`${this.signinOrigin}/auth/authorize?audience=${encodeURIComponent(t)}&code_challenge=${s}&code_challenge_method=S256&state=${r}`,c=await e(n,r),o;try{o=await this.call(`${this.signinOrigin}/api/auth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:c,code_verifier:i,audience:t})})}catch{throw new p("network","Buoy could not be reached. Check your connection and try again.")}let a=await o.json().catch(()=>null);if(o.status!==200||!a||typeof a.access_token!="string"||typeof a.refresh_token!="string"){let u=a&&typeof a.error_description=="string"?a.error_description:"Buoy did not accept this sign-in.";throw new p(o.status===503?"unavailable":"rejected",u)}let l={v:1,accessToken:a.access_token,refreshToken:a.refresh_token};if(this.epoch+=1,!this.adopt(l))throw new p("rejected","Buoy sent a sign-in this app could not check.");return await this.write(l),await this.load(),this.notify(),this.schedule(),this.getStatus()}async signOut(){this.epoch+=1;let e=this.current;this.clearLocal();let t=e;try{await this.deps.withLock(this.lockName,async()=>{t=await this.read()??e,await this.write(null)})}catch{await this.write(null).catch(()=>{})}if(t)try{await this.call(`${this.signinOrigin}/api/auth/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:t.refreshToken})})}catch{}}},E=new It;var Rt="bsk1_",Nt="service",Pt=15e3;function z(e){return e.startsWith(Rt)}function b(e){let t=e.trim();return z(t)||ge(t)?t:t.toUpperCase()}function Ct(e,t){return b(e??"")===b(t??"")}async function St(e,t){let i=t.fetch??((a,l)=>fetch(a,l)),r=typeof AbortController<"u"?new AbortController:void 0,s=setTimeout(()=>r?.abort(),Pt),n,c;try{let a=await i(`${t.origin.replace(/\/$/,"")}/api/auth/service-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:e}),signal:r?.signal});n=a.status,c=await a.json().catch(()=>null)}catch{return{ok:false,reason:"network",detail:"Buoy could not be reached to check the bot key."}}finally{clearTimeout(s)}if(n>=500||n===429)return{ok:false,reason:"network",detail:"Buoy is busy. The bot key will be checked again soon."};if(n!==200||typeof c?.access_token!="string")return{ok:false,reason:"rejected",detail:typeof c?.error_description=="string"?c.error_description:"This bot key was not accepted."};let o=B(c.access_token,{audience:Nt,nowMs:h(),publicKeyHex:t.publicKeyHex});return o.ok?{ok:true,claims:o.claims}:{ok:false,reason:"rejected",detail:"Buoy sent a token this app could not check."}}var Kt="dev";function ve(e){return ge(e)}function Te(e,t=h()){let i=B(e,{audience:Kt,ignoreTime:true});return i.ok?t>=i.claims.exp*1e3?{ok:false,reason:"expired",message:"Your dev token ran out. Run `npx buoy login` again."}:{ok:true,claims:i.claims}:{ok:false,reason:"invalid",message:"This dev token is not valid. Run `npx buoy login` again."}}var Ot="This is a dev token. It only works in dev builds. A live build turns on Buoy with Sign in with Buoy.";async function w(e,t){let i;try{return await Promise.race([e,new Promise(r=>{i=setTimeout(()=>r(t),15e3)})])}finally{i&&clearTimeout(i)}}function k(e){return e.tier==="pro"||e.tier==="free"?e.tier:e.isPro?"pro":"free"}var Mt=class{constructor(){this.state={isInitialized:false,isValidating:false,isPro:false,tier:"free",tierSource:"none",licenseKey:null,error:null,errorKind:null,expiresAt:null};this.listeners=new Set;this.revalidationTimer=null;this.cachedLicense=null;this.fingerprintPromise=null;this.initPromise=null;this.generation=0;this.persistence=Promise.resolve();this.verifiedAt=null;this.expiryTimer=null;this.removeForegroundListener=null;this.sessionUnsubscribe=null;this.retryTimer=null;this.retryAttempt=0;this.deviceRegistrationCallback=null;this.pendingRegistration=null}async initialize(){if(this.initPromise)return this.initPromise;if(!this.state.isInitialized)return this.initPromise=this._doInitialize(),this.initPromise}async _doInitialize(){if(this.state.isInitialized)return;let e=this.generation;this.sessionUnsubscribe||(this.sessionUnsubscribe=E.subscribe(()=>this.emit({type:"validated",isPro:this.isPro()}))),await w(E.load().catch(()=>{}),void 0);try{let t=await w(this.loadCachedLicense(),null);if(e!==this.generation){this.state.isInitialized=true,this.emit({type:"initialized",isPro:this.state.isPro}),this.startRevalidationTimer();return}if(t&&(this.configuredKey===void 0||t.licenseKey===this.configuredKey)){let i=A(t);this.cachedLicense=t,this.state.licenseKey=t.licenseKey,i&&this.isCacheValid(t)?this.applyTier(k(t)):this.lock(),this.state.expiresAt=t.expiresAt?new Date(t.expiresAt):null,i&&this.isCacheValid(t)&&this.setVerifiedAt(t.cachedAt),this.configuredKey===void 0&&t.licenseKey&&!(i&&this.isCacheValid(t)&&this.isCacheFresh(t))&&this.validateInBackground(t.licenseKey)}this.state.isInitialized=true,this.emit({type:"initialized",isPro:this.state.isPro}),this.startRevalidationTimer()}catch(t){console.warn("[React Buoy] License initialization failed:",t),this.state.isInitialized=true,this.emit({type:"initialized",isPro:false})}}async configure(e){if(e===void 0)return await this.initialize(),this.hasAccountAccess();let t=b(e),i=++this.generation;return this.cancelRetry(),this.configuredKey=t||null,this.state.licenseKey!==this.configuredKey&&this.lock(),this.state.licenseKey=this.configuredKey,this.state.isValidating=!!t,this.emit({type:"validating",isPro:this.isPro()}),await this.initialize(),i!==this.generation?false:t?this.setLicenseKey(t):(await this.clearLicense(),false)}async setLicenseKey(e,t){if(!e||e.trim().length===0)return this.state.error="License key is required",this.state.errorKind="verdict",false;let i=b(e),r=++this.generation;if(this.cancelRetry(),t?.isRetry||(this.retryAttempt=0),this.state.licenseKey!==i&&this.lock(),this.state.licenseKey=i,this.state.isValidating=true,this.state.error=null,this.state.errorKind=null,this.emit({type:"validating",isPro:this.isPro()}),he(i))return this.admitToken(i,r);if(z(i))return this.admitServiceKey(i,r);if(ve(i))return this.admitDevToken(i);if(!this.cachedLicense){let a=await w(this.loadCachedLicense(),null);if(r!==this.generation)return false;a?.licenseKey===i&&(this.cachedLicense=a,A(a)&&this.isCacheValid(a)&&(this.applyTier(k(a)),this.state.expiresAt=a.expiresAt?new Date(a.expiresAt):null,this.setVerifiedAt(a.cachedAt),this.emit({type:"validating",isPro:this.isPro()})))}let s=this.cachedLicense;if(!t?.force&&s&&s.valid&&s.licenseKey===i&&s.fingerprint&&A(s)&&this.isCacheValid(s)&&this.isCacheFresh(s))return this.applyTier(k(s)),this.state.licenseKey=i,this.state.isValidating=false,this.state.error=null,this.state.errorKind=null,this.state.expiresAt=s.expiresAt?new Date(s.expiresAt):null,this.setVerifiedAt(s.cachedAt),this.emit({type:"validated",isPro:this.state.isPro}),true;let n=await w(this.loadHold(),null);if(r!==this.generation)return false;let c=(n?.backoffUntil??0)-h();if(c>0)return this.standDown(i,r,"License server is rate limited; retrying later.",c);if(!t?.force&&n?.key===i&&n.verdictAt!==void 0&&h()-n.verdictAt>=0&&h()-n.verdictAt<Ne)return this.lock(),this.state.isValidating=false,this.state.error=n.detail||"License could not be verified",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),false;let o;try{o=await De(i)}catch(a){let l=a instanceof Error?a.message:"Validation failed";if(r!==this.generation)return false;if(Q(a)){if(Se(a)){let u=Math.min(Ke(a,q,Re)*2**this.retryAttempt,q);return this.retryAttempt+=1,await this.saveHold({...this.hold,backoffUntil:h()+u}),r!==this.generation?false:this.standDown(i,r,l,u)}return this.state.isValidating=false,this.cachedLicense&&this.cachedLicense.licenseKey===i&&A(this.cachedLicense)&&this.isCacheValid(this.cachedLicense)?(this.applyTier(k(this.cachedLicense)),this.state.error=null,this.state.errorKind=null,this.state.expiresAt=this.cachedLicense.expiresAt?new Date(this.cachedLicense.expiresAt):null,this.setVerifiedAt(this.cachedLicense.cachedAt),this.keyTier()!=="pro"&&this.scheduleRetry(i),this.emit({type:"validated",isPro:this.state.isPro}),true):(this.state.error=l,this.state.errorKind="transport",this.scheduleRetry(i),this.emit({type:"error",isPro:this.state.isPro,error:l}),false)}return this.state.isValidating=false,this.state.error=l,this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:l}),false}try{if(r!==this.generation)return false;let a=$e(o),l=o.data?.relationships?.product?.data?.id;if((!l||l===I.productId)&&a.code==="EXPIRED"&&(lt(o.data)||this.cachedLicense?.licenseKey===i&&A(this.cachedLicense)&&this.isCacheValid(this.cachedLicense))){let O=h(),W=await this.saveCache({licenseKey:i,licenseId:o.data?.id??"",valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:O,lastValidatedAt:O,fingerprint:this.cachedLicense?.licenseKey===i?this.cachedLicense.fingerprint:void 0},r);return r!==this.generation||!W?false:(this.cachedLicense=W,this.applyTier("free"),this.state.isValidating=false,this.state.expiresAt=null,this.setVerifiedAt(O),this.emit({type:"validated",isPro:false}),true)}if(He(a.code)||!Ue(o))return this.lock(),this.state.isValidating=false,this.state.error=a.detail||"License could not be verified",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),await this.clearCachedLicense(),await this.saveHold({key:i,code:a.code,detail:this.state.error,verdictAt:h()}),false;if(!o.data)return this.lock(),this.state.isValidating=false,this.state.error=a.detail||"License not found",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),await this.clearCachedLicense(),false;let u=ot(o.data),f=o.data.id,C=h(),G=Fe(o)>=Pe,Y=F(),Ae=Y&&!G,_e=u==="free"&&(G||!Y),v=this.cachedLicense?.fingerprint,x=this.cachedLicense?.deviceName,J=false;if(!v){if(v=await w(this.getOrCreateDeviceFingerprint(),U()),r!==this.generation)return false;x=$(),J=Ae}let S=false;J&&(this.deviceRegistrationCallback?(this.pendingRegistration={licenseId:f,licenseKey:i,fingerprint:v},this.deviceRegistrationCallback({fingerprint:v,defaultName:x}),S=true):this.registerDeviceAsync(f,i,v,x));let K={licenseKey:i,licenseId:f,valid:true,isPro:u==="pro",tier:u,expiresAt:o.data.attributes.expiry??null,cachedAt:C,lastValidatedAt:C,fingerprint:S?void 0:v,deviceName:S?void 0:x,quiet:_e||void 0},X=await this.saveCache(K,r);return r!==this.generation||!X||(this.cachedLicense=X,this.applyTier(u),this.state.isValidating=false,this.state.expiresAt=K.expiresAt?new Date(K.expiresAt):null,this.setVerifiedAt(C),this.hold&&await this.saveHold(null),r!==this.generation)?false:(this.retryAttempt=0,this.emit({type:"validated",isPro:this.state.isPro}),true)}catch(a){let l=a instanceof Error?a.message:"Validation failed";return r!==this.generation||(this.state.isValidating=false,this.state.error=l,this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:l})),false}}scheduleRetry(e){this.cancelRetry();let t=Ie[this.retryAttempt];if(t===void 0)return;this.retryAttempt+=1;let i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null,i===this.generation&&(this.state.isValidating||this.setLicenseKey(e,{force:true,isRetry:true}))},t),typeof d<"u"&&d&&(globalThis.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer)}async admitServiceKey(e,t){let i=await St(e,{origin:E.origin});if(t!==this.generation)return false;if(!i.ok)return this.lock(),this.state.isValidating=false,this.state.error=i.detail,this.state.errorKind=i.reason==="network"?"transport":"verdict",this.emit({type:"error",isPro:false,error:i.detail}),i.reason==="network"&&this.scheduleRetry(e),false;let r=i.claims.exp*1e3;return this.applyTier(i.claims.tier==="free"?"free":"pro"),this.state.isValidating=false,this.state.expiresAt=new Date(r),this.setVerifiedAt(h()),this.retryAttempt=0,this.scheduleRetryIn(e,Math.max(6e4,r-3600*1e3-h())),this.emit({type:"validated",isPro:this.isPro()}),true}admitDevToken(e){let t=Te(e);if(!t.ok||!F()){let r=t.ok?Ot:t.message;return this.lock(),this.state.isValidating=false,this.state.error=r,this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:r}),false}let i=h();return this.applyTier(t.claims.tier==="free"?"free":"pro"),this.state.isValidating=false,this.state.expiresAt=new Date(t.claims.exp*1e3),this.setVerifiedAt(Math.min(i,t.claims.iat*1e3)),this.retryAttempt=0,this.emit({type:"validated",isPro:this.isPro()}),true}async admitToken(e,t){let i=at(e);if(t!==this.generation)return false;if(!i)return this.lock(),this.state.isValidating=false,this.state.error="This key is not valid. Check that it was copied completely.",this.state.errorKind="verdict",this.emit({type:"error",isPro:false,error:this.state.error}),await this.clearCachedLicense(),false;let r=h(),s=this.cachedLicense?.fingerprint??await w(this.getOrCreateDeviceFingerprint(),U());if(t!==this.generation)return false;let n=await this.saveCache({licenseKey:e,licenseId:`token:${i.accountHash}`,valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:r,lastValidatedAt:r,fingerprint:s,deviceName:this.cachedLicense?.deviceName??$(),quiet:true},t);return t!==this.generation||!n?false:(this.cachedLicense=n,this.applyTier("free"),this.state.isValidating=false,this.state.expiresAt=null,this.setVerifiedAt(r),this.retryAttempt=0,this.emit({type:"validated",isPro:false}),true)}standDown(e,t,i,r){if(t!==this.generation)return false;this.state.isValidating=false;let s=this.cachedLicense;return s&&s.licenseKey===e&&A(s)&&this.isCacheValid(s)?(this.applyTier(k(s)),this.state.error=null,this.state.errorKind=null,this.state.expiresAt=s.expiresAt?new Date(s.expiresAt):null,this.setVerifiedAt(s.cachedAt),this.keyTier()!=="pro"&&this.scheduleRetryIn(e,r),this.emit({type:"validated",isPro:this.state.isPro}),true):(this.state.error=i,this.state.errorKind="transport",this.scheduleRetryIn(e,r),this.emit({type:"error",isPro:this.state.isPro,error:i}),false)}scheduleRetryIn(e,t){this.cancelRetry();let i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null,i===this.generation&&(this.state.isValidating||this.setLicenseKey(e,{force:true,isRetry:true}))},Math.max(1,Math.min(t,2147483647))),typeof d<"u"&&d&&(m.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer)}cancelRetry(){this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=null)}async clearLicense(){this.generation+=1,this.cancelRetry(),this.retryAttempt=0,this.configuredKey=null,this.state.licenseKey=null,this.lock(),this.state.expiresAt=null,this.state.error=null,this.state.errorKind=null,this.cachedLicense=null,this.pendingRegistration=null,this.state.isValidating=false,this.emit({type:"deactivated",isPro:false}),await this.clearCachedLicense(),this.hold?.verdictAt!==void 0&&await this.saveHold(this.hold.backoffUntil?{backoffUntil:this.hold.backoffUntil}:null)}applyTier(e){this.state.tier=e,this.state.tierSource="key",this.state.isPro=e==="pro"}lock(){this.state.tier="free",this.state.tierSource="none",this.state.isPro=false,this.verifiedAt=null,this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=null}getTier(){return this.credential().tier}credential(){let e=this.keyTier(),t=this.sessionTier();return t!==null&&(!this.hasKeyAccess()||t==="pro"&&e!=="pro")?{source:"session",tier:t}:{source:this.state.tierSource,tier:e}}keyTier(){return!this.hasKeyAccess()||this.state.expiresAt&&h()>=this.state.expiresAt.getTime()?"free":this.state.tier}sessionTier(){let e=E.getStatus();return e.state!=="active"?null:e.claims.tier==="free"?"free":"pro"}isPro(){return this.getTier()==="pro"}hasEntitlement(e){return this.isPro()}getState(){let{source:e,tier:t}=this.credential();return{...this.state,tier:t,tierSource:e,isPro:t==="pro",access:this.getAccountAccess()}}getAccountAccess(){let e=this.getKeyAccess();if(e.status==="admitted")return e;let t=E.getStatus();return t.state==="active"?{status:"admitted",reason:null,validUntil:t.validUntil}:e}getKeyAccess(){return de({initialized:this.state.isInitialized,validating:this.state.isValidating,key:this.state.licenseKey,tierSource:this.state.tierSource,verifiedAt:this.verifiedAt})}hasAccountAccess(){return this.getAccountAccess().status==="admitted"}hasKeyAccess(){return this.getKeyAccess().status==="admitted"}setVerifiedAt(e){this.verifiedAt=e,this.expiryTimer&&clearTimeout(this.expiryTimer);let t=e+N,i=this.state.expiresAt?.getTime(),r=(i&&i>h()?Math.min(t,i):t)-h();this.expiryTimer=setTimeout(()=>{this.expiryTimer=null,this.verifiedAt===e&&(this.emit({type:"validated",isPro:this.isPro()}),h()<t&&this.setVerifiedAt(e))},Math.max(1,Math.min(r,2147483647))),typeof d<"u"&&d&&(m.__LICENSE_MANAGER_EXPIRY_TIMER__=this.expiryTimer)}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}onDeviceRegistrationNeeded(e){return this.deviceRegistrationCallback=e,()=>{this.deviceRegistrationCallback=null}}async completeDeviceRegistration(e){if(!this.pendingRegistration){console.warn("[Buoy] No pending device registration");return}let{licenseId:t,licenseKey:i,fingerprint:r}=this.pendingRegistration,s=this.generation,n=e.trim()||$(),c=se();try{await te(t,i,r,n,c)}catch(o){let a=o instanceof Error?o.message:String(o);if(!(a==="FINGERPRINT_TAKEN"||a==="MACHINE_LIMIT_EXCEEDED"))throw console.warn("[Buoy] Device registration failed:",a),o}if(s===this.generation){if(this.cachedLicense?.licenseKey===i){let o=await this.saveCache({...this.cachedLicense,fingerprint:r,deviceName:n},s);if(s!==this.generation)return;o&&(this.cachedLicense=o)}this.pendingRegistration=null}}emit(e){[...this.listeners].forEach(t=>{try{t(e)}catch(i){console.warn("[React Buoy] License listener error:",i)}})}isCacheValid(e){let t=h()-e.cachedAt;return!(!e.valid||!Number.isFinite(e.cachedAt)||t<0||t>=N)}isCacheFresh(e){let t=this.isQuiet(e)?Ce:Le;return h()-(e.lastValidatedAt??0)<t}isQuiet(e){return e.quiet!==void 0?e.quiet:k(e)==="free"&&!F()}async loadHold(){if(this.hold!==void 0)return this.hold;try{let e=await(await g()).getItem(D),t=e?JSON.parse(e):null;this.hold=t&&typeof t=="object"?t:null}catch{this.hold=null}return this.hold}async saveHold(e){this.hold=e;try{let t=await g();e?await t.setItem(D,JSON.stringify(e)):await t.removeItem(D)}catch{}}async persistCache(e){let t=le(e);return await(await g()).setItem(M,JSON.stringify(t)),t}saveCache(e,t){let i=this.persistence.catch(()=>{}).then(async()=>{if(t!==this.generation)return null;let r=await this.persistCache(e);return t===this.generation?r:null});return this.persistence=i,w(i,le(e))}async loadCachedLicense(){try{await this.persistence.catch(()=>{});let e=await(await g()).getItem(M);return e?JSON.parse(e):null}catch(e){return console.warn("[Buoy] Error loading cached license:",e),null}}getOrCreateDeviceFingerprint(){return this.fingerprintPromise||(this.fingerprintPromise=(async()=>{let e=await g(),t=await e.getItem(V);if(t)return t;let i=this.cachedLicense?.fingerprint||U();return await e.setItem(V,i),i})()),this.fingerprintPromise}async clearCachedLicense(){this.cachedLicense=null;let e=this.persistence.catch(()=>{}).then(async()=>{await(await g()).removeItem(M)});this.persistence=e,await w(e.catch(()=>{}),void 0)}registerDeviceAsync(e,t,i,r){let s=se();te(e,t,i,r,s).catch(n=>{n.message!=="FINGERPRINT_TAKEN"&&n.message!=="MACHINE_LIMIT_EXCEEDED"&&console.warn("[Buoy] Device registration failed (non-blocking):",n.message)})}validateInBackground(e){this.setLicenseKey(e,{force:true,isRetry:true})}refreshAccess(){this.emit({type:"validated",isPro:this.isPro()})}startRevalidationTimer(){this.removeForegroundListener||(this.removeForegroundListener=ct(()=>this.refreshAccess()),typeof d<"u"&&d&&(m.__LICENSE_MANAGER_FOREGROUND_OFF__=this.removeForegroundListener)),this.revalidationTimer&&clearInterval(this.revalidationTimer),this.revalidationTimer=setInterval(()=>{if(!this.state.licenseKey||this.state.isValidating)return;let e=this.cachedLicense;e&&e.licenseKey===this.state.licenseKey&&this.isCacheFresh(e)||this.validateInBackground(this.state.licenseKey)},xe),typeof d<"u"&&d&&(globalThis.__LICENSE_MANAGER_TIMER__=this.revalidationTimer)}stopRevalidationTimer(){this.removeForegroundListener?.(),this.removeForegroundListener=null,this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=null,this.revalidationTimer&&(clearInterval(this.revalidationTimer),this.revalidationTimer=null),this.cancelRetry()}async forceReregister(){this.cachedLicense&&(this.cachedLicense.fingerprint=void 0,this.cachedLicense.deviceName=void 0),this.fingerprintPromise=null,await this.clearCachedLicense();try{await(await g()).removeItem(V)}catch{}}},Vt=new Mt,m=globalThis;typeof d<"u"&&d&&(m.__LICENSE_MANAGER_FOREGROUND_OFF__?.(),m.__LICENSE_MANAGER_FOREGROUND_OFF__=void 0,m.__LICENSE_MANAGER_EXPIRY_TIMER__&&clearTimeout(m.__LICENSE_MANAGER_EXPIRY_TIMER__),m.__LICENSE_MANAGER_TIMER__&&clearInterval(m.__LICENSE_MANAGER_TIMER__),m.__LICENSE_MANAGER_RETRY_TIMER__&&(clearTimeout(m.__LICENSE_MANAGER_RETRY_TIMER__),m.__LICENSE_MANAGER_RETRY_TIMER__=void 0));export{h as a,T as b,Q as c,he as d,de as e,p as f,E as g,z as h,b as i,Ct as j,ve as k,Te as l,Vt as m};/*!
|
|
2
|
-
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
5
|
-
* (https://buoy.gg/terms), not an OSS license.
|
|
6
|
-
*
|
|
7
|
-
* This file configures the technological measure that controls access to paid
|
|
8
|
-
* Buoy Pro features. Modifying, patching, aliasing, stubbing, or otherwise
|
|
9
|
-
* altering it — including repointing `apiUrl` at a stand-in license server — so
|
|
10
|
-
* that Pro features unlock without a valid license is a breach of the EULA and,
|
|
11
|
-
* in the United States, circumvention of an access control under 17 U.S.C.
|
|
12
|
-
* § 1201, which applies whether or not any code is copied.
|
|
13
|
-
*
|
|
14
|
-
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
15
|
-
* including a free one (`npx buoy login`). Buy a key at
|
|
16
|
-
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
17
|
-
*//*!
|
|
18
|
-
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
19
|
-
*
|
|
20
|
-
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
21
|
-
* (https://buoy.gg/terms), not an OSS license.
|
|
22
|
-
*
|
|
23
|
-
* This file is part of the technological measure that controls access to paid
|
|
24
|
-
* Buoy Pro features. Modifying, patching, aliasing, stubbing, or otherwise
|
|
25
|
-
* altering it so that Pro features unlock without a valid license is a breach
|
|
26
|
-
* of the EULA and, in the United States, circumvention of an access control
|
|
27
|
-
* under 17 U.S.C. § 1201 — which applies whether or not any code is copied.
|
|
28
|
-
*
|
|
29
|
-
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
30
|
-
* including a free one (`npx buoy login`). Buy a key at
|
|
31
|
-
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
32
|
-
*//*!
|
|
33
|
-
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
34
|
-
*
|
|
35
|
-
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
36
|
-
* (https://buoy.gg/terms), not an OSS license.
|
|
37
|
-
*//*!
|
|
38
|
-
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
39
|
-
*
|
|
40
|
-
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
41
|
-
* (https://buoy.gg/terms), not an OSS license.
|
|
42
|
-
*
|
|
43
|
-
* This file is part of the technological measure that controls access to paid
|
|
44
|
-
* Buoy Pro features: it decides which tier a validated key grants. Modifying,
|
|
45
|
-
* patching, aliasing, stubbing, or otherwise altering it so that Pro features
|
|
46
|
-
* unlock without a valid license is a breach of the EULA and, in the United
|
|
47
|
-
* States, circumvention of an access control under 17 U.S.C. § 1201 — which
|
|
48
|
-
* applies whether or not any code is copied.
|
|
49
|
-
*
|
|
50
|
-
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
51
|
-
* including a free one (`npx buoy login`). Buy a key at
|
|
52
|
-
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
53
|
-
*//*!
|
|
54
|
-
* PROPRIETARY — Buoy license enforcement. NOT open source.
|
|
55
|
-
*
|
|
56
|
-
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
57
|
-
* (https://buoy.gg/terms), not an OSS license.
|
|
58
|
-
*//*!
|
|
59
|
-
* PROPRIETARY — Buoy Pro license enforcement. NOT open source.
|
|
60
|
-
*
|
|
61
|
-
* Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
|
|
62
|
-
* (https://buoy.gg/terms), not an OSS license.
|
|
63
|
-
*
|
|
64
|
-
* This file is the technological measure that controls access to paid Buoy Pro
|
|
65
|
-
* features. Modifying, patching, aliasing, stubbing, or otherwise altering it
|
|
66
|
-
* so that Pro features unlock without a valid license is a breach of the EULA
|
|
67
|
-
* and, in the United States, circumvention of an access control under
|
|
68
|
-
* 17 U.S.C. § 1201 — which applies whether or not any code is copied.
|
|
69
|
-
*
|
|
70
|
-
* Pro is $9/mo, and is free every weekend for anyone holding a key —
|
|
71
|
-
* including a free one (`npx buoy login`). Buy a key at
|
|
72
|
-
* https://buoy.gg or use it free on Saturday and Sunday.
|
|
73
|
-
*/
|