@bodhiapp/bodhi-js-core 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const z=require("ua-parser-js");function w(t){return t!==null&&typeof t=="object"}function X(t){return w(t)&&"error"in t&&w(t.error)&&"message"in t.error&&typeof t.error.message=="string"&&"type"in t.error&&typeof t.error.type=="string"}function K(t){return w(t)&&"message"in t&&typeof t.message=="string"&&"type"in t&&typeof t.type=="string"}function Z(t){return t instanceof Error&&"error"in t&&!("response"in t)&&K(t.error)}function R(t){return t!==null&&typeof t=="object"&&"error"in t&&!("body"in t)&&K(t.error)}function O(t){return"body"in t&&"status"in t&&typeof t.status=="number"&&t.status>=200&&t.status<300}function J(t){return"body"in t&&"status"in t&&typeof t.status=="number"&&t.status>=400&&X(t.body)}const ee=(t,e,n,r)=>{const a=new Error(t);return a.response={status:e,body:n,headers:r},a},g=(t,e)=>{const n=new Error(t);return n.error={message:t,type:e},n},h={NOT_REACHABLE:{message:"server is not reachable on given url",type:"network_error"},SERVER_NOT_READY:{message:"server is not in ready state, configure to complete setup",type:"extension_error"}},y={status:"not-reachable",error:h.NOT_REACHABLE},k={status:"pending-extension-ready"},E={status:"not-connected"};function C(t){return t.status==="ready"}function L(t,e="unknown",n=h.SERVER_NOT_READY){return{status:t,version:e,error:n}}function b(t){return t.type==="extension"}function N(t){return t.type==="direct"}function te(t){return typeof t.server=="object"&&t.server.status!=="not-connected"&&C(t.server)}function P(t){return"url"in t&&!!t.url}const v={type:"direct",server:E};function ne(t,e="unknown"){return{type:"direct",server:{status:"ready",version:e},url:t}}function re(t){return{type:"direct",server:y,url:t}}function ae(t,e){return{type:"direct",server:e,url:t}}const M={type:"extension",extension:"not-initialized",server:k},D={type:"extension",extension:"not-found",server:k};function ie(t){return t.extension==="ready"&&t.server.status!=="pending-extension-ready"&&C(t.server)}function U(t){return t.extension==="ready"&&"extensionId"in t}function se(){return M}function oe(){return D}function le(t){return b(t)?U(t):P(t)}function ce(t){return t.server}function ue(t){return b(t)&&t.extension==="ready"?t.extensionId:void 0}function de(t){return N(t)&&"url"in t?t.url:void 0}const fe={isLoggedIn:!1,error:{message:"Client not initialized",code:"CLIENT_NOT_INITIALIZED"}};function pe(t){return typeof t=="object"&&t!==null&&"isLoggedIn"in t&&t.isLoggedIn===!1&&"error"in t&&typeof t.error=="object"&&t.error!==null&&"message"in t.error&&typeof t.error.message=="string"&&"code"in t.error&&typeof t.error.code=="string"}function he(t){return typeof t=="object"&&t!==null&&"isLoggedIn"in t&&t.isLoggedIn===!1&&!("error"in t)}function me(t){return typeof t=="object"&&t!==null&&"isLoggedIn"in t&&t.isLoggedIn===!0&&"userInfo"in t&&typeof t.userInfo=="object"&&t.userInfo!==null&&"sub"in t.userInfo&&typeof t.userInfo.sub=="string"&&"accessToken"in t&&typeof t.accessToken=="string"}const _=()=>{};function F(t,e){return{kind:"event",type:t,payload:e}}function j(t,e,n){return{kind:"response",type:e,requestId:t,payload:n}}function B(t,e){return{kind:"error",requestId:t,error:e}}function ge(t,e){const n=e[t.type];if(!n)return null;const r=n(t.payload);return j(t.requestId,t.type,r)}const T={debug:0,info:1,warn:2,error:3,silent:4};class V{constructor(e,n="warn"){this.prefix=e,this.level=n}shouldLog(e){return T[e]>=T[this.level]}ts(){const e=new Date,n=e.getHours().toString().padStart(2,"0"),r=e.getMinutes().toString().padStart(2,"0"),a=e.getSeconds().toString().padStart(2,"0"),i=e.getMilliseconds().toString().padStart(3,"0");return`${n}:${r}:${a}.${i}`}debug(...e){this.shouldLog("debug")&&console.log(`[${this.prefix}] ${this.ts()}`,...e)}info(...e){this.shouldLog("info")&&console.log(`[${this.prefix}] ${this.ts()}`,...e)}warn(...e){this.shouldLog("warn")&&console.warn(`[${this.prefix}] ${this.ts()}`,...e)}error(...e){this.shouldLog("error")&&console.error(`[${this.prefix}] ${this.ts()}`,...e)}}function ve(t){return"handleOAuthCallback"in t}function ye(){var a;const e=new z.UAParser().getBrowser(),n=((a=e.name)==null?void 0:a.toLowerCase())||"",r=e.name||"Unknown Browser";if(n.includes("chrome"))return{name:"Google Chrome",type:"chrome"};if(n.includes("edge"))return{name:"Microsoft Edge",type:"edge"};if(n.includes("firefox"))return{name:"Mozilla Firefox",type:"firefox"};if(n.includes("safari"))return{name:"Safari",type:"safari"};{let i=r.replace(/\s+Browser$/i,"").replace(/\s+browser$/i,"").trim();return(!i||i.toLowerCase()==="unknown")&&(i="Unknown Browser"),{name:i,type:"unknown"}}}function be(){var r;const n=((r=new z.UAParser().getOS().name)==null?void 0:r.toLowerCase())||"";return n.includes("mac")?{name:"macOS",type:"macos"}:n.includes("windows")?{name:"Windows",type:"windows"}:n.includes("linux")||n.includes("ubuntu")||n.includes("fedora")||n.includes("debian")?{name:"Linux",type:"linux"}:{name:"Unknown",type:"unknown"}}function Q(t){return{ACCESS_TOKEN:`${t}:access_token`,REFRESH_TOKEN:`${t}:refresh_token`,EXPIRES_AT:`${t}:expires_at`,CODE_VERIFIER:`${t}:code_verifier`,STATE:`${t}:state`,RESOURCE_SCOPE:`${t}:resource_scope`}}const xe={DIRECT:"bodhi:direct",WEB:"bodhi:web",EXT:"bodhi:ext"};class H{constructor(e="bodhijs:"){this.storagePrefix=e,this.STORAGE_KEYS={DIRECT_STATUS:`${e}connection:directStatus`,SERIALIZED_CLIENT_STATE:`${e}client:serializedState`},this.SERVER_INSTALL_KEY=`${e}server:installed`}getDirectStatus(){return this.getStorageValue(this.STORAGE_KEYS.DIRECT_STATUS,null)}setDirectStatus(e){this.setStorageValue(this.STORAGE_KEYS.DIRECT_STATUS,e)}clear(){try{localStorage.removeItem(this.STORAGE_KEYS.DIRECT_STATUS),localStorage.removeItem(this.STORAGE_KEYS.SERIALIZED_CLIENT_STATE),localStorage.removeItem(this.SERVER_INSTALL_KEY)}catch(e){console.warn("[BodhiClientUserPrefsManager] Failed to clear preferences:",e)}}isServerInstallConfirmed(){return this.getStorageValue(this.SERVER_INSTALL_KEY,!1)}setServerInstallConfirmed(e){this.setStorageValue(this.SERVER_INSTALL_KEY,e)}getSerializedClientState(){return this.getStorageValue(this.STORAGE_KEYS.SERIALIZED_CLIENT_STATE,null)}setSerializedClientState(e){this.setStorageValue(this.STORAGE_KEYS.SERIALIZED_CLIENT_STATE,e)}getStorageValue(e,n){try{const r=localStorage.getItem(e);return r===null?n:JSON.parse(r)}catch(r){return console.warn(`[BodhiClientUserPrefsManager] Failed to read ${e}:`,r),n}}setStorageValue(e,n){try{localStorage.setItem(e,JSON.stringify(n))}catch(r){console.warn(`[BodhiClientUserPrefsManager] Failed to write ${e}:`,r)}}}function we(t){return t.kind==="request"}const Se={MODAL_READY:"modal:ready",MODAL_REFRESH:"modal:refresh",MODAL_CLOSE:"modal:close",MODAL_COMPLETE:"modal:complete",MODAL_LNA_CONNECT:"modal:lna:connect",MODAL_LNA_SKIP:"modal:lna:skip",MODAL_CONFIRM_SERVER_INSTALL:"modal:confirm-server-install",MODAL_SELECT_CONNECTION:"modal:select-connection",PARENT_STATE_UPDATE:"parent:state-update"},ke=`<!doctype html>
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const z=require("ua-parser-js");function w(t){return t!==null&&typeof t=="object"}function Z(t){return w(t)&&"error"in t&&w(t.error)&&"message"in t.error&&typeof t.error.message=="string"&&"type"in t.error&&typeof t.error.type=="string"}function K(t){return w(t)&&"message"in t&&typeof t.message=="string"&&"type"in t&&typeof t.type=="string"}function J(t){return t instanceof Error&&"error"in t&&!("response"in t)&&K(t.error)}function R(t){return t!==null&&typeof t=="object"&&"error"in t&&!("body"in t)&&K(t.error)}function O(t){return"body"in t&&"status"in t&&typeof t.status=="number"&&t.status>=200&&t.status<300}function ee(t){return"body"in t&&"status"in t&&typeof t.status=="number"&&t.status>=400&&Z(t.body)}const te=(t,e,n,r)=>{const a=new Error(t);return a.response={status:e,body:n,headers:r},a},g=(t,e)=>{const n=new Error(t);return n.error={message:t,type:e},n},h={NOT_REACHABLE:{message:"server is not reachable on given url",type:"network_error"},SERVER_NOT_READY:{message:"server is not in ready state, configure to complete setup",type:"extension_error"}},y={status:"not-reachable",error:h.NOT_REACHABLE},k={status:"pending-extension-ready"},E={status:"not-connected"};function C(t){return t.status==="ready"}function L(t,e="unknown",n=h.SERVER_NOT_READY){return{status:t,version:e,error:n}}function b(t){return t.type==="extension"}function N(t){return t.type==="direct"}function ne(t){return typeof t.server=="object"&&t.server.status!=="not-connected"&&C(t.server)}function P(t){return"url"in t&&!!t.url}const v={type:"direct",server:E};function re(t,e="unknown"){return{type:"direct",server:{status:"ready",version:e},url:t}}function ae(t){return{type:"direct",server:y,url:t}}function ie(t,e){return{type:"direct",server:e,url:t}}const M={type:"extension",extension:"not-initialized",server:k},D={type:"extension",extension:"not-found",server:k};function se(t){return t.extension==="ready"&&t.server.status!=="pending-extension-ready"&&C(t.server)}function U(t){return t.extension==="ready"&&"extensionId"in t}function oe(){return M}function le(){return D}function ce(t){return b(t)?U(t):P(t)}function ue(t){return t.server}function de(t){return b(t)&&t.extension==="ready"?t.extensionId:void 0}function fe(t){return N(t)&&"url"in t?t.url:void 0}const pe={isLoggedIn:!1,error:{message:"Client not initialized",code:"CLIENT_NOT_INITIALIZED"}};function he(t){return typeof t=="object"&&t!==null&&"isLoggedIn"in t&&t.isLoggedIn===!1&&"error"in t&&typeof t.error=="object"&&t.error!==null&&"message"in t.error&&typeof t.error.message=="string"&&"code"in t.error&&typeof t.error.code=="string"}function me(t){return typeof t=="object"&&t!==null&&"isLoggedIn"in t&&t.isLoggedIn===!1&&!("error"in t)}function ge(t){return typeof t=="object"&&t!==null&&"isLoggedIn"in t&&t.isLoggedIn===!0&&"userInfo"in t&&typeof t.userInfo=="object"&&t.userInfo!==null&&"sub"in t.userInfo&&typeof t.userInfo.sub=="string"&&"accessToken"in t&&typeof t.accessToken=="string"}const _=()=>{};function B(t,e){return{kind:"event",type:t,payload:e}}function j(t,e,n){return{kind:"response",type:e,requestId:t,payload:n}}function F(t,e){return{kind:"error",requestId:t,error:e}}function ve(t,e){const n=e[t.type];if(!n)return null;const r=n(t.payload);return j(t.requestId,t.type,r)}const T={debug:0,info:1,warn:2,error:3,silent:4};class V{constructor(e,n="warn"){this.prefix=e,this.level=n}shouldLog(e){return T[e]>=T[this.level]}ts(){const e=new Date,n=e.getHours().toString().padStart(2,"0"),r=e.getMinutes().toString().padStart(2,"0"),a=e.getSeconds().toString().padStart(2,"0"),i=e.getMilliseconds().toString().padStart(3,"0");return`${n}:${r}:${a}.${i}`}debug(...e){this.shouldLog("debug")&&console.log(`[${this.prefix}] ${this.ts()}`,...e)}info(...e){this.shouldLog("info")&&console.log(`[${this.prefix}] ${this.ts()}`,...e)}warn(...e){this.shouldLog("warn")&&console.warn(`[${this.prefix}] ${this.ts()}`,...e)}error(...e){this.shouldLog("error")&&console.error(`[${this.prefix}] ${this.ts()}`,...e)}}function ye(t){return"handleOAuthCallback"in t}function be(){var a;const e=new z.UAParser().getBrowser(),n=((a=e.name)==null?void 0:a.toLowerCase())||"",r=e.name||"Unknown Browser";if(n.includes("chrome"))return{name:"Google Chrome",type:"chrome"};if(n.includes("edge"))return{name:"Microsoft Edge",type:"edge"};if(n.includes("firefox"))return{name:"Mozilla Firefox",type:"firefox"};if(n.includes("safari"))return{name:"Safari",type:"safari"};{let i=r.replace(/\s+Browser$/i,"").replace(/\s+browser$/i,"").trim();return(!i||i.toLowerCase()==="unknown")&&(i="Unknown Browser"),{name:i,type:"unknown"}}}function xe(){var r;const n=((r=new z.UAParser().getOS().name)==null?void 0:r.toLowerCase())||"";return n.includes("mac")?{name:"macOS",type:"macos"}:n.includes("windows")?{name:"Windows",type:"windows"}:n.includes("linux")||n.includes("ubuntu")||n.includes("fedora")||n.includes("debian")?{name:"Linux",type:"linux"}:{name:"Unknown",type:"unknown"}}function Q(t){return{ACCESS_TOKEN:`${t}:access_token`,REFRESH_TOKEN:`${t}:refresh_token`,EXPIRES_AT:`${t}:expires_at`,CODE_VERIFIER:`${t}:code_verifier`,STATE:`${t}:state`,RESOURCE_SCOPE:`${t}:resource_scope`}}const we={DIRECT:"bodhi:direct",WEB:"bodhi:web",EXT:"bodhi:ext"};function H(t,e){return`${t}:${e}`}class q{constructor(e="bodhijs:"){this.storagePrefix=e,this.STORAGE_KEYS={DIRECT_STATUS:`${e}connection:directStatus`,SERIALIZED_CLIENT_STATE:`${e}client:serializedState`},this.SERVER_INSTALL_KEY=`${e}server:installed`}getDirectStatus(){return this.getStorageValue(this.STORAGE_KEYS.DIRECT_STATUS,null)}setDirectStatus(e){this.setStorageValue(this.STORAGE_KEYS.DIRECT_STATUS,e)}clear(){try{localStorage.removeItem(this.STORAGE_KEYS.DIRECT_STATUS),localStorage.removeItem(this.STORAGE_KEYS.SERIALIZED_CLIENT_STATE),localStorage.removeItem(this.SERVER_INSTALL_KEY)}catch(e){console.warn("[BodhiClientUserPrefsManager] Failed to clear preferences:",e)}}isServerInstallConfirmed(){return this.getStorageValue(this.SERVER_INSTALL_KEY,!1)}setServerInstallConfirmed(e){this.setStorageValue(this.SERVER_INSTALL_KEY,e)}getSerializedClientState(){return this.getStorageValue(this.STORAGE_KEYS.SERIALIZED_CLIENT_STATE,null)}setSerializedClientState(e){this.setStorageValue(this.STORAGE_KEYS.SERIALIZED_CLIENT_STATE,e)}getStorageValue(e,n){try{const r=localStorage.getItem(e);return r===null?n:JSON.parse(r)}catch(r){return console.warn(`[BodhiClientUserPrefsManager] Failed to read ${e}:`,r),n}}setStorageValue(e,n){try{localStorage.setItem(e,JSON.stringify(n))}catch(r){console.warn(`[BodhiClientUserPrefsManager] Failed to write ${e}:`,r)}}}function Se(t){return t.kind==="request"}const ke={MODAL_READY:"modal:ready",MODAL_REFRESH:"modal:refresh",MODAL_CLOSE:"modal:close",MODAL_COMPLETE:"modal:complete",MODAL_LNA_CONNECT:"modal:lna:connect",MODAL_LNA_SKIP:"modal:lna:skip",MODAL_CONFIRM_SERVER_INSTALL:"modal:confirm-server-install",MODAL_SELECT_CONNECTION:"modal:select-connection",PARENT_STATE_UPDATE:"parent:state-update"},Ee=`<!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
@@ -66,7 +66,7 @@ var ne={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24
66
66
  <div id="root"></div>
67
67
  </body>
68
68
  </html>
69
- `;class Ee{constructor(e){this.overlayElement=null,this.iframeElement=null,this.isIframeReady=!1,this.currentSetupState=null,this.messageHandler=null,this.modalHtmlPath=e.modalHtmlPath??"src/sdk/core/onboarding/modal.html",this.handlers=e.handlers}show(e){this.overlayElement||(this.currentSetupState=e,this.overlayElement=document.createElement("div"),this.overlayElement.setAttribute("data-testid","div-setup-overlay"),this.overlayElement.style.cssText=`
69
+ `;class Ce{constructor(e){this.overlayElement=null,this.iframeElement=null,this.isIframeReady=!1,this.currentSetupState=null,this.messageHandler=null,this.modalHtmlPath=e.modalHtmlPath??"src/sdk/core/onboarding/modal.html",this.handlers=e.handlers}show(e){this.overlayElement||(this.currentSetupState=e,this.overlayElement=document.createElement("div"),this.overlayElement.setAttribute("data-testid","div-setup-overlay"),this.overlayElement.style.cssText=`
70
70
  position: fixed;
71
71
  top: 0;
72
72
  left: 0;
@@ -77,7 +77,7 @@ var ne={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24
77
77
  display: flex;
78
78
  align-items: center;
79
79
  justify-content: center;
80
- `,this.iframeElement=document.createElement("iframe"),this.iframeElement.setAttribute("data-testid","iframe-setup"),typeof chrome<"u"&&chrome.runtime&&chrome.runtime.getURL?(this.iframeElement.sandbox.add("allow-scripts","allow-same-origin"),this.iframeElement.src=chrome.runtime.getURL(this.modalHtmlPath)):(this.iframeElement.sandbox.add("allow-scripts"),this.iframeElement.srcdoc=ke),this.iframeElement.style.cssText=`
80
+ `,this.iframeElement=document.createElement("iframe"),this.iframeElement.setAttribute("data-testid","iframe-setup"),typeof chrome<"u"&&chrome.runtime&&chrome.runtime.getURL?(this.iframeElement.sandbox.add("allow-scripts","allow-same-origin"),this.iframeElement.src=chrome.runtime.getURL(this.modalHtmlPath)):(this.iframeElement.sandbox.add("allow-scripts"),this.iframeElement.srcdoc=Ee),this.iframeElement.style.cssText=`
81
81
  width: 90%;
82
82
  max-width: 800px;
83
83
  height: 90%;
@@ -85,5 +85,5 @@ var ne={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24
85
85
  border: none;
86
86
  border-radius: 8px;
87
87
  background: white;
88
- `,this.overlayElement.appendChild(this.iframeElement),document.body.appendChild(this.overlayElement),this.messageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.messageHandler))}updateState(e){this.currentSetupState=e,this.sendStateToModal()}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlayElement&&(this.overlayElement.remove(),this.overlayElement=null,this.iframeElement=null),this.isIframeReady=!1,this.currentSetupState=null}async handleMessage(e){var a,i,s,o,l;const n=e.data;if(!we(n)||e.source!==((a=this.iframeElement)==null?void 0:a.contentWindow))return;const r=this.handlers[n.type];if(!r){console.warn("[OnboardingModal] No handler for message type:",n.type);return}try{n.type===Se.MODAL_READY&&(this.isIframeReady=!0);const c=await r(n),u=j(n.requestId,n.type,c);(s=(i=this.iframeElement)==null?void 0:i.contentWindow)==null||s.postMessage(u,"*")}catch(c){console.error("[OnboardingModal] Handler error:",c);const u=B(n.requestId,{code:"handler-error",message:c instanceof Error?c.message:"Handler execution failed"});(l=(o=this.iframeElement)==null?void 0:o.contentWindow)==null||l.postMessage(u,"*")}}sendStateToModal(){var n;if(!this.isIframeReady||!this.iframeElement||!this.currentSetupState)return;const e=F("parent:state-update",{setupState:this.currentSetupState});(n=this.iframeElement.contentWindow)==null||n.postMessage(e,"*")}}const Ce=[{id:"chrome",status:"supported",name:"Google Chrome",extension_url:"https://chrome.google.com/webstore/detail/bodhi-browser/example-id"},{id:"edge",status:"supported",name:"Microsoft Edge",extension_url:"https://microsoftedge.microsoft.com/addons/detail/bodhi-browser/example-id"},{id:"firefox",status:"not-supported",name:"Mozilla Firefox",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/firefox-support"},{id:"safari",status:"not-supported",name:"Safari",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/safari-support"},{id:"unknown",status:"not-supported",name:"Unknown Browser",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/new"}],Ne=[{id:"macos",status:"supported",name:"macOS",download_url:"https://github.com/BodhiSearch/bodhi-browser/releases/latest/download/bodhi-server-macos"},{id:"windows",status:"supported",name:"Windows",download_url:"https://github.com/BodhiSearch/bodhi-browser/releases/latest/download/bodhi-server-windows.exe"},{id:"linux",status:"supported",name:"Linux",download_url:"https://github.com/BodhiSearch/bodhi-browser/releases/latest/download/bodhi-server-linux"},{id:"unknown",status:"not-supported",name:"Unknown OS",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/new"}];function A(t){return btoa(String.fromCharCode(...new Uint8Array(t))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function _e(){const t=new Uint8Array(32);return crypto.getRandomValues(t),A(t.buffer)}async function je(t){const n=new TextEncoder().encode(t),r=await crypto.subtle.digest("SHA-256",n);return A(r)}function q(t){const n=t.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),r=decodeURIComponent(atob(n).split("").map(a=>"%"+("00"+a.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(r)}function S(t){const e=q(t);return{sub:e.sub,email:e.email,name:e.name,given_name:e.given_name,family_name:e.family_name,preferred_username:e.preferred_username}}function W(t){return{authorize:`${t}/protocol/openid-connect/auth`,token:`${t}/protocol/openid-connect/token`,revoke:`${t}/protocol/openid-connect/revoke`}}async function $(t,e,n){try{const r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:n})});if(!r.ok)return null;const a=await r.json();return{access_token:a.access_token,refresh_token:a.refresh_token,id_token:a.id_token,expires_in:a.expires_in||3600}}catch{return null}}async function G(t){const e=`${t}/bodhi/v1/info`;try{const n=new AbortController,r=setTimeout(()=>n.abort(),5e3),a=await fetch(e,{method:"GET",headers:{"Content-Type":"application/json"},signal:n.signal});if(clearTimeout(r),a.ok&&a.status===200){const i=await a.json();return{success:!0,serverInfo:{status:i.status||"unknown",version:i.version||"unknown"}}}return{success:!1,error:{message:`Server returned ${a.status}: ${a.statusText}`,type:"server-error"}}}catch(n){return{success:!1,error:{message:`Cannot reach server: ${n instanceof Error?n.message:"Network error"}`,type:"network-error"}}}}class Ae{constructor(e,n,r){this.serverUrl=null,this.state=v,this.refreshPromise=null,this.logger=new V(n,e.logLevel),this.authClientId=e.authClientId,this.authServerUrl=e.authServerUrl||"https://id.getbodhi.app/realms/bodhi",this.userScope=e.userScope||"scope_user_user",this.authEndpoints=W(this.authServerUrl),this.storageKeys=Q(e.storagePrefix),this.onStateChange=r??_}setState(e){this.state=e,this.onStateChange({type:"client-state",state:e})}setAuthState(e){this.onStateChange({type:"auth-state",state:e})}setStateCallback(e){this.onStateChange=e}async init(e){var r;const n=e.serverUrl??((r=e.savedState)==null?void 0:r.url)??this.serverUrl;if(this.serverUrl&&this.serverUrl===n&&!e.testConnection)return this.logger.debug("Already initialized with serverUrl, skipping init"),this.state;if(this.serverUrl=n,!this.serverUrl)return this.logger.info("No serverUrl provided, returning not-initialized state"),v;if(this.logger.info("Initializing with serverUrl:",this.serverUrl),e.testConnection){const a=await this.testConnectivity();let i;if(a.success&&a.serverInfo){const s=a.serverInfo.status,o=a.serverInfo.version;s==="ready"?i={status:"ready",version:o}:s==="setup"||s==="resource-admin"||s==="error"?i=L(s,o):i=y}else this.logger.warn("Connection failed:",a.error),i=y;return this.setState({type:"direct",url:n,server:i}),this.logger.info("Initialized with testConnection, server state:",i.status),this.state}return e.selectedConnection?(this.setState({type:"direct",url:n,server:E}),this.logger.info("Initialized with selectedConnection, server state: not-connected"),this.state):(this.logger.info("No selectedConnection, returning not-initialized state"),v)}getState(){return this.state}isClientInitialized(){return this.serverUrl!==null}isServerReady(){return this.isClientInitialized()&&this.state.type==="direct"&&this.state.server.status==="ready"}ensureInitialized(){if(!this.serverUrl)throw g("DirectClient not initialized. Call init(serverUrl) first.","not-initialized")}async sendApiRequest(e,n,r,a,i=!0){if(!this.serverUrl)return{error:{message:"Client not initialized - connection failed",type:"not-initialized"}};const s=`${this.serverUrl}${n}`;this.logger.debug(`${e} ${s}`);try{const o=new AbortController,l=setTimeout(()=>o.abort(),5e3),c={"Content-Type":"application/json",...a};if(i){const f=await this._getAccessTokenRaw();f&&(c.Authorization=`Bearer ${f}`)}const u=await fetch(s,{method:e,headers:c,body:r?JSON.stringify(r):void 0,signal:o.signal});clearTimeout(l);const d={};return u.headers.forEach((f,m)=>{d[m]=f}),{body:await u.json(),status:u.status,headers:d}}catch(o){return{error:{message:`Network error: ${o instanceof Error?o.message:"Unknown error"}`,type:"network_error"}}}}async pingApi(){return this.sendApiRequest("GET","/ping",void 0,{},!1)}async fetchModels(){return this.sendApiRequest("GET","/v1/models")}async getServerState(){const e=await this.testConnectivity();if(!e.success)return{status:"not-reachable",error:e.error||{message:"Connection failed",type:"network_error"}};const n=e.serverInfo;switch(n.status){case"ready":return{status:"ready",version:n.version};case"setup":return{status:"setup",version:n.version,error:{message:"Setup required",type:"extension_error"}};case"resource-admin":return{status:"resource-admin",version:n.version,error:{message:"Resource admin required",type:"extension_error"}};case"error":return{status:"error",version:n.version||"unknown",error:{message:"Server error",type:"extension_error"}};default:return{status:"not-reachable",error:{message:"Unknown status",type:"extension_error"}}}}async*stream(e,n,r,a,i=!0){if(!this.serverUrl)throw g("Client not initialized - connection failed","not-initialized");const s=`${this.serverUrl}${n}`;this.logger.debug(`Stream ${e} ${s}`);const o={"Content-Type":"application/json",Accept:"text/event-stream",...a};if(i){const p=await this._getAccessTokenRaw();p&&(o.Authorization=`Bearer ${p}`)}const l=await fetch(s,{method:e,headers:o,body:r?JSON.stringify(r):void 0});if(!l.ok)throw new Error(`HTTP ${l.status}: ${await l.text()}`);if(!l.body)throw new Error("Response body is null");const c=l.body.getReader(),u=new TextDecoder;let d="";try{for(;;){const{done:p,value:f}=await c.read();if(p)break;d+=u.decode(f,{stream:!0});const m=d.split(`
89
- `);d=m.pop()||"";for(const I of m)if(I.startsWith("data: ")){const x=I.slice(6).trim();if(x==="[DONE]")return;try{yield JSON.parse(x)}catch(Y){this.logger.warn("Failed to parse SSE data:",x,Y)}}}}finally{c.releaseLock()}}async*streamChat(e,n,r=!0){yield*this.stream("POST","/v1/chat/completions",{model:e,messages:[{role:"user",content:n}],stream:!0},{},r)}async testConnectivity(){return this.ensureInitialized(),this.logger.debug("Testing connectivity to:",this.serverUrl),G(this.serverUrl)}serialize(){return this.serverUrl?{url:this.serverUrl}:{}}async debug(){return{type:"DirectClient",serverUrl:this.serverUrl,state:this.state,authState:await this.getAuthState(),authClientId:this.authClientId,authServerUrl:this.authServerUrl,userScope:this.userScope}}async getAuthState(){const e=await this._getAccessTokenRaw();return e?{isLoggedIn:!0,userInfo:S(e),accessToken:e}:{isLoggedIn:!1}}async _getAccessTokenRaw(){const e=await this._storageGet(this.storageKeys.ACCESS_TOKEN),n=await this._storageGet(this.storageKeys.EXPIRES_AT);if(!e)return null;if(n){const r=parseInt(n,10);if(Date.now()>=r-5*1e3){const a=await this._storageGet(this.storageKeys.REFRESH_TOKEN);return a?this._tryRefreshToken(a):null}}return e}async _tryRefreshToken(e){if(this.refreshPromise)return this.logger.debug("Refresh already in progress, returning existing promise"),this.refreshPromise;this.refreshPromise=this._doRefreshToken(e);try{return await this.refreshPromise}finally{this.refreshPromise=null}}async _doRefreshToken(e){this.logger.debug("Refreshing access token");try{const n=await $(this.authEndpoints.token,e,this.authClientId);if(n){await this._storeRefreshedTokens(n);const r=S(n.access_token);return this.setAuthState({isLoggedIn:!0,userInfo:r,accessToken:n.access_token}),this.logger.info("Token refreshed successfully"),n.access_token}}catch(n){this.logger.warn("Token refresh failed:",n)}throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),g("Access token expired and unable to refresh. Try logging out and logging in again.","token_refresh_failed")}async _storeRefreshedTokens(e){const n=Date.now()+e.expires_in*1e3,r={[this.storageKeys.ACCESS_TOKEN]:e.access_token,[this.storageKeys.EXPIRES_AT]:String(n)};e.refresh_token&&(r[this.storageKeys.REFRESH_TOKEN]=e.refresh_token),await this._storageSet(r)}async requestResourceAccess(){const e=await this.sendApiRequest("POST","/bodhi/v1/apps/request-access",{app_client_id:this.authClientId},{},!1);if(R(e))throw new Error("Failed to get resource access scope from server");if(!O(e))throw new Error("Failed to get resource access scope from server: API error");const n=e.body.scope;return await this._storageSet({[this.storageKeys.RESOURCE_SCOPE]:n}),n}async exchangeCodeForTokens(e){const n=await this._storageGet(this.storageKeys.CODE_VERIFIER);if(!n)throw new Error("Code verifier not found");const r=this._getRedirectUri(),a=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:r,client_id:this.authClientId,code_verifier:n})});if(!a.ok){const o=await a.text();throw new Error(`Token exchange failed: ${a.status} ${o}`)}const i=await a.json(),s=Date.now()+(i.expires_in||3600)*1e3;await this._storageSet({[this.storageKeys.ACCESS_TOKEN]:i.access_token,[this.storageKeys.REFRESH_TOKEN]:i.refresh_token||"",[this.storageKeys.EXPIRES_AT]:String(s)}),await this._storageRemove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE])}async revokeRefreshToken(){const e=await this._storageGet(this.storageKeys.REFRESH_TOKEN);if(e)try{const n=new URLSearchParams({token:e,client_id:this.authClientId,token_type_hint:"refresh_token"});await fetch(this.authEndpoints.revoke,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:n})}catch(n){this.logger.warn("Token revocation failed:",n)}}async clearAuthStorage(){await this._storageRemove([this.storageKeys.ACCESS_TOKEN,this.storageKeys.REFRESH_TOKEN,this.storageKeys.EXPIRES_AT,this.storageKeys.RESOURCE_SCOPE])}}class Ie{constructor(e,n,r,a){this.connectionMode=null,this.authClientId=e,this.config=n,this.onStateChange=r??_,this.prefs=new H(a),this.logger=this.createLogger(n),this.extClient=this.createExtClient(n,i=>this.handleInternalStateChange(i)),this.directClient=this.createDirectClient(e,n,i=>this.handleInternalStateChange(i))}notifyStateChange(){this.persist(),this.onStateChange({type:"client-state",state:this.getState()}),this.getAuthState().then(e=>{this.onStateChange({type:"auth-state",state:e})}).catch(e=>{this.logger.error("Failed to get auth state after client-state change:",e)})}handleInternalStateChange(e){e.type==="client-state"?this.notifyStateChange():e.type==="auth-state"&&this.getAuthState().then(n=>{this.onStateChange({type:"auth-state",state:n})}).catch(n=>{this.logger.error("Failed to get auth state after client-state change:",n)})}serialize(){return{connectionMode:this.connectionMode,direct:this.directClient.serialize(),extension:this.extClient.serialize()}}async debug(){return{type:"FacadeClient",connectionMode:this.connectionMode,authClientId:this.authClientId,config:this.config,extClient:await this.extClient.debug(),directClient:await this.directClient.debug()}}persist(){const e=this.serialize();this.prefs.setSerializedClientState(e),this.logger.debug("Persisted state:",JSON.stringify(e,null,2))}setStateCallback(e){this.onStateChange=e}isNotSetOrDirect(){return this.connectionMode==null||this.connectionMode==="direct"}async init(e={}){const n=this.prefs.getSerializedClientState()??{connectionMode:null,direct:{},extension:{}};if(this.connectionMode===null&&n.connectionMode&&(this.connectionMode=n.connectionMode,this.logger.info("Restored connectionMode from storage:",n.connectionMode)),this.connectionMode===null)return this.logger.info("{connectionMode: null} - Eager connection detection"),await this.directClient.init({savedState:n.direct,selectedConnection:!0,testConnection:!0,serverUrl:e.serverUrl}),this.directClient.isServerReady()&&(this.connectionMode="direct",this.logger.info("Auto-detected direct connection (server ready)")),this.connectionMode===null?(await this.extClient.init({savedState:n.extension,selectedConnection:!0,testConnection:!0,timeoutMs:e.timeoutMs}),this.extClient.isServerReady()&&(this.connectionMode="extension",this.logger.info("Auto-detected extension connection (server ready)"))):await this.extClient.init({savedState:n.extension,selectedConnection:!1,testConnection:!1}),this.notifyStateChange(),this.getState();const r=this.connectionMode==="direct",a=this.connectionMode==="extension";return await this.extClient.init({savedState:n.extension,selectedConnection:a,testConnection:a&&e.testConnection,timeoutMs:e.timeoutMs}),await this.directClient.init({savedState:n.direct,selectedConnection:r,testConnection:r&&e.testConnection,serverUrl:e.serverUrl}),this.notifyStateChange(),!e.testConnection&&this.isClientInitialized()&&!this.isServerReady()&&(this.logger.info("Triggering async server state refresh"),(this.isNotSetOrDirect()?this.directClient:this.extClient).init({testConnection:!0,selectedConnection:!0}).catch(s=>{this.logger.warn("Async server state refresh failed:",s)})),this.getState()}getState(){return this.isNotSetOrDirect()?this.directClient.getState():this.extClient.getState()}isClientInitialized(){return this.isNotSetOrDirect()?this.directClient.isClientInitialized():this.extClient.isClientInitialized()}isServerReady(){return this.isNotSetOrDirect()?this.directClient.isServerReady():this.extClient.isServerReady()}sendExtRequest(e,n){if(this.isNotSetOrDirect())throw new Error("sendExtRequest not available on direct connection");return this.extClient.sendExtRequest(e,n)}sendApiRequest(e,n,r,a,i){return this.isNotSetOrDirect()?this.directClient.sendApiRequest(e,n,r,a,i):this.extClient.sendApiRequest(e,n,r,a,i)}login(){return this.isNotSetOrDirect()?this.directClient.login():this.extClient.login()}logout(){return this.isNotSetOrDirect()?this.directClient.logout():this.extClient.logout()}getAuthState(){return this.isNotSetOrDirect()?this.directClient.getAuthState():this.extClient.getAuthState()}pingApi(){return this.isNotSetOrDirect()?this.directClient.pingApi():this.extClient.pingApi()}fetchModels(){return this.isNotSetOrDirect()?this.directClient.fetchModels():this.extClient.fetchModels()}async getServerState(){return this.isNotSetOrDirect()?this.directClient.getServerState():this.extClient.getServerState()}stream(e,n,r,a,i){return this.isNotSetOrDirect()?this.directClient.stream(e,n,r,a,i):this.extClient.stream(e,n,r,a,i)}streamChat(e,n,r){return this.isNotSetOrDirect()?this.directClient.streamChat(e,n,r):this.extClient.streamChat(e,n,r)}getConnectionMode(){return this.connectionMode}async setConnectionMode(e){if(this.logger.info("Switching connection mode to:",e),e==="direct"){if(!this.directClient.isClientInitialized())throw new Error(h.SERVER_NOT_READY.message)}else if(e==="extension"){if(!this.extClient.isClientInitialized())throw new Error(h.SERVER_NOT_READY.message)}else throw new Error("Invalid connection mode");return this.connectionMode=e,this.notifyStateChange(),this.getState()}async testExtensionConnectivity(e){const n=await this.extClient.init({testConnection:!0,timeoutMs:e});return this.notifyStateChange(),n}async testDirectConnectivity(e){const n=e??this.directClient.serialize().url;if(!n)return this.directClient.getState();const r=await this.directClient.init({serverUrl:n,testConnection:!0});return this.notifyStateChange(),r}async getExtensionState(){const e=this.extClient.getState();if(b(e))return e;throw new Error("extClient did not return an ExtensionState")}async getDirectState(){const e=this.directClient.getState();if(N(e))return e;throw new Error("directClient did not return a DirectState")}}exports.AUTH_EXT_NOT_INITIALIZED=fe;exports.BACKEND_SERVER_NOT_CONNECTED=E;exports.BACKEND_SERVER_NOT_REACHABLE=y;exports.BROWSER_CONFIGS=Ce;exports.BaseFacadeClient=Ie;exports.BodhiClientUserPrefsManager=H;exports.DIRECT_STATE_NOT_INITIALIZED=v;exports.DirectClientBase=Ae;exports.EXTENSION_STATE_NOT_FOUND=D;exports.EXTENSION_STATE_NOT_INITIALIZED=M;exports.Logger=V;exports.NOOP_STATE_CALLBACK=_;exports.OS_CONFIGS=Ne;exports.OnboardingModal=Ee;exports.PENDING_EXTENSION_READY=k;exports.SERVER_ERROR_CODES=h;exports.STORAGE_PREFIXES=xe;exports.backendServerNotReady=L;exports.base64UrlEncode=A;exports.buildError=B;exports.buildEvent=F;exports.buildResponse=j;exports.createApiError=ee;exports.createDirectStateNotReachable=re;exports.createDirectStateNotReady=ae;exports.createDirectStateReady=ne;exports.createExtensionStateNotFound=oe;exports.createExtensionStateNotInitialized=se;exports.createOAuthEndpoints=W;exports.createOperationError=g;exports.createStorageKeys=Q;exports.detectBrowser=ye;exports.detectOS=be;exports.extractUserInfo=S;exports.generateCodeChallenge=je;exports.generateCodeVerifier=_e;exports.getBackendServerState=ce;exports.getExtensionId=ue;exports.getServerUrl=de;exports.handleRequest=ge;exports.isApiResultError=J;exports.isApiResultOperationError=R;exports.isApiResultSuccess=O;exports.isAuthError=pe;exports.isAuthLoggedIn=me;exports.isAuthLoggedOut=he;exports.isClientReady=le;exports.isDirectClientReady=P;exports.isDirectServerReady=te;exports.isDirectState=N;exports.isExtensionClientReady=U;exports.isExtensionServerReady=ie;exports.isExtensionState=b;exports.isOperationError=Z;exports.isServerReady=C;exports.isWebUIClient=ve;exports.parseJwt=q;exports.refreshAccessToken=$;exports.testServerConnectivity=G;
88
+ `,this.overlayElement.appendChild(this.iframeElement),document.body.appendChild(this.overlayElement),this.messageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.messageHandler))}updateState(e){this.currentSetupState=e,this.sendStateToModal()}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlayElement&&(this.overlayElement.remove(),this.overlayElement=null,this.iframeElement=null),this.isIframeReady=!1,this.currentSetupState=null}async handleMessage(e){var a,i,s,o,l;const n=e.data;if(!Se(n)||e.source!==((a=this.iframeElement)==null?void 0:a.contentWindow))return;const r=this.handlers[n.type];if(!r){console.warn("[OnboardingModal] No handler for message type:",n.type);return}try{n.type===ke.MODAL_READY&&(this.isIframeReady=!0);const c=await r(n),u=j(n.requestId,n.type,c);(s=(i=this.iframeElement)==null?void 0:i.contentWindow)==null||s.postMessage(u,"*")}catch(c){console.error("[OnboardingModal] Handler error:",c);const u=F(n.requestId,{code:"handler-error",message:c instanceof Error?c.message:"Handler execution failed"});(l=(o=this.iframeElement)==null?void 0:o.contentWindow)==null||l.postMessage(u,"*")}}sendStateToModal(){var n;if(!this.isIframeReady||!this.iframeElement||!this.currentSetupState)return;const e=B("parent:state-update",{setupState:this.currentSetupState});(n=this.iframeElement.contentWindow)==null||n.postMessage(e,"*")}}const Ne=[{id:"chrome",status:"supported",name:"Google Chrome",extension_url:"https://chrome.google.com/webstore/detail/bodhi-browser/example-id"},{id:"edge",status:"supported",name:"Microsoft Edge",extension_url:"https://microsoftedge.microsoft.com/addons/detail/bodhi-browser/example-id"},{id:"firefox",status:"not-supported",name:"Mozilla Firefox",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/firefox-support"},{id:"safari",status:"not-supported",name:"Safari",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/safari-support"},{id:"unknown",status:"not-supported",name:"Unknown Browser",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/new"}],_e=[{id:"macos",status:"supported",name:"macOS",download_url:"https://github.com/BodhiSearch/bodhi-browser/releases/latest/download/bodhi-server-macos"},{id:"windows",status:"supported",name:"Windows",download_url:"https://github.com/BodhiSearch/bodhi-browser/releases/latest/download/bodhi-server-windows.exe"},{id:"linux",status:"supported",name:"Linux",download_url:"https://github.com/BodhiSearch/bodhi-browser/releases/latest/download/bodhi-server-linux"},{id:"unknown",status:"not-supported",name:"Unknown OS",github_issue_url:"https://github.com/BodhiSearch/bodhi-browser/issues/new"}];function A(t){return btoa(String.fromCharCode(...new Uint8Array(t))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function je(){const t=new Uint8Array(32);return crypto.getRandomValues(t),A(t.buffer)}async function Ae(t){const n=new TextEncoder().encode(t),r=await crypto.subtle.digest("SHA-256",n);return A(r)}function W(t){const n=t.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),r=decodeURIComponent(atob(n).split("").map(a=>"%"+("00"+a.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(r)}function S(t){const e=W(t);return{sub:e.sub,email:e.email,name:e.name,given_name:e.given_name,family_name:e.family_name,preferred_username:e.preferred_username}}function $(t){return{authorize:`${t}/protocol/openid-connect/auth`,token:`${t}/protocol/openid-connect/token`,revoke:`${t}/protocol/openid-connect/revoke`}}async function G(t,e,n){try{const r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:n})});if(!r.ok)return null;const a=await r.json();return{access_token:a.access_token,refresh_token:a.refresh_token,id_token:a.id_token,expires_in:a.expires_in||3600}}catch{return null}}async function Y(t){const e=`${t}/bodhi/v1/info`;try{const n=new AbortController,r=setTimeout(()=>n.abort(),5e3),a=await fetch(e,{method:"GET",headers:{"Content-Type":"application/json"},signal:n.signal});if(clearTimeout(r),a.ok&&a.status===200){const i=await a.json();return{success:!0,serverInfo:{status:i.status||"unknown",version:i.version||"unknown"}}}return{success:!1,error:{message:`Server returned ${a.status}: ${a.statusText}`,type:"server-error"}}}catch(n){return{success:!1,error:{message:`Cannot reach server: ${n instanceof Error?n.message:"Network error"}`,type:"network-error"}}}}class Ie{constructor(e,n,r){this.serverUrl=null,this.state=v,this.refreshPromise=null,this.logger=new V(n,e.logLevel),this.authClientId=e.authClientId,this.authServerUrl=e.authServerUrl||"https://id.getbodhi.app/realms/bodhi",this.userScope=e.userScope||"scope_user_user",this.authEndpoints=$(this.authServerUrl),this.storageKeys=Q(e.storagePrefix),this.onStateChange=r??_}setState(e){this.state=e,this.onStateChange({type:"client-state",state:e})}setAuthState(e){this.onStateChange({type:"auth-state",state:e})}setStateCallback(e){this.onStateChange=e}async init(e){var r;const n=e.serverUrl??((r=e.savedState)==null?void 0:r.url)??this.serverUrl;if(this.serverUrl&&this.serverUrl===n&&!e.testConnection)return this.logger.debug("Already initialized with serverUrl, skipping init"),this.state;if(this.serverUrl=n,!this.serverUrl)return this.logger.info("No serverUrl provided, returning not-initialized state"),v;if(this.logger.info("Initializing with serverUrl:",this.serverUrl),e.testConnection){const a=await this.testConnectivity();let i;if(a.success&&a.serverInfo){const s=a.serverInfo.status,o=a.serverInfo.version;s==="ready"?i={status:"ready",version:o}:s==="setup"||s==="resource-admin"||s==="error"?i=L(s,o):i=y}else this.logger.warn("Connection failed:",a.error),i=y;return this.setState({type:"direct",url:n,server:i}),this.logger.info("Initialized with testConnection, server state:",i.status),this.state}return e.selectedConnection?(this.setState({type:"direct",url:n,server:E}),this.logger.info("Initialized with selectedConnection, server state: not-connected"),this.state):(this.logger.info("No selectedConnection, returning not-initialized state"),v)}getState(){return this.state}isClientInitialized(){return this.serverUrl!==null}isServerReady(){return this.isClientInitialized()&&this.state.type==="direct"&&this.state.server.status==="ready"}ensureInitialized(){if(!this.serverUrl)throw g("DirectClient not initialized. Call init(serverUrl) first.","not-initialized")}async sendApiRequest(e,n,r,a,i=!0){if(!this.serverUrl)return{error:{message:"Client not initialized - connection failed",type:"not-initialized"}};const s=`${this.serverUrl}${n}`;this.logger.debug(`${e} ${s}`);try{const o=new AbortController,l=setTimeout(()=>o.abort(),5e3),c={"Content-Type":"application/json",...a};if(i){const f=await this._getAccessTokenRaw();f&&(c.Authorization=`Bearer ${f}`)}const u=await fetch(s,{method:e,headers:c,body:r?JSON.stringify(r):void 0,signal:o.signal});clearTimeout(l);const d={};return u.headers.forEach((f,m)=>{d[m]=f}),{body:await u.json(),status:u.status,headers:d}}catch(o){return{error:{message:`Network error: ${o instanceof Error?o.message:"Unknown error"}`,type:"network_error"}}}}async pingApi(){return this.sendApiRequest("GET","/ping",void 0,{},!1)}async fetchModels(){return this.sendApiRequest("GET","/v1/models")}async getServerState(){const e=await this.testConnectivity();if(!e.success)return{status:"not-reachable",error:e.error||{message:"Connection failed",type:"network_error"}};const n=e.serverInfo;switch(n.status){case"ready":return{status:"ready",version:n.version};case"setup":return{status:"setup",version:n.version,error:{message:"Setup required",type:"extension_error"}};case"resource-admin":return{status:"resource-admin",version:n.version,error:{message:"Resource admin required",type:"extension_error"}};case"error":return{status:"error",version:n.version||"unknown",error:{message:"Server error",type:"extension_error"}};default:return{status:"not-reachable",error:{message:"Unknown status",type:"extension_error"}}}}async*stream(e,n,r,a,i=!0){if(!this.serverUrl)throw g("Client not initialized - connection failed","not-initialized");const s=`${this.serverUrl}${n}`;this.logger.debug(`Stream ${e} ${s}`);const o={"Content-Type":"application/json",Accept:"text/event-stream",...a};if(i){const p=await this._getAccessTokenRaw();p&&(o.Authorization=`Bearer ${p}`)}const l=await fetch(s,{method:e,headers:o,body:r?JSON.stringify(r):void 0});if(!l.ok)throw new Error(`HTTP ${l.status}: ${await l.text()}`);if(!l.body)throw new Error("Response body is null");const c=l.body.getReader(),u=new TextDecoder;let d="";try{for(;;){const{done:p,value:f}=await c.read();if(p)break;d+=u.decode(f,{stream:!0});const m=d.split(`
89
+ `);d=m.pop()||"";for(const I of m)if(I.startsWith("data: ")){const x=I.slice(6).trim();if(x==="[DONE]")return;try{yield JSON.parse(x)}catch(X){this.logger.warn("Failed to parse SSE data:",x,X)}}}}finally{c.releaseLock()}}async*streamChat(e,n,r=!0){yield*this.stream("POST","/v1/chat/completions",{model:e,messages:[{role:"user",content:n}],stream:!0},{},r)}async testConnectivity(){return this.ensureInitialized(),this.logger.debug("Testing connectivity to:",this.serverUrl),Y(this.serverUrl)}serialize(){return this.serverUrl?{url:this.serverUrl}:{}}async debug(){return{type:"DirectClient",serverUrl:this.serverUrl,state:this.state,authState:await this.getAuthState(),authClientId:this.authClientId,authServerUrl:this.authServerUrl,userScope:this.userScope}}async getAuthState(){const e=await this._getAccessTokenRaw();return e?{isLoggedIn:!0,userInfo:S(e),accessToken:e}:{isLoggedIn:!1}}async _getAccessTokenRaw(){const e=await this._storageGet(this.storageKeys.ACCESS_TOKEN),n=await this._storageGet(this.storageKeys.EXPIRES_AT);if(!e)return null;if(n){const r=parseInt(n,10);if(Date.now()>=r-5*1e3){const a=await this._storageGet(this.storageKeys.REFRESH_TOKEN);return a?this._tryRefreshToken(a):null}}return e}async _tryRefreshToken(e){if(this.refreshPromise)return this.logger.debug("Refresh already in progress, returning existing promise"),this.refreshPromise;this.refreshPromise=this._doRefreshToken(e);try{return await this.refreshPromise}finally{this.refreshPromise=null}}async _doRefreshToken(e){this.logger.debug("Refreshing access token");try{const n=await G(this.authEndpoints.token,e,this.authClientId);if(n){await this._storeRefreshedTokens(n);const r=S(n.access_token);return this.setAuthState({isLoggedIn:!0,userInfo:r,accessToken:n.access_token}),this.logger.info("Token refreshed successfully"),n.access_token}}catch(n){this.logger.warn("Token refresh failed:",n)}throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),g("Access token expired and unable to refresh. Try logging out and logging in again.","token_refresh_failed")}async _storeRefreshedTokens(e){const n=Date.now()+e.expires_in*1e3,r={[this.storageKeys.ACCESS_TOKEN]:e.access_token,[this.storageKeys.EXPIRES_AT]:String(n)};e.refresh_token&&(r[this.storageKeys.REFRESH_TOKEN]=e.refresh_token),await this._storageSet(r)}async requestResourceAccess(){const e=await this.sendApiRequest("POST","/bodhi/v1/apps/request-access",{app_client_id:this.authClientId},{},!1);if(R(e))throw new Error("Failed to get resource access scope from server");if(!O(e))throw new Error("Failed to get resource access scope from server: API error");const n=e.body.scope;return await this._storageSet({[this.storageKeys.RESOURCE_SCOPE]:n}),n}async exchangeCodeForTokens(e){const n=await this._storageGet(this.storageKeys.CODE_VERIFIER);if(!n)throw new Error("Code verifier not found");const r=this._getRedirectUri(),a=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:r,client_id:this.authClientId,code_verifier:n})});if(!a.ok){const o=await a.text();throw new Error(`Token exchange failed: ${a.status} ${o}`)}const i=await a.json(),s=Date.now()+(i.expires_in||3600)*1e3;await this._storageSet({[this.storageKeys.ACCESS_TOKEN]:i.access_token,[this.storageKeys.REFRESH_TOKEN]:i.refresh_token||"",[this.storageKeys.EXPIRES_AT]:String(s)}),await this._storageRemove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE])}async revokeRefreshToken(){const e=await this._storageGet(this.storageKeys.REFRESH_TOKEN);if(e)try{const n=new URLSearchParams({token:e,client_id:this.authClientId,token_type_hint:"refresh_token"});await fetch(this.authEndpoints.revoke,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:n})}catch(n){this.logger.warn("Token revocation failed:",n)}}async clearAuthStorage(){await this._storageRemove([this.storageKeys.ACCESS_TOKEN,this.storageKeys.REFRESH_TOKEN,this.storageKeys.EXPIRES_AT,this.storageKeys.RESOURCE_SCOPE])}}class Te{constructor(e,n,r,a,i){this.connectionMode=null,this.authClientId=e,this.config=n,this.onStateChange=r??_;const s=H(i||"/",a||"bodhijs:");this.prefs=new q(s),this.logger=this.createLogger(n),this.extClient=this.createExtClient(n,o=>this.handleInternalStateChange(o)),this.directClient=this.createDirectClient(e,n,o=>this.handleInternalStateChange(o))}notifyStateChange(){this.persist(),this.onStateChange({type:"client-state",state:this.getState()}),this.getAuthState().then(e=>{this.onStateChange({type:"auth-state",state:e})}).catch(e=>{this.logger.error("Failed to get auth state after client-state change:",e)})}handleInternalStateChange(e){e.type==="client-state"?this.notifyStateChange():e.type==="auth-state"&&this.getAuthState().then(n=>{this.onStateChange({type:"auth-state",state:n})}).catch(n=>{this.logger.error("Failed to get auth state after client-state change:",n)})}serialize(){return{connectionMode:this.connectionMode,direct:this.directClient.serialize(),extension:this.extClient.serialize()}}async debug(){return{type:"FacadeClient",connectionMode:this.connectionMode,authClientId:this.authClientId,config:this.config,extClient:await this.extClient.debug(),directClient:await this.directClient.debug()}}persist(){const e=this.serialize();this.prefs.setSerializedClientState(e),this.logger.debug("Persisted state:",JSON.stringify(e,null,2))}setStateCallback(e){this.onStateChange=e}isNotSetOrDirect(){return this.connectionMode==null||this.connectionMode==="direct"}async init(e={}){const n=this.prefs.getSerializedClientState()??{connectionMode:null,direct:{},extension:{}};if(this.connectionMode===null&&n.connectionMode&&(this.connectionMode=n.connectionMode,this.logger.info("Restored connectionMode from storage:",n.connectionMode)),this.connectionMode===null)return this.logger.info("{connectionMode: null} - Eager connection detection"),await this.directClient.init({savedState:n.direct,selectedConnection:!0,testConnection:!0,serverUrl:e.serverUrl}),this.directClient.isServerReady()&&(this.connectionMode="direct",this.logger.info("Auto-detected direct connection (server ready)")),this.connectionMode===null?(await this.extClient.init({savedState:n.extension,selectedConnection:!0,testConnection:!0,timeoutMs:e.timeoutMs}),this.extClient.isServerReady()&&(this.connectionMode="extension",this.logger.info("Auto-detected extension connection (server ready)"))):await this.extClient.init({savedState:n.extension,selectedConnection:!1,testConnection:!1}),this.notifyStateChange(),this.getState();const r=this.connectionMode==="direct",a=this.connectionMode==="extension";return await this.extClient.init({savedState:n.extension,selectedConnection:a,testConnection:a&&e.testConnection,timeoutMs:e.timeoutMs}),await this.directClient.init({savedState:n.direct,selectedConnection:r,testConnection:r&&e.testConnection,serverUrl:e.serverUrl}),this.notifyStateChange(),!e.testConnection&&this.isClientInitialized()&&!this.isServerReady()&&(this.logger.info("Triggering async server state refresh"),(this.isNotSetOrDirect()?this.directClient:this.extClient).init({testConnection:!0,selectedConnection:!0}).catch(s=>{this.logger.warn("Async server state refresh failed:",s)})),this.getState()}getState(){return this.isNotSetOrDirect()?this.directClient.getState():this.extClient.getState()}isClientInitialized(){return this.isNotSetOrDirect()?this.directClient.isClientInitialized():this.extClient.isClientInitialized()}isServerReady(){return this.isNotSetOrDirect()?this.directClient.isServerReady():this.extClient.isServerReady()}sendExtRequest(e,n){if(this.isNotSetOrDirect())throw new Error("sendExtRequest not available on direct connection");return this.extClient.sendExtRequest(e,n)}sendApiRequest(e,n,r,a,i){return this.isNotSetOrDirect()?this.directClient.sendApiRequest(e,n,r,a,i):this.extClient.sendApiRequest(e,n,r,a,i)}login(){return this.isNotSetOrDirect()?this.directClient.login():this.extClient.login()}logout(){return this.isNotSetOrDirect()?this.directClient.logout():this.extClient.logout()}getAuthState(){return this.isNotSetOrDirect()?this.directClient.getAuthState():this.extClient.getAuthState()}pingApi(){return this.isNotSetOrDirect()?this.directClient.pingApi():this.extClient.pingApi()}fetchModels(){return this.isNotSetOrDirect()?this.directClient.fetchModels():this.extClient.fetchModels()}async getServerState(){return this.isNotSetOrDirect()?this.directClient.getServerState():this.extClient.getServerState()}stream(e,n,r,a,i){return this.isNotSetOrDirect()?this.directClient.stream(e,n,r,a,i):this.extClient.stream(e,n,r,a,i)}streamChat(e,n,r){return this.isNotSetOrDirect()?this.directClient.streamChat(e,n,r):this.extClient.streamChat(e,n,r)}getConnectionMode(){return this.connectionMode}async setConnectionMode(e){if(this.logger.info("Switching connection mode to:",e),e==="direct"){if(!this.directClient.isClientInitialized())throw new Error(h.SERVER_NOT_READY.message)}else if(e==="extension"){if(!this.extClient.isClientInitialized())throw new Error(h.SERVER_NOT_READY.message)}else throw new Error("Invalid connection mode");return this.connectionMode=e,this.notifyStateChange(),this.getState()}async testExtensionConnectivity(e){const n=await this.extClient.init({testConnection:!0,timeoutMs:e});return this.notifyStateChange(),n}async testDirectConnectivity(e){const n=e??this.directClient.serialize().url;if(!n)return this.directClient.getState();const r=await this.directClient.init({serverUrl:n,testConnection:!0});return this.notifyStateChange(),r}async getExtensionState(){const e=this.extClient.getState();if(b(e))return e;throw new Error("extClient did not return an ExtensionState")}async getDirectState(){const e=this.directClient.getState();if(N(e))return e;throw new Error("directClient did not return a DirectState")}}const ze="production";exports.AUTH_EXT_NOT_INITIALIZED=pe;exports.BACKEND_SERVER_NOT_CONNECTED=E;exports.BACKEND_SERVER_NOT_REACHABLE=y;exports.BROWSER_CONFIGS=Ne;exports.BaseFacadeClient=Te;exports.BodhiClientUserPrefsManager=q;exports.CORE_BUILD_MODE=ze;exports.DIRECT_STATE_NOT_INITIALIZED=v;exports.DirectClientBase=Ie;exports.EXTENSION_STATE_NOT_FOUND=D;exports.EXTENSION_STATE_NOT_INITIALIZED=M;exports.Logger=V;exports.NOOP_STATE_CALLBACK=_;exports.OS_CONFIGS=_e;exports.OnboardingModal=Ce;exports.PENDING_EXTENSION_READY=k;exports.SERVER_ERROR_CODES=h;exports.STORAGE_PREFIXES=we;exports.backendServerNotReady=L;exports.base64UrlEncode=A;exports.buildError=F;exports.buildEvent=B;exports.buildResponse=j;exports.createApiError=te;exports.createDirectStateNotReachable=ae;exports.createDirectStateNotReady=ie;exports.createDirectStateReady=re;exports.createExtensionStateNotFound=le;exports.createExtensionStateNotInitialized=oe;exports.createOAuthEndpoints=$;exports.createOperationError=g;exports.createStorageKeys=Q;exports.createStoragePrefixWithBasePath=H;exports.detectBrowser=be;exports.detectOS=xe;exports.extractUserInfo=S;exports.generateCodeChallenge=Ae;exports.generateCodeVerifier=je;exports.getBackendServerState=ue;exports.getExtensionId=de;exports.getServerUrl=fe;exports.handleRequest=ve;exports.isApiResultError=ee;exports.isApiResultOperationError=R;exports.isApiResultSuccess=O;exports.isAuthError=he;exports.isAuthLoggedIn=ge;exports.isAuthLoggedOut=me;exports.isClientReady=ce;exports.isDirectClientReady=P;exports.isDirectServerReady=ne;exports.isDirectState=N;exports.isExtensionClientReady=U;exports.isExtensionServerReady=se;exports.isExtensionState=b;exports.isOperationError=J;exports.isServerReady=C;exports.isWebUIClient=ye;exports.parseJwt=W;exports.refreshAccessToken=G;exports.testServerConnectivity=Y;
@@ -8,7 +8,7 @@ function O(t) {
8
8
  function N(t) {
9
9
  return b(t) && "message" in t && typeof t.message == "string" && "type" in t && typeof t.type == "string";
10
10
  }
11
- function ne(t) {
11
+ function re(t) {
12
12
  return t instanceof Error && "error" in t && !("response" in t) && N(t.error);
13
13
  }
14
14
  function L(t) {
@@ -17,10 +17,10 @@ function L(t) {
17
17
  function P(t) {
18
18
  return "body" in t && "status" in t && typeof t.status == "number" && t.status >= 200 && t.status < 300;
19
19
  }
20
- function re(t) {
20
+ function ae(t) {
21
21
  return "body" in t && "status" in t && typeof t.status == "number" && t.status >= 400 && O(t.body);
22
22
  }
23
- const ae = (t, e, n, r) => {
23
+ const ie = (t, e, n, r) => {
24
24
  const a = new Error(t);
25
25
  return a.response = { status: e, body: n, headers: r }, a;
26
26
  }, v = (t, e) => {
@@ -59,7 +59,7 @@ function w(t) {
59
59
  function I(t) {
60
60
  return t.type === "direct";
61
61
  }
62
- function ie(t) {
62
+ function se(t) {
63
63
  return typeof t.server == "object" && t.server.status !== "not-connected" && A(t.server);
64
64
  }
65
65
  function D(t) {
@@ -69,13 +69,13 @@ const y = {
69
69
  type: "direct",
70
70
  server: j
71
71
  };
72
- function se(t, e = "unknown") {
72
+ function oe(t, e = "unknown") {
73
73
  return { type: "direct", server: { status: "ready", version: e }, url: t };
74
74
  }
75
- function oe(t) {
75
+ function le(t) {
76
76
  return { type: "direct", server: x, url: t };
77
77
  }
78
- function le(t, e) {
78
+ function ce(t, e) {
79
79
  return { type: "direct", server: e, url: t };
80
80
  }
81
81
  const U = {
@@ -93,38 +93,38 @@ function ue(t) {
93
93
  function B(t) {
94
94
  return t.extension === "ready" && "extensionId" in t;
95
95
  }
96
- function ce() {
96
+ function de() {
97
97
  return U;
98
98
  }
99
- function de() {
99
+ function fe() {
100
100
  return F;
101
101
  }
102
- function fe(t) {
102
+ function pe(t) {
103
103
  return w(t) ? B(t) : D(t);
104
104
  }
105
- function pe(t) {
105
+ function he(t) {
106
106
  return t.server;
107
107
  }
108
- function he(t) {
108
+ function me(t) {
109
109
  return w(t) && t.extension === "ready" ? t.extensionId : void 0;
110
110
  }
111
- function me(t) {
111
+ function ge(t) {
112
112
  return I(t) && "url" in t ? t.url : void 0;
113
113
  }
114
- const ge = {
114
+ const ve = {
115
115
  isLoggedIn: !1,
116
116
  error: {
117
117
  message: "Client not initialized",
118
118
  code: "CLIENT_NOT_INITIALIZED"
119
119
  }
120
120
  };
121
- function ve(t) {
121
+ function ye(t) {
122
122
  return typeof t == "object" && t !== null && "isLoggedIn" in t && t.isLoggedIn === !1 && "error" in t && typeof t.error == "object" && t.error !== null && "message" in t.error && typeof t.error.message == "string" && "code" in t.error && typeof t.error.code == "string";
123
123
  }
124
- function ye(t) {
124
+ function be(t) {
125
125
  return typeof t == "object" && t !== null && "isLoggedIn" in t && t.isLoggedIn === !1 && !("error" in t);
126
126
  }
127
- function be(t) {
127
+ function xe(t) {
128
128
  return typeof t == "object" && t !== null && "isLoggedIn" in t && t.isLoggedIn === !0 && "userInfo" in t && typeof t.userInfo == "object" && t.userInfo !== null && "sub" in t.userInfo && typeof t.userInfo.sub == "string" && "accessToken" in t && typeof t.accessToken == "string";
129
129
  }
130
130
  const z = () => {
@@ -138,7 +138,7 @@ function K(t, e, n) {
138
138
  function Q(t, e) {
139
139
  return { kind: "error", requestId: t, error: e };
140
140
  }
141
- function xe(t, e) {
141
+ function we(t, e) {
142
142
  const n = e[t.type];
143
143
  if (!n) return null;
144
144
  const r = n(t.payload);
@@ -175,10 +175,10 @@ class H {
175
175
  this.shouldLog("error") && console.error(`[${this.prefix}] ${this.ts()}`, ...e);
176
176
  }
177
177
  }
178
- function we(t) {
178
+ function Se(t) {
179
179
  return "handleOAuthCallback" in t;
180
180
  }
181
- function Se() {
181
+ function ke() {
182
182
  var a;
183
183
  const e = new C().getBrowser(), n = ((a = e.name) == null ? void 0 : a.toLowerCase()) || "", r = e.name || "Unknown Browser";
184
184
  if (n.includes("chrome"))
@@ -194,7 +194,7 @@ function Se() {
194
194
  return (!i || i.toLowerCase() === "unknown") && (i = "Unknown Browser"), { name: i, type: "unknown" };
195
195
  }
196
196
  }
197
- function ke() {
197
+ function Ee() {
198
198
  var r;
199
199
  const n = ((r = new C().getOS().name) == null ? void 0 : r.toLowerCase()) || "";
200
200
  return n.includes("mac") ? { name: "macOS", type: "macos" } : n.includes("windows") ? { name: "Windows", type: "windows" } : n.includes("linux") || n.includes("ubuntu") || n.includes("fedora") || n.includes("debian") ? { name: "Linux", type: "linux" } : { name: "Unknown", type: "unknown" };
@@ -209,12 +209,15 @@ function q(t) {
209
209
  RESOURCE_SCOPE: `${t}:resource_scope`
210
210
  };
211
211
  }
212
- const Ee = {
212
+ const Ce = {
213
213
  DIRECT: "bodhi:direct",
214
214
  WEB: "bodhi:web",
215
215
  EXT: "bodhi:ext"
216
216
  };
217
- class W {
217
+ function W(t, e) {
218
+ return `${t}:${e}`;
219
+ }
220
+ class $ {
218
221
  /**
219
222
  * @param storagePrefix - Prefix for localStorage keys (default: 'bodhijs:')
220
223
  */
@@ -291,10 +294,10 @@ class W {
291
294
  }
292
295
  }
293
296
  }
294
- function $(t) {
297
+ function Y(t) {
295
298
  return t.kind === "request";
296
299
  }
297
- const Y = {
300
+ const G = {
298
301
  // Modal lifecycle
299
302
  MODAL_READY: "modal:ready",
300
303
  MODAL_REFRESH: "modal:refresh",
@@ -309,7 +312,7 @@ const Y = {
309
312
  MODAL_SELECT_CONNECTION: "modal:select-connection",
310
313
  // Parent → Modal events
311
314
  PARENT_STATE_UPDATE: "parent:state-update"
312
- }, G = `<!doctype html>
315
+ }, X = `<!doctype html>
313
316
  <html lang="en">
314
317
  <head>
315
318
  <meta charset="UTF-8" />
@@ -378,7 +381,7 @@ var ne={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24
378
381
  </body>
379
382
  </html>
380
383
  `;
381
- class Ce {
384
+ class Ne {
382
385
  constructor(e) {
383
386
  this.overlayElement = null, this.iframeElement = null, this.isIframeReady = !1, this.currentSetupState = null, this.messageHandler = null, this.modalHtmlPath = e.modalHtmlPath ?? "src/sdk/core/onboarding/modal.html", this.handlers = e.handlers;
384
387
  }
@@ -398,7 +401,7 @@ class Ce {
398
401
  display: flex;
399
402
  align-items: center;
400
403
  justify-content: center;
401
- `, this.iframeElement = document.createElement("iframe"), this.iframeElement.setAttribute("data-testid", "iframe-setup"), typeof chrome < "u" && chrome.runtime && chrome.runtime.getURL ? (this.iframeElement.sandbox.add("allow-scripts", "allow-same-origin"), this.iframeElement.src = chrome.runtime.getURL(this.modalHtmlPath)) : (this.iframeElement.sandbox.add("allow-scripts"), this.iframeElement.srcdoc = G), this.iframeElement.style.cssText = `
404
+ `, this.iframeElement = document.createElement("iframe"), this.iframeElement.setAttribute("data-testid", "iframe-setup"), typeof chrome < "u" && chrome.runtime && chrome.runtime.getURL ? (this.iframeElement.sandbox.add("allow-scripts", "allow-same-origin"), this.iframeElement.src = chrome.runtime.getURL(this.modalHtmlPath)) : (this.iframeElement.sandbox.add("allow-scripts"), this.iframeElement.srcdoc = X), this.iframeElement.style.cssText = `
402
405
  width: 90%;
403
406
  max-width: 800px;
404
407
  height: 90%;
@@ -428,7 +431,7 @@ class Ce {
428
431
  async handleMessage(e) {
429
432
  var a, i, s, o, l;
430
433
  const n = e.data;
431
- if (!$(n) || e.source !== ((a = this.iframeElement) == null ? void 0 : a.contentWindow))
434
+ if (!Y(n) || e.source !== ((a = this.iframeElement) == null ? void 0 : a.contentWindow))
432
435
  return;
433
436
  const r = this.handlers[n.type];
434
437
  if (!r) {
@@ -436,16 +439,16 @@ class Ce {
436
439
  return;
437
440
  }
438
441
  try {
439
- n.type === Y.MODAL_READY && (this.isIframeReady = !0);
440
- const u = await r(n), c = K(n.requestId, n.type, u);
441
- (s = (i = this.iframeElement) == null ? void 0 : i.contentWindow) == null || s.postMessage(c, "*");
442
- } catch (u) {
443
- console.error("[OnboardingModal] Handler error:", u);
444
- const c = Q(n.requestId, {
442
+ n.type === G.MODAL_READY && (this.isIframeReady = !0);
443
+ const c = await r(n), u = K(n.requestId, n.type, c);
444
+ (s = (i = this.iframeElement) == null ? void 0 : i.contentWindow) == null || s.postMessage(u, "*");
445
+ } catch (c) {
446
+ console.error("[OnboardingModal] Handler error:", c);
447
+ const u = Q(n.requestId, {
445
448
  code: "handler-error",
446
- message: u instanceof Error ? u.message : "Handler execution failed"
449
+ message: c instanceof Error ? c.message : "Handler execution failed"
447
450
  });
448
- (l = (o = this.iframeElement) == null ? void 0 : o.contentWindow) == null || l.postMessage(c, "*");
451
+ (l = (o = this.iframeElement) == null ? void 0 : o.contentWindow) == null || l.postMessage(u, "*");
449
452
  }
450
453
  }
451
454
  /**
@@ -461,7 +464,7 @@ class Ce {
461
464
  (n = this.iframeElement.contentWindow) == null || n.postMessage(e, "*");
462
465
  }
463
466
  }
464
- const Ne = [
467
+ const _e = [
465
468
  {
466
469
  id: "chrome",
467
470
  status: "supported",
@@ -492,7 +495,7 @@ const Ne = [
492
495
  name: "Unknown Browser",
493
496
  github_issue_url: "https://github.com/BodhiSearch/bodhi-browser/issues/new"
494
497
  }
495
- ], _e = [
498
+ ], je = [
496
499
  {
497
500
  id: "macos",
498
501
  status: "supported",
@@ -521,22 +524,22 @@ const Ne = [
521
524
  function T(t) {
522
525
  return btoa(String.fromCharCode(...new Uint8Array(t))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
523
526
  }
524
- function je() {
527
+ function Ae() {
525
528
  const t = new Uint8Array(32);
526
529
  return crypto.getRandomValues(t), T(t.buffer);
527
530
  }
528
- async function Ae(t) {
531
+ async function Ie(t) {
529
532
  const n = new TextEncoder().encode(t), r = await crypto.subtle.digest("SHA-256", n);
530
533
  return T(r);
531
534
  }
532
- function X(t) {
535
+ function Z(t) {
533
536
  const n = t.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"), r = decodeURIComponent(
534
537
  atob(n).split("").map((a) => "%" + ("00" + a.charCodeAt(0).toString(16)).slice(-2)).join("")
535
538
  );
536
539
  return JSON.parse(r);
537
540
  }
538
541
  function E(t) {
539
- const e = X(t);
542
+ const e = Z(t);
540
543
  return {
541
544
  sub: e.sub,
542
545
  email: e.email,
@@ -546,14 +549,14 @@ function E(t) {
546
549
  preferred_username: e.preferred_username
547
550
  };
548
551
  }
549
- function Z(t) {
552
+ function J(t) {
550
553
  return {
551
554
  authorize: `${t}/protocol/openid-connect/auth`,
552
555
  token: `${t}/protocol/openid-connect/token`,
553
556
  revoke: `${t}/protocol/openid-connect/revoke`
554
557
  };
555
558
  }
556
- async function J(t, e, n) {
559
+ async function ee(t, e, n) {
557
560
  try {
558
561
  const r = await fetch(t, {
559
562
  method: "POST",
@@ -577,7 +580,7 @@ async function J(t, e, n) {
577
580
  return null;
578
581
  }
579
582
  }
580
- async function ee(t) {
583
+ async function te(t) {
581
584
  const e = `${t}/bodhi/v1/info`;
582
585
  try {
583
586
  const n = new AbortController(), r = setTimeout(() => n.abort(), 5e3), a = await fetch(e, {
@@ -612,9 +615,9 @@ async function ee(t) {
612
615
  };
613
616
  }
614
617
  }
615
- class Ie {
618
+ class ze {
616
619
  constructor(e, n, r) {
617
- this.serverUrl = null, this.state = y, this.refreshPromise = null, this.logger = new H(n, e.logLevel), this.authClientId = e.authClientId, this.authServerUrl = e.authServerUrl || "https://id.getbodhi.app/realms/bodhi", this.userScope = e.userScope || "scope_user_user", this.authEndpoints = Z(this.authServerUrl), this.storageKeys = q(e.storagePrefix), this.onStateChange = r ?? z;
620
+ this.serverUrl = null, this.state = y, this.refreshPromise = null, this.logger = new H(n, e.logLevel), this.authClientId = e.authClientId, this.authServerUrl = e.authServerUrl || "https://id.getbodhi.app/realms/bodhi", this.userScope = e.userScope || "scope_user_user", this.authEndpoints = J(this.authServerUrl), this.storageKeys = q(e.storagePrefix), this.onStateChange = r ?? z;
618
621
  }
619
622
  /**
620
623
  * Set client state and notify callback
@@ -689,27 +692,27 @@ class Ie {
689
692
  const s = `${this.serverUrl}${n}`;
690
693
  this.logger.debug(`${e} ${s}`);
691
694
  try {
692
- const o = new AbortController(), l = setTimeout(() => o.abort(), 5e3), u = {
695
+ const o = new AbortController(), l = setTimeout(() => o.abort(), 5e3), c = {
693
696
  "Content-Type": "application/json",
694
697
  ...a
695
698
  };
696
699
  if (i) {
697
700
  const f = await this._getAccessTokenRaw();
698
- f && (u.Authorization = `Bearer ${f}`);
701
+ f && (c.Authorization = `Bearer ${f}`);
699
702
  }
700
- const c = await fetch(s, {
703
+ const u = await fetch(s, {
701
704
  method: e,
702
- headers: u,
705
+ headers: c,
703
706
  body: r ? JSON.stringify(r) : void 0,
704
707
  signal: o.signal
705
708
  });
706
709
  clearTimeout(l);
707
710
  const d = {};
708
- return c.headers.forEach((f, h) => {
711
+ return u.headers.forEach((f, h) => {
709
712
  d[h] = f;
710
713
  }), {
711
- body: await c.json(),
712
- status: c.status,
714
+ body: await u.json(),
715
+ status: u.status,
713
716
  headers: d
714
717
  };
715
718
  } catch (o) {
@@ -793,13 +796,13 @@ class Ie {
793
796
  throw new Error(`HTTP ${l.status}: ${await l.text()}`);
794
797
  if (!l.body)
795
798
  throw new Error("Response body is null");
796
- const u = l.body.getReader(), c = new TextDecoder();
799
+ const c = l.body.getReader(), u = new TextDecoder();
797
800
  let d = "";
798
801
  try {
799
802
  for (; ; ) {
800
- const { done: p, value: f } = await u.read();
803
+ const { done: p, value: f } = await c.read();
801
804
  if (p) break;
802
- d += c.decode(f, { stream: !0 });
805
+ d += u.decode(f, { stream: !0 });
803
806
  const h = d.split(`
804
807
  `);
805
808
  d = h.pop() || "";
@@ -816,7 +819,7 @@ class Ie {
816
819
  }
817
820
  }
818
821
  } finally {
819
- u.releaseLock();
822
+ c.releaseLock();
820
823
  }
821
824
  }
822
825
  async *streamChat(e, n, r = !0) {
@@ -839,7 +842,7 @@ class Ie {
839
842
  * Test connectivity to local server
840
843
  */
841
844
  async testConnectivity() {
842
- return this.ensureInitialized(), this.logger.debug("Testing connectivity to:", this.serverUrl), ee(this.serverUrl);
845
+ return this.ensureInitialized(), this.logger.debug("Testing connectivity to:", this.serverUrl), te(this.serverUrl);
843
846
  }
844
847
  // ============================================================================
845
848
  // Serialization
@@ -900,7 +903,7 @@ class Ie {
900
903
  async _doRefreshToken(e) {
901
904
  this.logger.debug("Refreshing access token");
902
905
  try {
903
- const n = await J(
906
+ const n = await ee(
904
907
  this.authEndpoints.token,
905
908
  e,
906
909
  this.authClientId
@@ -1003,15 +1006,20 @@ class Ie {
1003
1006
  ]);
1004
1007
  }
1005
1008
  }
1006
- class ze {
1007
- constructor(e, n, r, a) {
1008
- this.connectionMode = null, this.authClientId = e, this.config = n, this.onStateChange = r ?? z, this.prefs = new W(a), this.logger = this.createLogger(n), this.extClient = this.createExtClient(
1009
+ class Ke {
1010
+ constructor(e, n, r, a, i) {
1011
+ this.connectionMode = null, this.authClientId = e, this.config = n, this.onStateChange = r ?? z;
1012
+ const s = W(
1013
+ i || "/",
1014
+ a || "bodhijs:"
1015
+ );
1016
+ this.prefs = new $(s), this.logger = this.createLogger(n), this.extClient = this.createExtClient(
1009
1017
  n,
1010
- (i) => this.handleInternalStateChange(i)
1018
+ (o) => this.handleInternalStateChange(o)
1011
1019
  ), this.directClient = this.createDirectClient(
1012
1020
  e,
1013
1021
  n,
1014
- (i) => this.handleInternalStateChange(i)
1022
+ (o) => this.handleInternalStateChange(o)
1015
1023
  );
1016
1024
  }
1017
1025
  // ============================================================================
@@ -1269,64 +1277,67 @@ class ze {
1269
1277
  throw new Error("directClient did not return a DirectState");
1270
1278
  }
1271
1279
  }
1280
+ const Te = "production";
1272
1281
  export {
1273
- ge as AUTH_EXT_NOT_INITIALIZED,
1282
+ ve as AUTH_EXT_NOT_INITIALIZED,
1274
1283
  j as BACKEND_SERVER_NOT_CONNECTED,
1275
1284
  x as BACKEND_SERVER_NOT_REACHABLE,
1276
- Ne as BROWSER_CONFIGS,
1277
- ze as BaseFacadeClient,
1278
- W as BodhiClientUserPrefsManager,
1285
+ _e as BROWSER_CONFIGS,
1286
+ Ke as BaseFacadeClient,
1287
+ $ as BodhiClientUserPrefsManager,
1288
+ Te as CORE_BUILD_MODE,
1279
1289
  y as DIRECT_STATE_NOT_INITIALIZED,
1280
- Ie as DirectClientBase,
1290
+ ze as DirectClientBase,
1281
1291
  F as EXTENSION_STATE_NOT_FOUND,
1282
1292
  U as EXTENSION_STATE_NOT_INITIALIZED,
1283
1293
  H as Logger,
1284
1294
  z as NOOP_STATE_CALLBACK,
1285
- _e as OS_CONFIGS,
1286
- Ce as OnboardingModal,
1295
+ je as OS_CONFIGS,
1296
+ Ne as OnboardingModal,
1287
1297
  _ as PENDING_EXTENSION_READY,
1288
1298
  m as SERVER_ERROR_CODES,
1289
- Ee as STORAGE_PREFIXES,
1299
+ Ce as STORAGE_PREFIXES,
1290
1300
  M as backendServerNotReady,
1291
1301
  T as base64UrlEncode,
1292
1302
  Q as buildError,
1293
1303
  V as buildEvent,
1294
1304
  K as buildResponse,
1295
- ae as createApiError,
1296
- oe as createDirectStateNotReachable,
1297
- le as createDirectStateNotReady,
1298
- se as createDirectStateReady,
1299
- de as createExtensionStateNotFound,
1300
- ce as createExtensionStateNotInitialized,
1301
- Z as createOAuthEndpoints,
1305
+ ie as createApiError,
1306
+ le as createDirectStateNotReachable,
1307
+ ce as createDirectStateNotReady,
1308
+ oe as createDirectStateReady,
1309
+ fe as createExtensionStateNotFound,
1310
+ de as createExtensionStateNotInitialized,
1311
+ J as createOAuthEndpoints,
1302
1312
  v as createOperationError,
1303
1313
  q as createStorageKeys,
1304
- Se as detectBrowser,
1305
- ke as detectOS,
1314
+ W as createStoragePrefixWithBasePath,
1315
+ ke as detectBrowser,
1316
+ Ee as detectOS,
1306
1317
  E as extractUserInfo,
1307
- Ae as generateCodeChallenge,
1308
- je as generateCodeVerifier,
1309
- pe as getBackendServerState,
1310
- he as getExtensionId,
1311
- me as getServerUrl,
1312
- xe as handleRequest,
1313
- re as isApiResultError,
1318
+ Ie as generateCodeChallenge,
1319
+ Ae as generateCodeVerifier,
1320
+ he as getBackendServerState,
1321
+ me as getExtensionId,
1322
+ ge as getServerUrl,
1323
+ we as handleRequest,
1324
+ ae as isApiResultError,
1314
1325
  L as isApiResultOperationError,
1315
1326
  P as isApiResultSuccess,
1316
- ve as isAuthError,
1317
- be as isAuthLoggedIn,
1318
- ye as isAuthLoggedOut,
1319
- fe as isClientReady,
1327
+ ye as isAuthError,
1328
+ xe as isAuthLoggedIn,
1329
+ be as isAuthLoggedOut,
1330
+ pe as isClientReady,
1320
1331
  D as isDirectClientReady,
1321
- ie as isDirectServerReady,
1332
+ se as isDirectServerReady,
1322
1333
  I as isDirectState,
1323
1334
  B as isExtensionClientReady,
1324
1335
  ue as isExtensionServerReady,
1325
1336
  w as isExtensionState,
1326
- ne as isOperationError,
1337
+ re as isOperationError,
1327
1338
  A as isServerReady,
1328
- we as isWebUIClient,
1329
- X as parseJwt,
1330
- J as refreshAccessToken,
1331
- ee as testServerConnectivity
1339
+ Se as isWebUIClient,
1340
+ Z as parseJwt,
1341
+ ee as refreshAccessToken,
1342
+ te as testServerConnectivity
1332
1343
  };
@@ -0,0 +1 @@
1
+ export declare const BUILD_MODE: string;
@@ -20,7 +20,7 @@ export declare abstract class BaseFacadeClient<TConfig, TExtClient extends IConn
20
20
  protected authClientId: string;
21
21
  protected config: TConfig;
22
22
  protected onStateChange: StateChangeCallback;
23
- constructor(authClientId: string, config: TConfig, onStateChange?: StateChangeCallback, storagePrefix?: string);
23
+ constructor(authClientId: string, config: TConfig, onStateChange?: StateChangeCallback, storagePrefix?: string, basePath?: string);
24
24
  /**
25
25
  * Create logger instance
26
26
  * Subclasses extract logLevel from their specific config type
@@ -17,3 +17,4 @@ export * from './oauth';
17
17
  export * from './direct-client-base';
18
18
  export * from './facade-client-base';
19
19
  export { isOperationError, type OperationError } from '../../../bodhi-browser-ext/src/types';
20
+ export { BUILD_MODE as CORE_BUILD_MODE } from './build-info';
@@ -27,6 +27,18 @@ export declare const STORAGE_PREFIXES: {
27
27
  readonly WEB: "bodhi:web";
28
28
  readonly EXT: "bodhi:ext";
29
29
  };
30
+ /**
31
+ * Create storage prefix with basePath for path isolation
32
+ *
33
+ * @param basePath - Base path of app (e.g., '/', '/app1/')
34
+ * @param prefix - Storage prefix (e.g., 'bodhi:web', 'bodhijs:')
35
+ * @returns Combined prefix with basePath isolation
36
+ *
37
+ * Examples:
38
+ * - createStoragePrefixWithBasePath('/', 'bodhi:web') => '/:bodhi:web'
39
+ * - createStoragePrefixWithBasePath('/app1/', 'bodhi:web') => '/app1/:bodhi:web'
40
+ */
41
+ export declare function createStoragePrefixWithBasePath(basePath: string, prefix: string): string;
30
42
  /**
31
43
  * User Preferences Storage Manager
32
44
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bodhiapp/bodhi-js-core",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Core types and interfaces for Bodhi Browser SDK",
5
5
  "type": "module",
6
6
  "main": "dist/bodhi-core.cjs.js",