@dypai-ai/client-sdk 1.10.4 → 1.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +40 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/dist/react/index.esm.js +1 -1
- package/dist/react/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@dypai-ai/client-sdk` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.10.6] - 2026-05-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `useChat` now supports the Vercel AI SDK UI Message Stream protocol (`x-vercel-ai-ui-message-stream: v1`) used by DYPAI agent streaming endpoints.
|
|
10
|
+
- Preserved compatibility with legacy Vercel data streams and the previous DYPAI plain-text stream fallback.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- `useChat` now keeps streamed tool calls and tool results in `message.parts` while still accumulating assistant text in `message.content`.
|
|
15
|
+
|
|
16
|
+
## [1.10.5] - 2026-05-10
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Smart Upload now keeps `confirm` and `client_upload` SDK-owned even if app params include those keys by mistake. The prepare request always sends `confirm:false` and the confirm request always sends `confirm:true`.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Clarified file upload docs: browser code should pass only domain params to `dypai.api.upload(...)`; `confirm` and `client_upload` are internal Smart Upload flags.
|
|
25
|
+
|
|
5
26
|
## [1.10.3] - 2026-05-07
|
|
6
27
|
|
|
7
28
|
### Added
|
package/README.md
CHANGED
|
@@ -303,6 +303,8 @@ const { data, error } = await dypai.api.upload('storage_files', file, {
|
|
|
303
303
|
params: {
|
|
304
304
|
operation: 'upload',
|
|
305
305
|
file_path: `avatars/${userId}.jpg`,
|
|
306
|
+
// Domain params are fine here, e.g. task_id/file_name/content_type.
|
|
307
|
+
// Do not pass confirm or client_upload; the SDK controls Smart Upload.
|
|
306
308
|
},
|
|
307
309
|
onProgress: (percent) => {
|
|
308
310
|
console.log(`Upload: ${percent}%`);
|
|
@@ -310,6 +312,8 @@ const { data, error } = await dypai.api.upload('storage_files', file, {
|
|
|
310
312
|
});
|
|
311
313
|
```
|
|
312
314
|
|
|
315
|
+
`confirm` and `client_upload` are internal Smart Upload flags. They may exist in the endpoint input schema, but browser code should not include them in `params`.
|
|
316
|
+
|
|
313
317
|
### Download
|
|
314
318
|
|
|
315
319
|
```ts
|
|
@@ -598,6 +602,42 @@ await patchSettings({ theme: 'dark' });
|
|
|
598
602
|
await deleteItem({ id: '123' });
|
|
599
603
|
```
|
|
600
604
|
|
|
605
|
+
### useChat
|
|
606
|
+
|
|
607
|
+
```tsx
|
|
608
|
+
import { useChat } from '@dypai-ai/client-sdk/react';
|
|
609
|
+
|
|
610
|
+
function SupportChat() {
|
|
611
|
+
const { messages, input, setInput, sendMessage, isLoading, stop } = useChat('support_agent');
|
|
612
|
+
|
|
613
|
+
return (
|
|
614
|
+
<section>
|
|
615
|
+
{messages.map(message => (
|
|
616
|
+
<article key={message.id}>
|
|
617
|
+
<strong>{message.role}</strong>
|
|
618
|
+
<p>{message.content}</p>
|
|
619
|
+
{message.parts?.map((part, index) => {
|
|
620
|
+
if (part.type === 'tool-call') {
|
|
621
|
+
return <small key={index}>Calling {part.toolName}...</small>;
|
|
622
|
+
}
|
|
623
|
+
if (part.type === 'tool-result') {
|
|
624
|
+
return <small key={index}>{part.toolName} finished</small>;
|
|
625
|
+
}
|
|
626
|
+
return null;
|
|
627
|
+
})}
|
|
628
|
+
</article>
|
|
629
|
+
))}
|
|
630
|
+
|
|
631
|
+
<input value={input} onChange={event => setInput(event.target.value)} />
|
|
632
|
+
<button onClick={() => sendMessage()} disabled={isLoading}>Send</button>
|
|
633
|
+
{isLoading && <button onClick={stop}>Stop</button>}
|
|
634
|
+
</section>
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
`useChat` supports DYPAI agent streams that use the Vercel AI SDK UI Message Stream protocol, plus legacy DYPAI text streams for older engines. Assistant text is accumulated in `message.content`; streamed tool calls and tool results are exposed in `message.parts`.
|
|
640
|
+
|
|
601
641
|
### useUpload
|
|
602
642
|
|
|
603
643
|
```tsx
|
package/dist/index.d.ts
CHANGED
|
@@ -1515,7 +1515,7 @@ declare const toastInfo: (title: string, description?: string) => void;
|
|
|
1515
1515
|
|
|
1516
1516
|
declare const PACKAGE_INFO: {
|
|
1517
1517
|
readonly name: "@dypai-ai/client-sdk";
|
|
1518
|
-
readonly version: "1.10.
|
|
1518
|
+
readonly version: "1.10.5";
|
|
1519
1519
|
readonly description: "Official JavaScript/TypeScript SDK for DYPAI";
|
|
1520
1520
|
readonly features: readonly ["Authentication (email, OAuth, OTP)", "Database CRUD", "Direct database access (service role)", "Custom endpoints (typed API)", "File upload/download (Smart Upload)", "User management (admin)", "React hooks (useAuth, useEndpoint, useAction, useUpload)"];
|
|
1521
1521
|
};
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class t extends Error{constructor(t,e=500,i,s){super(t),this.status=e,this.code=i,this.details=s,this.name="DypaiError"}}function e(t){if(!t)return{};const e=t.user||t,i=e.name||e.user_metadata?.full_name||null,s=e.emailVerified??e.confirmed_at??null,n=e.app_metadata||{},r=e.user_metadata||{full_name:i};return{id:e.id,email:e.email,phone:e.phone||null,role:n.role||r.role||e.role||"authenticated",created_at:e.createdAt||e.created_at,updated_at:e.updatedAt||e.updated_at,confirmed_at:s?"boolean"==typeof s?(new Date).toISOString():s:null,last_sign_in_at:e.last_sign_in_at||null,app_metadata:n,user_metadata:{...r,full_name:i},roleDetails:{name:n.role||r.role||null,weight:0},appContext:{app_id:"default"}}}function i(e){return e instanceof t}class s extends t{constructor(t="Auth session missing"){super(t,400,"session_missing"),this.name="AuthSessionMissingError"}}function n(t){return t instanceof s}class r extends t{constructor(t="Invalid login credentials"){super(t,400,"invalid_credentials"),this.name="AuthInvalidCredentialsError"}}function o(t){return t instanceof r}class a extends t{constructor(t="Invalid or expired token"){super(t,401,"invalid_token"),this.name="AuthInvalidTokenError"}}function c(t){return t instanceof a}class h extends t{constructor(t="Password is too weak",e=[]){super(t,400,"weak_password"),this.name="AuthWeakPasswordError",this.reasons=e}}function l(t){return t instanceof h}class u extends t{constructor(t="Email not verified"){super(t,403,"email_not_verified"),this.name="AuthEmailNotVerifiedError"}}function d(t){return t instanceof u}class f extends t{constructor(t="User already exists"){super(t,409,"user_already_exists"),this.name="AuthUserAlreadyExistsError"}}function p(t){return t instanceof f}class w extends t{constructor(t,e=0){super(t,e,"retryable_fetch"),this.name="RetryableFetchError"}}function y(t){return t instanceof w}function m(t){return 0===t||408===t||429===t||t>=500&&t<600}function v(e,i,n){const o=(e||"").toLowerCase(),c=(n||"").toLowerCase();return c.includes("invalid_token")||c.includes("invalid_grant")||o.includes("invalid token")||o.includes("expired")||o.includes("revoked")?new a(e):c.includes("session_missing")||o.includes("no session")||o.includes("session missing")?new s(e):c.includes("invalid_credentials")||c.includes("invalid_login")||o.includes("invalid credentials")||o.includes("invalid login")?new r(e):c.includes("email_not_verified")||o.includes("email not verified")?new u(e):c.includes("user_already_exists")||c.includes("user_exists")||o.includes("already exists")||o.includes("already registered")?new f(e):c.includes("weak_password")||o.includes("password is too weak")||o.includes("password too short")?new h(e):m(i)?new w(e,i):new t(e,i,n)}let S=!1;function g(t,...e){"error"===t?console.error(...e):S&&("warn"===t?console.warn(...e):console.log(...e))}async function k(t,e){let i={};try{i=await t.json()}catch{}const s=i.msg||i.error_description||i.message||e,n=i.error_code||i.error||i.code||void 0;return v(s,t.status,n)}class b{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const D={getItem:t=>"undefined"==typeof window?null:window.localStorage.getItem(t),setItem:(t,e)=>{if("undefined"!=typeof window)try{window.localStorage.setItem(t,e)}catch(t){console.error("[DYPAI SDK] ❌ Error crítico guardando en localStorage (¿Quota/Permisos?):",t)}},removeItem:t=>{if("undefined"!=typeof window)try{window.localStorage.removeItem(t)}catch(t){}}};async function _(e){try{return{data:await e,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e.message||"Error de autenticación",e.status||400)}}}class A{constructor(t,e=null){this.config=t,this.t=null,this.i=null,this.o=null,this.h=null,this.l=null,this.u=[],this.p=null,this.m=null,this.v=!1,this.S=null,this.k=null,this.D=0,this._=0,this.A=null,this.storage=t.storage||D,S=!!t.debug;const i=t.storageKey||this.deriveStorageKey(t.apiKey);if(g("log",`[DYPAI SDK] 🛠️ Inicializando AuthModule (storageKey: ${i})`),this.STORAGE_KEY=`dypai-${i}-auth-session`,this.P=this.T().then(()=>{this.i&&(this.getUser().catch(()=>{}),this.startAutoRefresh());const t=this.m;this.m=null,t?setTimeout(()=>{this.v=!0,"PASSWORD_RECOVERY"!==t&&"AUTH_ERROR"!==t&&this.I(t)},0):this.v=!0}),"undefined"!=typeof window){if(window.addEventListener("visibilitychange",this.$.bind(this)),window.addEventListener("focus",this.$.bind(this)),"undefined"!=typeof BroadcastChannel)try{this.A=new BroadcastChannel(`bc:${this.STORAGE_KEY}`),this.A.addEventListener("message",t=>{const e=t.data;e&&"object"==typeof e&&(g("log",`[DYPAI SDK] 📡 BroadcastChannel: ${e.type}`),"SIGNED_OUT"===e.type?(this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh(),this.I("SIGNED_OUT")):"SESSION_UPDATED"===e.type&&this.T(!1))})}catch(t){g("warn","[DYPAI SDK] BroadcastChannel no disponible, usando storage event:",t)}window.addEventListener("storage",t=>{t.key===this.STORAGE_KEY&&(g("log","[DYPAI SDK] 🔄 Sesión actualizada (storage event). Sincronizando..."),this.T(!1))})}}O(t){return 0===t||408===t||429===t||t>=500&&t<600}R(t){if(this.A)try{this.A.postMessage({type:t,timestamp:Date.now()})}catch{}}async $(){"undefined"!=typeof document&&"visible"===document.visibilityState&&(g("log","[DYPAI SDK] 👁️ Ventana visible. Sincronizando estado y reiniciando auto-refresh..."),await this.T(!0),this.i&&this.startAutoRefresh())}deriveStorageKey(t){return t?t.substring(0,8):"default"}getBaseUrl(){return this.config.baseUrl||"http://localhost:8000"}U(){if("undefined"==typeof window)return;const t=new URLSearchParams(window.location.search);["token","type","error","error_code","error_description","access_token","refresh_token","expires_in"].forEach(e=>t.delete(e));const e=t.toString(),i=`${window.location.pathname}${e?`?${e}`:""}`;window.history.replaceState({},document.title,i)}N(t){try{if(!t)return;sessionStorage.setItem(A.CALLBACK_TYPE_STORAGE_KEY,t),"recovery"===t||"invite"===t?sessionStorage.setItem(A.CALLBACK_REDIRECT_STORAGE_KEY,"PASSWORD_RECOVERY"):sessionStorage.removeItem(A.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}K(t){try{sessionStorage.setItem(A.RESET_TOKEN_STORAGE_KEY,t)}catch{}}C(){try{return sessionStorage.getItem(A.RESET_TOKEN_STORAGE_KEY)}catch{return null}}Y(){try{sessionStorage.removeItem(A.RESET_TOKEN_STORAGE_KEY)}catch{}}L(){try{sessionStorage.removeItem(A.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(A.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}j(t){if("undefined"==typeof window)return t;try{return new URL(t,window.location.origin).toString()}catch{return t}}B(t){c(t)&&(this.Y(),this.L(),this.p={code:t.code||"invalid_token",message:t.message||"The reset link is invalid or has expired"},this.v&&this.I("AUTH_ERROR"))}get user(){return this.t}get token(){return this.i}get lastError(){return this.p}get hasPasswordRecoveryToken(){return!!this.C()}consumeCallbackType(){try{const t=sessionStorage.getItem(A.CALLBACK_TYPE_STORAGE_KEY);return sessionStorage.removeItem(A.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(A.CALLBACK_REDIRECT_STORAGE_KEY),t}catch{return null}}get isPasswordRecoveryCallback(){try{return"PASSWORD_RECOVERY"===sessionStorage.getItem(A.CALLBACK_REDIRECT_STORAGE_KEY)||!!sessionStorage.getItem(A.RESET_TOKEN_STORAGE_KEY)}catch{return!1}}isLoggedIn(){return!(!this.i||!this.t)}onAuthStateChange(t){return g("log","[DYPAI SDK] 👂 Nuevo suscriptor añadido a onAuthStateChange"),this.u.push(t),this.P.then(()=>{const e=this.J();g("log",`[DYPAI SDK] 📣 INITIAL_SESSION para suscriptor (Sesión activa: ${!!e})`),t("INITIAL_SESSION",e),this.isPasswordRecoveryCallback?t("PASSWORD_RECOVERY",e):this.p&&t("AUTH_ERROR",null)}).catch(e=>{g("error","[DYPAI SDK] ❌ Error esperando recuperación de sesión para suscriptor:",e),t("INITIAL_SESSION",null)}),{data:{subscription:{unsubscribe:()=>{this.u=this.u.filter(e=>e!==t)}}}}}async signInWithPassword(t){return _((async()=>{const e=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-in/email`,i={email:t.email||t.identifier||"",password:t.password},s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(i)});if(!s.ok)throw await k(s,"Login failed");const n=await s.json(),r=this.W(n);return await this.q(r),r})())}async login(t){return this.signInWithPassword(t)}async signUp(t,e){return _((async()=>{const i=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-up/email`,{email:s,phone:n,password:r,user_data:o,...a}=t,c=s||"",h=a.full_name||a.name||c.split("@")[0],l={...a,...o||{}},u={email:c,password:r,name:h};Object.keys(l).length>0&&Object.assign(u,l),e?.redirectTo?u.callbackURL=e.redirectTo:"undefined"!=typeof window&&(u.callbackURL=window.location.href.split("#")[0]);const d=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(u)});if(!d.ok)throw await k(d,"Registration failed");const f=await d.json(),p=this.W(f),w={...p,confirmationRequired:!p.token};return w.token?await this.q(w):g("log","[DYPAI SDK] 📧 Signup exitoso, se requiere confirmación de email."),w})())}async register(t){return this.signUp(t)}async getSession(){try{if(await this.P,!this.i||!this.t)return{data:null,error:null};const t=Math.floor(Date.now()/1e3);return(this.l||0)-t<30+A.CLOCK_SKEW_TOLERANCE_S&&(this.h||this.o)&&(g("log","[DYPAI SDK] ⏳ getSession: Token próximo a expirar. Refrescando..."),await this.refreshSession().catch(t=>g("warn","[DYPAI SDK] Error refreshing session in getSession:",t))),{data:{access_token:this.i,refresh_token:this.h||void 0,token_type:"bearer",user:this.t},error:null}}catch(e){return{data:null,error:new t(e.message,500)}}}async getUser(){return _((async()=>{await this.P;const e=this.o||this.i;if(!e)throw new s("No hay sesión activa");const i=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,n=await fetch(i,{headers:{Authorization:`Bearer ${e}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!n.ok)throw new t("Session invalid",n.status);const r=await n.json(),o=r?.jwt||n.headers.get("set-auth-jwt");o&&(this.i=o);const a=this.M(r);return this.G(a),a})())}async me(){return this.getUser()}async signInWithOAuth(e,i={}){return _((async()=>{if("undefined"==typeof window)throw new t("signInWithOAuth requiere un entorno de navegador (window no está disponible)",400);const{redirectTo:s=window.location.href,scopes:n}=i,r=this.config.baseUrl||"http://localhost:8000",o=new URLSearchParams({provider:e,callbackURL:s});n?.length&&o.set("scopes",n.join(" "));const a=`${r}/api/v0/auth/sign-in/social?${o.toString()}`;window.location.href=a})())}async signOut(){return _((async()=>{try{const t=this.o||this.i;if(t){const e=this.config.baseUrl||"http://localhost:8000";await fetch(`${e}/api/v0/auth/sign-out`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}})}}finally{this.F("signOut called")}})())}async logout(){return this.signOut()}async resetPasswordForEmail(t,e){return _((async()=>{const i=this.config.baseUrl||"http://localhost:8000",s={email:t};e?.redirectTo?s.redirectTo=e.redirectTo:this.config.passwordRecoveryRedirect&&"undefined"!=typeof window?s.redirectTo=this.j(this.config.passwordRecoveryRedirect):"undefined"!=typeof window&&(s.redirectTo=window.location.href.split("#")[0]);const n=await fetch(`${i}/api/v0/auth/request-password-reset`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(s)});if(!n.ok)throw await k(n,"Recovery failed");return await n.json()})())}async recoverPassword(t){return this.resetPasswordForEmail(t.email)}async resendConfirmationEmail(t,e){return _((async()=>{const i=this.config.baseUrl||"http://localhost:8000",s={email:t};e?.redirectTo?s.callbackURL=e.redirectTo:"undefined"!=typeof window&&(s.callbackURL=window.location.href.split("#")[0]);const n=await fetch(`${i}/api/v0/auth/send-verification-email`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(s)});if(!n.ok)throw await k(n,"Failed to resend confirmation email");return{message:"Confirmation email resent"}})())}async refreshSession(){if(this.S)return g("log","[DYPAI SDK] 🔄 Refresco de sesión ya en curso. Esperando resolución..."),this.S.promise;const e=new b;this.S=e;const i=`lock:${this.STORAGE_KEY}`;return(async()=>{try{const t=await async function(t,e,i){if("undefined"!=typeof globalThis&&globalThis.navigator?.locks?.request){const s=new AbortController,n=setTimeout(()=>s.abort(),e);try{return await globalThis.navigator.locks.request(t,{signal:s.signal},async()=>(clearTimeout(n),await i()))}catch(s){if("AbortError"===s.name)return console.warn(`[DYPAI SDK] ⚠️ Web Lock "${t}" timeout (${e}ms). Proceeding without lock.`),await i();throw s}}return await i()}(i,A.LOCK_ACQUIRE_TIMEOUT_MS,()=>this.V());e.resolve(t)}catch(i){const s=i instanceof t?i:new t(i.message||"Error refrescando sesión",401);e.resolve({data:null,error:s})}finally{this.S=null}})(),e.promise}async V(){try{const e=await this.storage.getItem(this.STORAGE_KEY);if(e){const t=JSON.parse(e),i=t.expires_at||0,s=Math.floor(Date.now()/1e3);if(t.access_token!==this.i&&i-s>60)return g("log","[DYPAI SDK] 🔄 Otra pestaña ya refrescó el token. Adoptando sesión del storage."),this.i=t.access_token,this.o=t.session_token||t.access_token,this.h=t.refresh_token,this.l=t.expires_at,this.t=t.user,this.D=0,this.I("TOKEN_REFRESHED"),{data:{token:this.i,sessionToken:this.o||void 0,refreshToken:this.h||void 0,expiresAt:this.l||void 0,user:this.t},error:null}}const i=this.o||this.i;if(!i)throw new s("No hay sesión activa");g("log","[DYPAI SDK] 🔄 Verificando sesión activa con session_token...");const n=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,r=new AbortController,o=setTimeout(()=>r.abort(),A.REFRESH_TIMEOUT_MS);let a;try{a=await fetch(n,{method:"GET",headers:{Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},signal:r.signal})}finally{clearTimeout(o)}if(!a.ok){let e={};try{e=await a.json()}catch{}const i=a.status,s=e.error||e.code||"",n=(e.error_description||e.msg||"").toLowerCase(),r=this.O(i),o=i>=400&&i<500&&!r,c="invalid_grant"===s||n.includes("invalid refresh token")||n.includes("revoked")||n.includes("expired")||n.includes("invalid token");throw o&&c?(g("error",`[DYPAI SDK] ❌ Token inválido/revocado (${i}). Limpiando sesión.`),this.F(`refreshSession failed (${i}: ${s})`)):o?(g("error",`[DYPAI SDK] ❌ Error 4xx en refresh (${i}). Limpiando sesión.`),this.F(`refreshSession failed (${i})`)):g("warn",`[DYPAI SDK] ⚠️ Error transitorio en refresh (${i}). Manteniendo sesión.`),new t(e.msg||e.error_description||"Refresh session failed",i)}const c=await a.json(),h=a.headers.get("set-auth-jwt"),l=c?.jwt,u=l||h,d=this.W(c,this.i);return u&&(d.token=u),d.sessionToken=i,this.D=0,await this.q(d),g("log",`[DYPAI SDK] ✅ Session refresh exitoso (fresh JWT: ${!!u}).`),{data:d,error:null}}catch(e){this.D++,this._=Date.now();const i=e instanceof DOMException&&"AbortError"===e.name,s=i?new t("Refresh token timeout (servidor no responde)",408):e instanceof t?e:new t(e.message||"Error refrescando sesión",401),n=Math.min(A.RETRY_BASE_MS*Math.pow(2,this.D),A.MAX_RETRY_MS);return i?g("error",`[DYPAI SDK] ⏱️ Timeout en refresh después de ${A.REFRESH_TIMEOUT_MS/1e3}s. Backoff: ${n}ms (intento ${this.D})`):g("error","[DYPAI SDK] ❌ Refresh falló:",s.message,`(status: ${s.status}). Backoff: ${n}ms (intento ${this.D})`),this.D>=A.MAX_REFRESH_FAILURES&&(g("error",`[DYPAI SDK] 🗑️ ${this.D} fallos consecutivos de refresh. Sesión irrecuperable. Limpiando...`),this.F(`${this.D} consecutive refresh failures`),this.D=0),{data:null,error:s}}}async signInWithOtp(e){return _((async()=>{if(e.phone)throw new t("Phone OTP is not supported by the current DYPAI auth backend",400);if(!e.email)throw new t("Email is required for magic link sign-in",400);const i=await this.signInWithMagicLink(e.email);if(i.error)throw i.error;return{status:!0}})())}async verifyOtp(e){return _((async()=>{if(!e.token)throw new t("Verification token is required",400);if("magiclink"===e.type)return await this.H(e.token);if("signup"===e.type||"email_change"===e.type){const i=await this.X(e.token);if(i)return i;throw new t("Verification completed but no session was returned",400)}if("recovery"===e.type||"invite"===e.type)throw new t("Recovery links do not use verifyOtp(). Use setPassword() after the PASSWORD_RECOVERY callback.",400);throw new t("Unsupported verification type",400)})())}async updateUser(e){return _((async()=>{const i=this.o||this.i;if(!i)throw new s("No hay sesión activa");const n=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/update-user`,r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(e)});if(!r.ok){const e=await r.json();throw new t(e.detail||"Update user failed",r.status)}const o=await r.json(),a=this.M(o);return this.G(a),a})())}async setPassword(e){return _((async()=>{if("undefined"==typeof window)throw new t("setPassword requiere un entorno de navegador",400);const i=new URLSearchParams(window.location.search).get("token")||this.C();if(!i){if(this.o||this.i){const t=await this.updateUser({password:e});if(t.error)throw t.error;return this.L(),this.U(),{status:!0}}throw new t("No reset token found in the current URL",400)}const s=this.config.baseUrl||"http://localhost:8000",n=await fetch(`${s}/api/v0/auth/reset-password`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({newPassword:e,token:i})});if(!n.ok){const t=await k(n,"Reset password failed");throw this.B(t),t}this.Y(),this.L(),this.U();const r=await n.json();if(r?.jwt&&r?.session_token&&r?.user){const t={token:r.jwt,sessionToken:r.session_token,user:this.M(r),expiresAt:r.session?.expiresAt?Math.floor(new Date(r.session.expiresAt).getTime()/1e3):void 0};await this.q(t)}return{status:r?.status??!0}})())}async signInWithMagicLink(t,e){return _((async()=>{const i=this.config.baseUrl||"http://localhost:8000",s=e?.redirectTo||("undefined"!=typeof window?window.location.href.split("#")[0]:void 0),n=await fetch(`${i}/api/v0/auth/sign-in/magic-link`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({email:t,callbackURL:s})});if(!n.ok)throw await k(n,"Magic link failed");return{message:"Magic link sent"}})())}async enableTwoFactor(){return _((async()=>{const t=this.o||this.i;if(!t)throw new s("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/two-factor/enable`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await k(i,"Enable 2FA failed");return await i.json()})())}async verifyTwoFactor(t){return _((async()=>{const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/two-factor/verify-totp`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.o||this.i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({code:t})});if(!i.ok)throw await k(i,"2FA verification failed");const s=await i.json(),n={token:s.jwt||s.token||this.i,expiresIn:604800,expiresAt:Math.floor(Date.now()/1e3)+604800,user:this.M(s)};return await this.q(n),n})())}async disableTwoFactor(){return _((async()=>{const t=this.o||this.i;if(!t)throw new s("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/two-factor/disable`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await k(i,"Disable 2FA failed");return{message:"2FA disabled"}})())}async createOrganization(t,e){return _((async()=>{const i=this.o||this.i;if(!i)throw new s("No hay sesión activa");const n=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${n}/api/v0/auth/organization/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({name:t,slug:e})});if(!r.ok)throw await k(r,"Create organization failed");return await r.json()})())}async inviteMember(t,e,i){return _((async()=>{const n=this.o||this.i;if(!n)throw new s("No hay sesión activa");const r=this.config.baseUrl||"http://localhost:8000",o=await fetch(`${r}/api/v0/auth/organization/invite-member`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({organizationId:t,email:e,role:i||"member"})});if(!o.ok)throw await k(o,"Invite member failed");return await o.json()})())}async listOrganizations(){return _((async()=>{const t=this.o||this.i;if(!t)throw new s("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/organization/list`,{headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await k(i,"List organizations failed");return await i.json()})())}startAutoRefresh(){this.stopAutoRefresh(),g("log","[DYPAI SDK] ⏱️ Auto-refresh iniciado (tick cada 30s)."),this.Z(),this.k=setInterval(()=>this.Z(),A.AUTO_REFRESH_TICK_DURATION_MS)}stopAutoRefresh(){this.k&&(g("log","[DYPAI SDK] ⏹️ Auto-refresh detenido."),clearInterval(this.k),this.k=null)}async Z(){try{const t=Math.floor(Date.now()/1e3),e=this.l||0;if(!this.h&&!this.o||!e)return;if(1e3*(e-t)>A.AUTO_REFRESH_TICK_THRESHOLD*A.AUTO_REFRESH_TICK_DURATION_MS+1e3*A.CLOCK_SKEW_TOLERANCE_S)return;if(this.D>0){const t=Math.min(A.RETRY_BASE_MS*Math.pow(2,this.D),A.MAX_RETRY_MS);if(Date.now()-this._<t)return}g("log",`[DYPAI SDK] ⏱️ Auto-refresh tick: token expira en ${e-t}s. Refrescando...`),await this.refreshSession()}catch(t){g("error","[DYPAI SDK] ❌ Error en auto-refresh tick:",t)}}M(t){return t?e(t):(g("warn","[DYPAI SDK] ⚠️ Intentando normalizar un usuario inexistente (data es null/undefined)"),{})}W(t,e){const i="number"==typeof t?.expiresIn?t.expiresIn:"number"==typeof t?.expires_in?t.expires_in:604800,s=t?.jwt||void 0,n=t?.session_token||t?.token||t?.access_token||t?.session?.token||t?.session?.accessToken||t?.session?.access_token||e;return{token:s||n,sessionToken:n,refreshToken:t?.refreshToken||t?.refresh_token||t?.session?.refreshToken||t?.session?.refresh_token||void 0,expiresIn:i,expiresAt:Math.floor(Date.now()/1e3)+i,user:this.M(t)}}async q(t){this.p=null,t.token&&(this.i=t.token,this.o=t.sessionToken||t.token,void 0!==t.refreshToken&&(this.h=t.refreshToken||null),void 0!==t.expiresAt&&(this.l=t.expiresAt||null),await this.G(t.user,t.token,t.refreshToken,t.expiresAt),g("log",`[DYPAI SDK] 💾 Sesión persistida en storage (expires_at: ${this.l}, has_refresh: ${!!this.h}, has_session_token: ${!!this.o})`),this.startAutoRefresh())}async G(t,e,i,s){this.t=t,e&&(this.i=e),void 0!==i&&(this.h=i||null),void 0!==s&&(this.l=s||null);try{if(this.i&&this.t){const t={access_token:this.i,session_token:this.o,refresh_token:this.h,expires_at:this.l,user:this.t,version:2};await this.storage.setItem(this.STORAGE_KEY,JSON.stringify(t)),this.R("SESSION_UPDATED")}else g("warn","[DYPAI SDK] ⚠️ _updateUser: No se guardó sesión porque falta token o user.")}catch(t){g("error","[DYPAI SDK] ❌ Error guardando sesión en storage:",t)}if(this.v){const t=e?"SIGNED_IN":"USER_UPDATED";this.I(t)}}async F(t="unknown"){g("log",`[DYPAI SDK] 🧹 Limpiando sesión del estado y storage. Motivo: ${t}`),this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh();try{await this.storage.removeItem(this.STORAGE_KEY),g("log","[DYPAI SDK] ✅ Storage limpiado con éxito.")}catch(t){g("error","[DYPAI SDK] ❌ Error eliminando sesión de storage:",t)}this.R("SIGNED_OUT"),this.I("SIGNED_OUT")}async H(t){const e=this.getBaseUrl(),i=new URL(`${e}/api/v0/auth/magic-link/verify`);i.searchParams.set("token",t);const s=await fetch(i.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!s.ok)throw await k(s,"Magic link verification failed");const n=await s.json(),r=this.W(n);return await this.q(r),r}async X(t){const e=this.getBaseUrl(),i=new URL(`${e}/api/v0/auth/verify-email`);i.searchParams.set("token",t);const s=await fetch(i.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!s.ok)throw await k(s,"Email verification failed");if(!(s.headers.get("content-type")||s.headers.get("Content-Type")||"").includes("application/json"))return null;const n=await s.json(),r=this.W(n);return r.token&&r.user?.id?(await this.q(r),r):null}async tt(t,e){const i=e||("undefined"!=typeof window&&window.location.pathname.includes("reset-password")?"recovery":null);if(!i)return!1;if(g("log",`[DYPAI SDK] 🔗 Auth callback detectado en query (type: ${i}). Procesando...`),this.N(i),"recovery"===i||"invite"===i)return this.K(t),this.U(),this.m="PASSWORD_RECOVERY",!0;if(this.U(),"magiclink"===i)return await this.H(t),this.m="SIGNED_IN",!0;if("signup"===i||"email_change"===i){const e=await this.X(t);return this.m=e?.token?"SIGNED_IN":null,!0}return!1}async T(t=!0){g("log","[DYPAI SDK] 🔍 Iniciando recuperación de sesión...");try{if(await this.et())return void g("log","[DYPAI SDK] ✅ Sesión establecida desde callback URL. Saltando recuperación de localStorage.");const e=await this.storage.getItem(this.STORAGE_KEY);if(!e)return void g("log","[DYPAI SDK] ℹ️ No se encontró ninguna sesión (storage vacío).");let i;try{i=JSON.parse(e)}catch{return g("warn","[DYPAI SDK] ⚠️ Sesión corrupta (JSON inválido). Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY)}if(!i||"object"!=typeof i||"string"!=typeof i.access_token)return g("warn","[DYPAI SDK] ⚠️ Sesión con formato inválido. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if((i.version||1)<2&&!i.session_token)return g("warn","[DYPAI SDK] 🔄 Sesión en formato antiguo (v1) sin session_token. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if(this.i=i.access_token,this.o="string"==typeof i.session_token?i.session_token:i.access_token,this.h="string"==typeof i.refresh_token?i.refresh_token:null,this.l="number"==typeof i.expires_at?i.expires_at:null,this.t=i.user&&"object"==typeof i.user?i.user:null,t){g("log","[DYPAI SDK] 🔄 Validando sesión con el servidor...");const t=await this.refreshSession();if(t.error)return void g("warn","[DYPAI SDK] ⚠️ Sesión inválida. Limpiando:",t.error.message)}this.m="SIGNED_IN"}catch(t){g("error","[DYPAI SDK] ❌ Error crítico durante la recuperación de sesión:",t)}}async et(){if("undefined"==typeof window)return!1;try{const t=new URLSearchParams(window.location.search),e=t.get("error"),i=t.get("token"),s=t.get("type");if(e){const i=t.get("error_code")||e,s=t.get("error_description")?.replace(/\+/g," ")||"Authentication error";return g("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${i} — ${s}`),this.p={code:i,message:s},this.U(),this.m="AUTH_ERROR",!0}if(i)return await this.tt(i,s);const n=window.location.hash.substring(1);if(!n)return!1;const r=new URLSearchParams(n);if(r.get("error")){const t=r.get("error_code")||"unknown",e=r.get("error_description")?.replace(/\+/g," ")||"Authentication error";return g("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${t} — ${e}`),this.p={code:t,message:e},this.U(),this.m="AUTH_ERROR",!0}const o=r.get("access_token");if(!o)return!1;const a=r.get("refresh_token"),c=r.get("type"),h=parseInt(r.get("expires_in")||"3600",10);g("log",`[DYPAI SDK] 🔗 Auth callback detectado en URL hash (type: ${c||"unknown"}). Procesando...`),this.N(c),this.U(),this.o=o,this.i=o,this.h=a||null,this.l=Math.floor(Date.now()/1e3)+h;const l=await fetch(`${this.getBaseUrl()}/api/v0/auth/get-session`,{headers:{Authorization:`Bearer ${o}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!l.ok)return g("error",`[DYPAI SDK] ❌ Callback URL: token inválido o expirado (status: ${l.status}). Limpiando...`),this.i=null,this.o=null,this.h=null,this.l=null,this.p={code:"token_invalid",message:"The authentication link is invalid or has expired"},this.I("AUTH_ERROR"),!0;const u=await l.json(),d=u?.jwt||l.headers.get("set-auth-jwt"),f=this.M(u),p={token:d||o,sessionToken:o,refreshToken:a||void 0,expiresIn:h,expiresAt:this.l,user:f};return await this.q(p),this.m="recovery"===c||"invite"===c?"PASSWORD_RECOVERY":"SIGNED_IN",g("log",`[DYPAI SDK] 🔑 Callback type=${c}. Pending event: ${this.m}`),!0}catch(t){return g("error","[DYPAI SDK] ❌ Error procesando callback URL:",t),!1}}J(){return this.i&&this.t?{access_token:this.i,refresh_token:this.h||void 0,expires_at:this.l||void 0,token_type:"bearer",user:this.t}:null}I(t="USER_UPDATED"){const e=this.J();g("log",`[DYPAI SDK] 📢 Notificando a ${this.u.length} suscriptores: Evento=${t} (Sesión activa: ${!!e})`),this.u.forEach(i=>i(t,e))}handleSessionExpired(){this.F("handleSessionExpired called (likely 401 from API)")}}A.REFRESH_TIMEOUT_MS=15e3,A.MAX_REFRESH_FAILURES=5,A.AUTO_REFRESH_TICK_DURATION_MS=3e4,A.AUTO_REFRESH_TICK_THRESHOLD=3,A.RETRY_BASE_MS=200,A.MAX_RETRY_MS=3e4,A.LOCK_ACQUIRE_TIMEOUT_MS=5e3,A.CLOCK_SKEW_TOLERANCE_S=10,A.CALLBACK_TYPE_STORAGE_KEY="dypai-auth-callback-type",A.CALLBACK_REDIRECT_STORAGE_KEY="dypai-auth-callback-redirect",A.RESET_TOKEN_STORAGE_KEY="dypai-auth-reset-token";class P{constructor(t,e,i){this.ws=null,this.bindings=[],this.it={},this.st=!1,this.topic=t,this.client=e,this.config=i||{}}on(t,e,i){return this.bindings.push({type:t,filter:e,callback:i}),this}subscribe(t){this.nt=t;const e=this.client.rt();this.ws=e;const i=()=>{const i=this.bindings.filter(t=>"postgres_changes"===t.type);for(const t of i)e.send(JSON.stringify({type:"subscribe",table:t.filter.table||"*",event:t.filter.event||"*",filter:t.filter.filter?this.ot(t.filter.filter):void 0,schema:t.filter.schema||"public"}));const s=this.bindings.some(t=>"broadcast"===t.type),n=this.bindings.some(t=>"presence"===t.type);(s||n)&&e.send(JSON.stringify({type:"join",channel:this.topic,presence:!!n||void 0})),this.st=!0,t?.("SUBSCRIBED")};e.readyState===WebSocket.OPEN?i():e.readyState===WebSocket.CONNECTING&&e.addEventListener("open",i,{once:!0}),this.ct&&e.removeEventListener("message",this.ct);const s=t=>{try{const e=JSON.parse("string"==typeof t.data?t.data:"");this.ht(e)}catch{}};return this.ct=s,e.addEventListener("message",s),e.addEventListener("close",()=>{this.st=!1,t?.("CLOSED")},{once:!0}),e.addEventListener("error",()=>{t?.("CHANNEL_ERROR",new Error("WebSocket error"))},{once:!0}),this}ut(t){const e=this.bindings.filter(t=>"postgres_changes"===t.type);for(const i of e)t.send(JSON.stringify({type:"subscribe",table:i.filter.table||"*",event:i.filter.event||"*",filter:i.filter.filter?this.ot(i.filter.filter):void 0,schema:i.filter.schema||"public"}));const i=this.bindings.some(t=>"broadcast"===t.type),s=this.bindings.some(t=>"presence"===t.type);(i||s)&&t.send(JSON.stringify({type:"join",channel:this.topic,presence:!!s||void 0})),this.nt?.("SUBSCRIBED")}async send(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"broadcast",channel:this.topic,event:t.event,payload:t.payload,self:this.config.broadcast?.self||!1})),"ok"}catch{return"error"}}async track(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"presence",channel:this.topic,data:t})),"ok"}catch{return"error"}}async untrack(){return"ok"}presenceState(){return{...this.it}}async unsubscribe(){if(this.ct&&this.ws&&(this.ws.removeEventListener("message",this.ct),this.ct=void 0),this.ws?.readyState===WebSocket.OPEN){const t=this.bindings.filter(t=>"postgres_changes"===t.type);for(const e of t)this.ws.send(JSON.stringify({type:"unsubscribe",table:e.filter.table}));this.ws.send(JSON.stringify({type:"leave",channel:this.topic}))}return this.st=!1,this.bindings=[],this.it={},"ok"}ht(t){if("change"===t.type)for(const e of this.bindings)"postgres_changes"===e.type&&(e.filter.table&&"*"!==e.filter.table&&e.filter.table!==t.table||e.filter.event&&"*"!==e.filter.event&&e.filter.event!==t.event||e.callback({schema:"public",table:t.table,eventType:t.event,new:t.record||{},old:t.old_record||{},commit_timestamp:t.timestamp,truncated:t.truncated||!1}));if("broadcast"===t.type&&t.channel===this.topic)for(const e of this.bindings)"broadcast"===e.type&&("*"!==e.filter.event&&e.filter.event!==t.event||e.callback({event:t.event,payload:t.payload,sender:t.sender,timestamp:t.timestamp}));if("presence_state"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";this.it={};for(const[i,s]of Object.entries(t.presences||{})){const t=s[e]||i;this.it[t]||(this.it[t]=[]),this.it[t].push(s)}for(const t of this.bindings)"presence"===t.type&&("sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"}))}if("presence_diff"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";if(t.joins)for(const[i,s]of Object.entries(t.joins)){const t=s[e]||i;this.it[t]||(this.it[t]=[]),this.it[t].push(s);for(const e of this.bindings)"presence"===e.type&&("join"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"join",key:t,newPresences:[s],currentPresences:this.it[t]}))}if(t.leaves)for(const[i,s]of Object.entries(t.leaves)){const t=s[e]||i;this.it[t]=(this.it[t]||[]).filter(t=>t.userId!==i),0===this.it[t].length&&delete this.it[t];for(const e of this.bindings)"presence"===e.type&&("leave"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"leave",key:t,leftPresences:[s],currentPresences:this.it[t]||[]}))}for(const t of this.bindings)"presence"!==t.type||"sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"})}}ot(t){const e=t.match(/^(\w+)=eq\.(.+)$/);if(e)return{[e[1]]:e[2]}}}class E{constructor(t,e){this.ws=null,this.channels=new Map,this.heartbeatTimer=null,this.reconnectAttempt=0,this.reconnectTimer=null,this.baseUrl=t,this.tokenProvider=e}channel(t,e){const i=this.channels.get(t);if(i)return i;const s=new P(t,this,e?.config);return this.channels.set(t,s),s}getChannels(){return Array.from(this.channels.values())}async removeChannel(t){await t.unsubscribe(),this.channels.delete(t.topic),0===this.channels.size&&this.disconnect()}async removeAllChannels(){for(const t of this.channels.values())await t.unsubscribe();this.channels.clear(),this.disconnect()}async setAuth(t){t&&(this.tokenProvider=()=>t),this.ws&&this.channels.size>0&&(this.disconnect(),this.rt())}disconnect(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.ws?.close(),this.ws=null}rt(){if(this.ws&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING))return this.ws;const t=this.tokenProvider(),e=this.baseUrl.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(t||"")}`,i=new WebSocket(e);return this.ws=i,i.addEventListener("open",()=>{this.reconnectAttempt=0;for(const t of this.channels.values())t.st&&t.ut(i);this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{i.readyState===WebSocket.OPEN&&i.send(JSON.stringify({type:"heartbeat"}))},25e3)}),i.addEventListener("close",()=>{if(this.ws=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.channels.size>0&&!this.reconnectTimer){const t=E.RECONNECT_INTERVALS[Math.min(this.reconnectAttempt,E.RECONNECT_INTERVALS.length-1)];this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.channels.size>0&&this.rt()},t)}}),i}}E.RECONNECT_INTERVALS=[1e3,2e3,5e3,1e4,3e4];class T{get direct(){if(!this.dt)throw new Error("client.db.direct is not available. Pass serviceRoleKey to createClient() to enable direct database access.\n\nExample:\n const client = createClient(url, { serviceRoleKey: process.env.DYPAI_SERVICE_ROLE_KEY });\n\nWARNING: serviceRoleKey grants admin-level access. Only use from server-side code (scripts, migrations, seeds). Never expose it in browser/client-side code.");return this.dt}set direct(t){this.dt=t}constructor(t){this.api=t}from(t){return new I(t,this.api)}}class I{constructor(t,e){this.table=t,this.api=e}async select(t={}){return this.api.get(this.table,{params:t})}async insert(t){return this.api.post(this.table,t)}async update(t,e){return this.api.patch(`${this.table}/${t}`,e)}async delete(t){return this.api.delete(`${this.table}/${t}`)}}const $=1e3;class O{constructor(t){this.api=t}from(t){return new R(t,this.api)}async sql(t,e,i){return this.api.post("/api/v0/admin/db/sql",{query:t,params:e||[],...void 0!==i?.limit&&{limit:i.limit}})}}class R{constructor(t,e){this.table=t,this.api=e,this.ft="public",this.wt=[],this.yt=100,this.vt=0,this.St="ASC"}schema(t){return this.ft=t,this}eq(t,e){return this.wt.push({column:t,operator:"eq",value:e}),this}neq(t,e){return this.wt.push({column:t,operator:"neq",value:e}),this}gt(t,e){return this.wt.push({column:t,operator:"gt",value:e}),this}gte(t,e){return this.wt.push({column:t,operator:"gte",value:e}),this}lt(t,e){return this.wt.push({column:t,operator:"lt",value:e}),this}lte(t,e){return this.wt.push({column:t,operator:"lte",value:e}),this}like(t,e){return this.wt.push({column:t,operator:"ilike",value:e}),this}ilike(t,e){return this.like(t,e)}isNull(t){return this.wt.push({column:t,operator:"is_null",value:null}),this}is(t,e){return this.isNull(t)}notNull(t){return this.wt.push({column:t,operator:"not_null",value:null}),this}in(t,e){return this.wt.push({column:t,operator:"in",value:e}),this}contains(t,e){return this.wt.push({column:t,operator:"array_contains",value:e}),this}containedBy(t,e){return this.wt.push({column:t,operator:"contained_by",value:e}),this}overlaps(t,e){return this.wt.push({column:t,operator:"overlaps",value:e}),this}textSearch(t,e){return this.wt.push({column:t,operator:"fts",value:e}),this}phraseSearch(t,e){return this.wt.push({column:t,operator:"phfts",value:e}),this}not(t,e,i){return this.wt.push({column:t,operator:"not",value:{operator:e,value:i}}),this}or(t){return this.wt.push({operator:"or",or:t}),this}limit(t){return this.yt=t,this}offset(t){return this.vt=t,this}orderBy(t,e="ASC"){return this.kt=t,this.St=e,this}async select(t){const e=[...this.wt];let i=this.bt;if("string"==typeof t)i=t;else if(t&&"object"==typeof t)for(const[i,s]of Object.entries(t))e.push({column:i,operator:"eq",value:s});return this.execute("select",{...i&&{columns:i},filters:e,limit:this.yt,offset:this.vt,sort_by:this.kt,order:this.St})}async single(){const e=await this.select();if(e.error)return e;const i=e.data;return!i||Array.isArray(i)&&0===i.length?{data:null,error:new t("No rows found.",404)}:{data:Array.isArray(i)?i[0]:i,error:null}}async maybeSingle(){const t=await this.select();if(t.error)return t;const e=t.data;return!e||Array.isArray(e)&&0===e.length?{data:null,error:null}:{data:Array.isArray(e)?e[0]:e,error:null}}async count(){const t=await this.execute("select",{columns:"count(*) as count",filters:[...this.wt],limit:1,offset:0});if(t.error)return t;const e=t.data;return{data:Array.isArray(e)&&e.length>0?Number(e[0].count):0,error:null}}async insert(t){if(Array.isArray(t)&&t.length>$){const e=[];for(let i=0;i<t.length;i+=$){const s=t.slice(i,i+$),{data:n,error:r}=await this.execute("insert",{data:s,mode:"bulk"});if(r)return{data:null,error:r};n&&e.push(...Array.isArray(n)?n:[n])}return{data:e,error:null}}const e=Array.isArray(t)?"bulk":"single";return this.execute("insert",{data:t,mode:e})}async update(e){return 0===this.wt.length?{data:null,error:new t("Update requires at least one filter. Use .eq(), .in(), etc.",400)}:this.execute("update",{data:e,filters:this.wt})}async delete(){return 0===this.wt.length?{data:null,error:new t("Delete requires at least one filter.",400)}:this.execute("delete",{filters:this.wt})}async upsert(t,e="id"){return this.execute("upsert",{data:t,conflict_column:e})}async execute(t,e){return this.api.post("/api/v0/admin/db/query",{operation:t,schema_name:this.ft,table_name:this.table,...e})}}class U{constructor(t){this.api=t}async list(t={}){const i=await this.api.get("admin/users",{params:t});return i.data?.users&&(i.data.users=i.data.users.map(e)),i}async create(t){const i=await this.api.post("admin/users",t);return i.data&&(i.data=e(i.data)),i}async update(t,i){const s=await this.api.put(`admin/users/${t}`,i);return s.data&&(s.data=e(s.data)),s}async delete(t){return this.api.delete(`admin/users/${t}`)}}class x{constructor(t){this.api=t}async list(t={}){const e={};void 0!==t.include_global&&(e.include_global=t.include_global);const i=await this.api.get("admin/roles",{params:e});return Array.isArray(i.data)?i.data={roles:i.data,total:i.data.length}:i.data?.roles&&null==i.data.total&&(i.data.total=i.data.roles.length),i}async get(t){return this.api.get(`admin/roles/${t}`)}async create(t){return this.api.post("admin/roles",t)}async update(t,e){return this.api.put(`admin/roles/${t}`,e)}async delete(t){return this.api.delete(`admin/roles/${t}`)}}let N=null;function K(t){N=t}const C=t=>{const{title:e,description:i,variant:s="default"}=t,n=`${"error"===s?"❌":"success"===s?"✅":"warning"===s?"⚠️":"info"===s?"ℹ️":"📢"} ${e}${i?`: ${i}`:""}`;"error"===s?console.error(n):"warning"===s&&console.warn(n)};function Y(){const t=N||C;return{toast:t,toastSuccess:(e,i)=>t({title:e,description:i,variant:"success"}),toastError:(e,i)=>t({title:e,description:i,variant:"error"}),toastWarning:(e,i)=>t({title:e,description:i,variant:"warning"}),toastInfo:(e,i)=>t({title:e,description:i,variant:"info"})}}const L=t=>(N||C)(t),j=(t,e)=>L({title:t,description:e,variant:"success"}),B=(t,e)=>L({title:t,description:e,variant:"error"}),z=(t,e)=>L({title:t,description:e,variant:"warning"}),J=(t,e)=>L({title:t,description:e,variant:"info"});let W={},q=null;function M(t){q=t}function G(t){W={...W,...t}}const F=new Map,V=new Set(["params","token","apiKey","showToasts"]);function H(e,i,s){if(null==s)return;if("object"!=typeof s||Array.isArray(s))throw new t(`Invalid ${e} options for endpoint "${i}": options must be an object. Use { params: { ... } } for query parameters.`,400,"invalid_api_options");const n=Object.keys(s).filter(t=>!V.has(t));if(!n.length)return;const r=n.map(t=>`"${t}"`).join(", "),o=n[0],a=e.toLowerCase();throw new t(`Invalid ${e} options for endpoint "${i}": unknown option ${r}. ${"GET"===e||"DELETE"===e?`Use client.api.${a}("${i}", { params: { ${o}: value } }).`:`Pass request data as the second argument and options as the third argument, for example client.api.${a}("${i}", body, { params: { ${o}: value } }).`}`,400,"invalid_api_options")}function Q(){let t=W;try{const{getGlobalConfig:e}=require("../config/global-config");t={...e(),...W}}catch(t){}return t}async function X(e,i,s,n,r,o,a,c){const h=Q(),l=c||null;if(!l&&!s.startsWith("http"))throw new Error("Base URL no definida. Usa createClient(url[, apiKey]).");if("string"!=typeof s||!s.trim())throw new Error("Endpoint debe ser un string válido");let u;if(s.startsWith("http"))u=s;else{const t=l.replace(/\/+$/,"");u=s.startsWith("/")?t+s:t+"/api/v0/"+s}if(o&&Object.keys(o).length>0){const t=new URLSearchParams,e=(i,s)=>{null!=s&&(Array.isArray(s)?s.forEach((t,s)=>e(`${i}[${s}]`,t)):"object"==typeof s?Object.entries(s).forEach(([t,s])=>e(`${i}[${t}]`,s)):t.append(i,String(s)))};Object.entries(o).forEach(([t,i])=>e(t,i));const i=t.toString();i&&(u+=`?${i}`)}const d="GET"===i?`${i}:${u}:${JSON.stringify(n)}`:null;if(d&&F.has(d))return F.get(d);const f=n instanceof FormData,p={...h.headers||{},...f?{}:{"Content-Type":"application/json"},...e&&{Authorization:`Bearer ${e}`},...a&&{"x-api-key":a}},w={method:i,headers:p,credentials:"include"};n&&"GET"!==i&&"DELETE"!==i&&(w.body=f?n:JSON.stringify(n));const y=h.fetch||("undefined"!=typeof window?window.fetch.bind(window):fetch);if(!y)throw new Error("Fetch no disponible.");const m=(async()=>{const e=Q().toast||L,s=(void 0!==r?r:!1!==h.showToasts)&&e;try{const n=await y(u,w);if(!n.ok){let i,r="Error en la petición";try{const t=await n.text();try{const e=JSON.parse(t);i=e,r=e.message||e.msg||e.error_description||e.error||r}catch{t.length<200&&(r=t)}}catch{}throw 401===n.status&&h.onUnauthorized&&h.onUnauthorized(),s&&e({title:"Error",description:r,variant:"error"}),new t(r,n.status,void 0,i)}!s||"POST"!==i&&"PUT"!==i&&"PATCH"!==i&&"DELETE"!==i||e({title:"Éxito",description:"Operación completada",variant:"success"});const r=n.headers.get("content-type")||"";return r.includes("application/pdf")||r.includes("image/")||r.includes("audio/")||r.includes("video/")||r.includes("application/octet-stream")||r.includes("application/zip")||r.includes("application/vnd.openxmlformats-officedocument")?await n.blob():r.includes("application/json")?await n.json():await n.text()}finally{d&&F.delete(d)}})();return d&&F.set(d,m),m}async function Z(t,e,i,s){const n=s?.method||(i?"POST":"GET"),{data:r,error:o}=await("GET"===n?t.get(e,{params:s?.params}):t.post(e,i,{params:s?.params}));if(o)throw o;if(r instanceof Blob){const t=window.URL.createObjectURL(r),e=document.createElement("a");e.href=t,e.download=s?.fileName||"archivo-descargado",document.body.appendChild(e),e.click(),window.URL.revokeObjectURL(t),document.body.removeChild(e)}else if(r&&"object"==typeof r&&("url"in r||"signed_url"in r||"signedUrl"in r)){const t=r.url||r.signed_url||r.signedUrl;if("string"==typeof t){const e=new URL(t).searchParams.get("response-content-disposition");let i;if(e){const t=decodeURIComponent(e).match(/filename\*?=(?:UTF-8''|")?([^";]+)/i);t?.[1]&&(i=t[1].replace(/"/g,""))}const n=s?.fileName||i;if(n){const e=document.createElement("a");e.href=t,e.download=n,document.body.appendChild(e),e.click(),document.body.removeChild(e)}else window.open(t,"_blank")}}}async function tt(e,i,s,n){const{data:r,error:o}=await e.post(i,{file_path:s.name,content_type:s.type||"application/octet-stream",size_bytes:s.size,confirm:!1,client_upload:!0,...n?.params||{}});if(o)throw o;const a=function(t){if(!t||"object"!=typeof t)return null;const e=t.upload_url||t.uploadUrl||t.url?t:null;if(e?.upload_url)return et(e);const i=[t.data,t.result,t.output,t.payload].filter(Boolean);for(const t of i)if(t&&(t.upload_url||t.uploadUrl||t.url))return et(t);const s=[t.steps_results,t.nodes_results].filter(Boolean);for(const t of s){const e=Array.isArray(t)?t:Object.values(t);for(const t of e)if(t&&(t.upload_url||t.uploadUrl||t.url))return et(t)}return null}(r);if(!a?.upload_url)throw new t("The workflow did not return a valid upload URL (missing storage upload node?)",400);const{upload_url:c,method:h="PUT",headers:l={},file_path:u,storage_path:d}=a;n?.onProgress&&n.onProgress(10);const f=await fetch(c,{method:h,headers:{"Content-Type":s.type||"application/octet-stream",...l},body:s});if(!f.ok)throw new t("Direct upload to cloud storage failed",f.status);n?.onProgress&&n.onProgress(90);const p=n?.confirmEndpoint||i,{data:w,error:y}=await e.post(p,{...n?.params,bucket:a.bucket||n?.params?.bucket,file_path:u,storage_path:d,filename:s.name,content_type:s.type||"application/octet-stream",size_bytes:s.size,confirm:!0,client_upload:!0});if(y)throw y;return n?.onProgress&&n.onProgress(100),w}function et(t){return t&&"object"==typeof t?{...t,upload_url:t.upload_url||t.uploadUrl||t.url,storage_path:t.storage_path||t.storagePath}:null}function it(e){const i={get:st(e,"GET"),post:st(e,"POST"),put:st(e,"PUT"),patch:st(e,"PATCH"),delete:st(e,"DELETE"),upload:async(e,s,n)=>{try{return{data:await tt(i,e,s,n),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Upload failed",e?.status??500)}}},download:async(e,s,n)=>{try{return await Z(i,e,s,{...n,method:"POST"}),{data:void 0,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Download failed",e?.status??500)}}}};return i}function st(e,i){const s="POST"===i||"PUT"===i||"PATCH"===i;return async(n,r,o)=>{const a=e();let c,h={};s?(c=r,h=o||{}):(h=r||{},c=void 0);try{H(i,n,h);const t=h.token||a.token||(q?q():"")||"",e=h.apiKey||a.apiKey;return{data:await X(t,i,n,c,h.showToasts,h.params,e,a.baseUrl),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Error desconocido",e?.status??500)}}}}function nt(e){const i=()=>"function"==typeof e?e():e,s={get:rt(i,"GET"),post:rt(i,"POST"),put:rt(i,"PUT"),patch:rt(i,"PATCH"),delete:rt(i,"DELETE"),upload:async(e,i,n)=>{try{return{data:await tt(s,e,i,n),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Upload failed",e?.status??500)}}},download:async(e,i,n)=>{try{return await Z(s,e,i,{...n,method:"POST"}),{data:void 0,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Download failed",e?.status??500)}}}};return s}function rt(e,i){return async(s,n,r)=>{try{const t=e(),o=await async function(t,e,i,s){let n,r={};"POST"===t||"PUT"===t||"PATCH"===t?(n=i,r=s||{}):(r=i||{},n=void 0),H(t,e,r);let o=r.token;return!o&&q&&(o=q()||""),o=o||"",{token:o,apiKey:r.apiKey,body:n,params:r.params,showToasts:r.showToasts}}(i,s,n,r),a=o.token||t.token||"",c=o.apiKey||t.apiKey;return{data:await X(a,i,s,o.body,o.showToasts,o.params,c,t.baseUrl),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Error desconocido",e?.status??500)}}}}class ot{get baseUrl(){return this.Dt}getAuthToken(){return this.auth.token||this._t||null}constructor(t){const{baseUrl:e,apiKey:i}=t;this.Dt=e,this._t=i,this.realtime=new E(e,()=>this.getAuthToken());const s=t.auth?.storageKey||t.storageKey;t.global&&function(t){W={...W,...t}}(t.global),this.auth=new A({baseUrl:e,apiKey:i,storageKey:s,storage:t.auth?.storage,autoRefreshToken:t.auth?.autoRefreshToken,persistSession:t.auth?.persistSession,passwordRecoveryRedirect:t.redirects?.passwordRecovery},null);const n=it(()=>({token:this.auth.token,apiKey:i,baseUrl:e}));if(this.auth.api=n,this.db=new T(n),t.serviceRoleKey)if("undefined"!=typeof window&&void 0!==window.document)console.error("[DYPAI SDK] ❌ serviceRoleKey detected in a browser environment. This is a security risk — the key grants admin-level database access. db.direct has been disabled. Use serviceRoleKey only from server-side code (Node.js, Bun, Deno, scripts).");else{const i=it(()=>({token:t.serviceRoleKey,baseUrl:e}));this.db.direct=new O(i)}this.users=new U(n),this.roles=new x(n),this.api=nt(()=>({token:this.auth.token||"",apiKey:i,baseUrl:e})),M(()=>this.auth.token);const r="undefined"!=typeof window?window.location.search:"";if("undefined"!=typeof window&&t.redirects){const e=t.redirects;this.auth.P?.then(()=>{if(this.auth.isPasswordRecoveryCallback&&e.passwordRecovery){if(!window.location.pathname.includes(e.passwordRecovery)){const t=e.passwordRecovery+(r||"");window.location.replace(t)}}else if(this.auth.lastError&&e.authError)window.location.pathname.includes(e.authError)||window.location.replace(e.authError);else if(this.auth.token&&e.signIn){const t=this.auth.consumeCallbackType();t&&"recovery"!==t&&"invite"!==t&&(window.location.pathname.includes(e.signIn)||window.location.replace(e.signIn))}})}G({onUnauthorized:()=>this.auth.handleSessionExpired()})}from(t){return this.db.from(t)}async me(){return this.auth.getUser()}}function at(t,e,i){if(!t)throw new Error("createClient() requiere la URL base");return new ot({baseUrl:t,apiKey:"string"==typeof e?e:void 0,..."string"==typeof e?i:e})}let ct={};function ht(t){ct={...ct,toast:L,...t},ut()}function lt(){return{...ct}}function ut(){try{const{configureApiService:t}=require("../services/ApiService");t(ct)}catch(t){}}function dt(){ct={},ut()}async function ft(){ut()}const pt={name:"@dypai-ai/client-sdk",version:"1.10.3",description:"Official JavaScript/TypeScript SDK for DYPAI",features:["Authentication (email, OAuth, OTP)","Database CRUD","Direct database access (service role)","Custom endpoints (typed API)","File upload/download (Smart Upload)","User management (admin)","React hooks (useAuth, useEndpoint, useAction, useUpload)"]};export{u as AuthEmailNotVerifiedError,r as AuthInvalidCredentialsError,a as AuthInvalidTokenError,s as AuthSessionMissingError,f as AuthUserAlreadyExistsError,h as AuthWeakPasswordError,O as DirectDBModule,R as DirectQueryBuilder,ot as DypaiClient,t as DypaiError,E as DypaiRealtimeClient,pt as PACKAGE_INFO,P as RealtimeChannel,w as RetryableFetchError,X as callApi,v as classifyAuthError,G as configureApiService,ht as configureDypaiServices,nt as createApiClient,at as createClient,lt as getGlobalConfig,d as isAuthEmailNotVerifiedError,o as isAuthInvalidCredentialsError,c as isAuthInvalidTokenError,n as isAuthSessionMissingError,p as isAuthUserAlreadyExistsError,l as isAuthWeakPasswordError,i as isDypaiError,y as isRetryableFetchError,m as isRetryableStatus,ft as reloadDypaiConfig,dt as resetGlobalConfig,K as setToastFunction,M as setTokenProvider,L as toast,B as toastError,J as toastInfo,j as toastSuccess,z as toastWarning,Y as useToast};
|
|
1
|
+
class t extends Error{constructor(t,e=500,i,s){super(t),this.status=e,this.code=i,this.details=s,this.name="DypaiError"}}function e(t){if(!t)return{};const e=t.user||t,i=e.name||e.user_metadata?.full_name||null,s=e.emailVerified??e.confirmed_at??null,n=e.app_metadata||{},r=e.user_metadata||{full_name:i};return{id:e.id,email:e.email,phone:e.phone||null,role:n.role||r.role||e.role||"authenticated",created_at:e.createdAt||e.created_at,updated_at:e.updatedAt||e.updated_at,confirmed_at:s?"boolean"==typeof s?(new Date).toISOString():s:null,last_sign_in_at:e.last_sign_in_at||null,app_metadata:n,user_metadata:{...r,full_name:i},roleDetails:{name:n.role||r.role||null,weight:0},appContext:{app_id:"default"}}}function i(e){return e instanceof t}class s extends t{constructor(t="Auth session missing"){super(t,400,"session_missing"),this.name="AuthSessionMissingError"}}function n(t){return t instanceof s}class r extends t{constructor(t="Invalid login credentials"){super(t,400,"invalid_credentials"),this.name="AuthInvalidCredentialsError"}}function o(t){return t instanceof r}class a extends t{constructor(t="Invalid or expired token"){super(t,401,"invalid_token"),this.name="AuthInvalidTokenError"}}function c(t){return t instanceof a}class h extends t{constructor(t="Password is too weak",e=[]){super(t,400,"weak_password"),this.name="AuthWeakPasswordError",this.reasons=e}}function l(t){return t instanceof h}class u extends t{constructor(t="Email not verified"){super(t,403,"email_not_verified"),this.name="AuthEmailNotVerifiedError"}}function d(t){return t instanceof u}class f extends t{constructor(t="User already exists"){super(t,409,"user_already_exists"),this.name="AuthUserAlreadyExistsError"}}function p(t){return t instanceof f}class w extends t{constructor(t,e=0){super(t,e,"retryable_fetch"),this.name="RetryableFetchError"}}function y(t){return t instanceof w}function m(t){return 0===t||408===t||429===t||t>=500&&t<600}function v(e,i,n){const o=(e||"").toLowerCase(),c=(n||"").toLowerCase();return c.includes("invalid_token")||c.includes("invalid_grant")||o.includes("invalid token")||o.includes("expired")||o.includes("revoked")?new a(e):c.includes("session_missing")||o.includes("no session")||o.includes("session missing")?new s(e):c.includes("invalid_credentials")||c.includes("invalid_login")||o.includes("invalid credentials")||o.includes("invalid login")?new r(e):c.includes("email_not_verified")||o.includes("email not verified")?new u(e):c.includes("user_already_exists")||c.includes("user_exists")||o.includes("already exists")||o.includes("already registered")?new f(e):c.includes("weak_password")||o.includes("password is too weak")||o.includes("password too short")?new h(e):m(i)?new w(e,i):new t(e,i,n)}let S=!1;function g(t,...e){"error"===t?console.error(...e):S&&("warn"===t?console.warn(...e):console.log(...e))}async function k(t,e){let i={};try{i=await t.json()}catch{}const s=i.msg||i.error_description||i.message||e,n=i.error_code||i.error||i.code||void 0;return v(s,t.status,n)}class b{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const D={getItem:t=>"undefined"==typeof window?null:window.localStorage.getItem(t),setItem:(t,e)=>{if("undefined"!=typeof window)try{window.localStorage.setItem(t,e)}catch(t){console.error("[DYPAI SDK] ❌ Error crítico guardando en localStorage (¿Quota/Permisos?):",t)}},removeItem:t=>{if("undefined"!=typeof window)try{window.localStorage.removeItem(t)}catch(t){}}};async function _(e){try{return{data:await e,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e.message||"Error de autenticación",e.status||400)}}}class A{constructor(t,e=null){this.config=t,this.t=null,this.i=null,this.o=null,this.h=null,this.l=null,this.u=[],this.p=null,this.m=null,this.v=!1,this.S=null,this.k=null,this.D=0,this._=0,this.A=null,this.storage=t.storage||D,S=!!t.debug;const i=t.storageKey||this.deriveStorageKey(t.apiKey);if(g("log",`[DYPAI SDK] 🛠️ Inicializando AuthModule (storageKey: ${i})`),this.STORAGE_KEY=`dypai-${i}-auth-session`,this.P=this.T().then(()=>{this.i&&(this.getUser().catch(()=>{}),this.startAutoRefresh());const t=this.m;this.m=null,t?setTimeout(()=>{this.v=!0,"PASSWORD_RECOVERY"!==t&&"AUTH_ERROR"!==t&&this.I(t)},0):this.v=!0}),"undefined"!=typeof window){if(window.addEventListener("visibilitychange",this.$.bind(this)),window.addEventListener("focus",this.$.bind(this)),"undefined"!=typeof BroadcastChannel)try{this.A=new BroadcastChannel(`bc:${this.STORAGE_KEY}`),this.A.addEventListener("message",t=>{const e=t.data;e&&"object"==typeof e&&(g("log",`[DYPAI SDK] 📡 BroadcastChannel: ${e.type}`),"SIGNED_OUT"===e.type?(this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh(),this.I("SIGNED_OUT")):"SESSION_UPDATED"===e.type&&this.T(!1))})}catch(t){g("warn","[DYPAI SDK] BroadcastChannel no disponible, usando storage event:",t)}window.addEventListener("storage",t=>{t.key===this.STORAGE_KEY&&(g("log","[DYPAI SDK] 🔄 Sesión actualizada (storage event). Sincronizando..."),this.T(!1))})}}O(t){return 0===t||408===t||429===t||t>=500&&t<600}R(t){if(this.A)try{this.A.postMessage({type:t,timestamp:Date.now()})}catch{}}async $(){"undefined"!=typeof document&&"visible"===document.visibilityState&&(g("log","[DYPAI SDK] 👁️ Ventana visible. Sincronizando estado y reiniciando auto-refresh..."),await this.T(!0),this.i&&this.startAutoRefresh())}deriveStorageKey(t){return t?t.substring(0,8):"default"}getBaseUrl(){return this.config.baseUrl||"http://localhost:8000"}U(){if("undefined"==typeof window)return;const t=new URLSearchParams(window.location.search);["token","type","error","error_code","error_description","access_token","refresh_token","expires_in"].forEach(e=>t.delete(e));const e=t.toString(),i=`${window.location.pathname}${e?`?${e}`:""}`;window.history.replaceState({},document.title,i)}N(t){try{if(!t)return;sessionStorage.setItem(A.CALLBACK_TYPE_STORAGE_KEY,t),"recovery"===t||"invite"===t?sessionStorage.setItem(A.CALLBACK_REDIRECT_STORAGE_KEY,"PASSWORD_RECOVERY"):sessionStorage.removeItem(A.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}K(t){try{sessionStorage.setItem(A.RESET_TOKEN_STORAGE_KEY,t)}catch{}}C(){try{return sessionStorage.getItem(A.RESET_TOKEN_STORAGE_KEY)}catch{return null}}Y(){try{sessionStorage.removeItem(A.RESET_TOKEN_STORAGE_KEY)}catch{}}L(){try{sessionStorage.removeItem(A.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(A.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}j(t){if("undefined"==typeof window)return t;try{return new URL(t,window.location.origin).toString()}catch{return t}}B(t){c(t)&&(this.Y(),this.L(),this.p={code:t.code||"invalid_token",message:t.message||"The reset link is invalid or has expired"},this.v&&this.I("AUTH_ERROR"))}get user(){return this.t}get token(){return this.i}get lastError(){return this.p}get hasPasswordRecoveryToken(){return!!this.C()}consumeCallbackType(){try{const t=sessionStorage.getItem(A.CALLBACK_TYPE_STORAGE_KEY);return sessionStorage.removeItem(A.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(A.CALLBACK_REDIRECT_STORAGE_KEY),t}catch{return null}}get isPasswordRecoveryCallback(){try{return"PASSWORD_RECOVERY"===sessionStorage.getItem(A.CALLBACK_REDIRECT_STORAGE_KEY)||!!sessionStorage.getItem(A.RESET_TOKEN_STORAGE_KEY)}catch{return!1}}isLoggedIn(){return!(!this.i||!this.t)}onAuthStateChange(t){return g("log","[DYPAI SDK] 👂 Nuevo suscriptor añadido a onAuthStateChange"),this.u.push(t),this.P.then(()=>{const e=this.J();g("log",`[DYPAI SDK] 📣 INITIAL_SESSION para suscriptor (Sesión activa: ${!!e})`),t("INITIAL_SESSION",e),this.isPasswordRecoveryCallback?t("PASSWORD_RECOVERY",e):this.p&&t("AUTH_ERROR",null)}).catch(e=>{g("error","[DYPAI SDK] ❌ Error esperando recuperación de sesión para suscriptor:",e),t("INITIAL_SESSION",null)}),{data:{subscription:{unsubscribe:()=>{this.u=this.u.filter(e=>e!==t)}}}}}async signInWithPassword(t){return _((async()=>{const e=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-in/email`,i={email:t.email||t.identifier||"",password:t.password},s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(i)});if(!s.ok)throw await k(s,"Login failed");const n=await s.json(),r=this.W(n);return await this.q(r),r})())}async login(t){return this.signInWithPassword(t)}async signUp(t,e){return _((async()=>{const i=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-up/email`,{email:s,phone:n,password:r,user_data:o,...a}=t,c=s||"",h=a.full_name||a.name||c.split("@")[0],l={...a,...o||{}},u={email:c,password:r,name:h};Object.keys(l).length>0&&Object.assign(u,l),e?.redirectTo?u.callbackURL=e.redirectTo:"undefined"!=typeof window&&(u.callbackURL=window.location.href.split("#")[0]);const d=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(u)});if(!d.ok)throw await k(d,"Registration failed");const f=await d.json(),p=this.W(f),w={...p,confirmationRequired:!p.token};return w.token?await this.q(w):g("log","[DYPAI SDK] 📧 Signup exitoso, se requiere confirmación de email."),w})())}async register(t){return this.signUp(t)}async getSession(){try{if(await this.P,!this.i||!this.t)return{data:null,error:null};const t=Math.floor(Date.now()/1e3);return(this.l||0)-t<30+A.CLOCK_SKEW_TOLERANCE_S&&(this.h||this.o)&&(g("log","[DYPAI SDK] ⏳ getSession: Token próximo a expirar. Refrescando..."),await this.refreshSession().catch(t=>g("warn","[DYPAI SDK] Error refreshing session in getSession:",t))),{data:{access_token:this.i,refresh_token:this.h||void 0,token_type:"bearer",user:this.t},error:null}}catch(e){return{data:null,error:new t(e.message,500)}}}async getUser(){return _((async()=>{await this.P;const e=this.o||this.i;if(!e)throw new s("No hay sesión activa");const i=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,n=await fetch(i,{headers:{Authorization:`Bearer ${e}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!n.ok)throw new t("Session invalid",n.status);const r=await n.json(),o=r?.jwt||n.headers.get("set-auth-jwt");o&&(this.i=o);const a=this.M(r);return this.G(a),a})())}async me(){return this.getUser()}async signInWithOAuth(e,i={}){return _((async()=>{if("undefined"==typeof window)throw new t("signInWithOAuth requiere un entorno de navegador (window no está disponible)",400);const{redirectTo:s=window.location.href,scopes:n}=i,r=this.config.baseUrl||"http://localhost:8000",o=new URLSearchParams({provider:e,callbackURL:s});n?.length&&o.set("scopes",n.join(" "));const a=`${r}/api/v0/auth/sign-in/social?${o.toString()}`;window.location.href=a})())}async signOut(){return _((async()=>{try{const t=this.o||this.i;if(t){const e=this.config.baseUrl||"http://localhost:8000";await fetch(`${e}/api/v0/auth/sign-out`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}})}}finally{this.F("signOut called")}})())}async logout(){return this.signOut()}async resetPasswordForEmail(t,e){return _((async()=>{const i=this.config.baseUrl||"http://localhost:8000",s={email:t};e?.redirectTo?s.redirectTo=e.redirectTo:this.config.passwordRecoveryRedirect&&"undefined"!=typeof window?s.redirectTo=this.j(this.config.passwordRecoveryRedirect):"undefined"!=typeof window&&(s.redirectTo=window.location.href.split("#")[0]);const n=await fetch(`${i}/api/v0/auth/request-password-reset`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(s)});if(!n.ok)throw await k(n,"Recovery failed");return await n.json()})())}async recoverPassword(t){return this.resetPasswordForEmail(t.email)}async resendConfirmationEmail(t,e){return _((async()=>{const i=this.config.baseUrl||"http://localhost:8000",s={email:t};e?.redirectTo?s.callbackURL=e.redirectTo:"undefined"!=typeof window&&(s.callbackURL=window.location.href.split("#")[0]);const n=await fetch(`${i}/api/v0/auth/send-verification-email`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(s)});if(!n.ok)throw await k(n,"Failed to resend confirmation email");return{message:"Confirmation email resent"}})())}async refreshSession(){if(this.S)return g("log","[DYPAI SDK] 🔄 Refresco de sesión ya en curso. Esperando resolución..."),this.S.promise;const e=new b;this.S=e;const i=`lock:${this.STORAGE_KEY}`;return(async()=>{try{const t=await async function(t,e,i){if("undefined"!=typeof globalThis&&globalThis.navigator?.locks?.request){const s=new AbortController,n=setTimeout(()=>s.abort(),e);try{return await globalThis.navigator.locks.request(t,{signal:s.signal},async()=>(clearTimeout(n),await i()))}catch(s){if("AbortError"===s.name)return console.warn(`[DYPAI SDK] ⚠️ Web Lock "${t}" timeout (${e}ms). Proceeding without lock.`),await i();throw s}}return await i()}(i,A.LOCK_ACQUIRE_TIMEOUT_MS,()=>this.V());e.resolve(t)}catch(i){const s=i instanceof t?i:new t(i.message||"Error refrescando sesión",401);e.resolve({data:null,error:s})}finally{this.S=null}})(),e.promise}async V(){try{const e=await this.storage.getItem(this.STORAGE_KEY);if(e){const t=JSON.parse(e),i=t.expires_at||0,s=Math.floor(Date.now()/1e3);if(t.access_token!==this.i&&i-s>60)return g("log","[DYPAI SDK] 🔄 Otra pestaña ya refrescó el token. Adoptando sesión del storage."),this.i=t.access_token,this.o=t.session_token||t.access_token,this.h=t.refresh_token,this.l=t.expires_at,this.t=t.user,this.D=0,this.I("TOKEN_REFRESHED"),{data:{token:this.i,sessionToken:this.o||void 0,refreshToken:this.h||void 0,expiresAt:this.l||void 0,user:this.t},error:null}}const i=this.o||this.i;if(!i)throw new s("No hay sesión activa");g("log","[DYPAI SDK] 🔄 Verificando sesión activa con session_token...");const n=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,r=new AbortController,o=setTimeout(()=>r.abort(),A.REFRESH_TIMEOUT_MS);let a;try{a=await fetch(n,{method:"GET",headers:{Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},signal:r.signal})}finally{clearTimeout(o)}if(!a.ok){let e={};try{e=await a.json()}catch{}const i=a.status,s=e.error||e.code||"",n=(e.error_description||e.msg||"").toLowerCase(),r=this.O(i),o=i>=400&&i<500&&!r,c="invalid_grant"===s||n.includes("invalid refresh token")||n.includes("revoked")||n.includes("expired")||n.includes("invalid token");throw o&&c?(g("error",`[DYPAI SDK] ❌ Token inválido/revocado (${i}). Limpiando sesión.`),this.F(`refreshSession failed (${i}: ${s})`)):o?(g("error",`[DYPAI SDK] ❌ Error 4xx en refresh (${i}). Limpiando sesión.`),this.F(`refreshSession failed (${i})`)):g("warn",`[DYPAI SDK] ⚠️ Error transitorio en refresh (${i}). Manteniendo sesión.`),new t(e.msg||e.error_description||"Refresh session failed",i)}const c=await a.json(),h=a.headers.get("set-auth-jwt"),l=c?.jwt,u=l||h,d=this.W(c,this.i);return u&&(d.token=u),d.sessionToken=i,this.D=0,await this.q(d),g("log",`[DYPAI SDK] ✅ Session refresh exitoso (fresh JWT: ${!!u}).`),{data:d,error:null}}catch(e){this.D++,this._=Date.now();const i=e instanceof DOMException&&"AbortError"===e.name,s=i?new t("Refresh token timeout (servidor no responde)",408):e instanceof t?e:new t(e.message||"Error refrescando sesión",401),n=Math.min(A.RETRY_BASE_MS*Math.pow(2,this.D),A.MAX_RETRY_MS);return i?g("error",`[DYPAI SDK] ⏱️ Timeout en refresh después de ${A.REFRESH_TIMEOUT_MS/1e3}s. Backoff: ${n}ms (intento ${this.D})`):g("error","[DYPAI SDK] ❌ Refresh falló:",s.message,`(status: ${s.status}). Backoff: ${n}ms (intento ${this.D})`),this.D>=A.MAX_REFRESH_FAILURES&&(g("error",`[DYPAI SDK] 🗑️ ${this.D} fallos consecutivos de refresh. Sesión irrecuperable. Limpiando...`),this.F(`${this.D} consecutive refresh failures`),this.D=0),{data:null,error:s}}}async signInWithOtp(e){return _((async()=>{if(e.phone)throw new t("Phone OTP is not supported by the current DYPAI auth backend",400);if(!e.email)throw new t("Email is required for magic link sign-in",400);const i=await this.signInWithMagicLink(e.email);if(i.error)throw i.error;return{status:!0}})())}async verifyOtp(e){return _((async()=>{if(!e.token)throw new t("Verification token is required",400);if("magiclink"===e.type)return await this.H(e.token);if("signup"===e.type||"email_change"===e.type){const i=await this.X(e.token);if(i)return i;throw new t("Verification completed but no session was returned",400)}if("recovery"===e.type||"invite"===e.type)throw new t("Recovery links do not use verifyOtp(). Use setPassword() after the PASSWORD_RECOVERY callback.",400);throw new t("Unsupported verification type",400)})())}async updateUser(e){return _((async()=>{const i=this.o||this.i;if(!i)throw new s("No hay sesión activa");const n=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/update-user`,r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(e)});if(!r.ok){const e=await r.json();throw new t(e.detail||"Update user failed",r.status)}const o=await r.json(),a=this.M(o);return this.G(a),a})())}async setPassword(e){return _((async()=>{if("undefined"==typeof window)throw new t("setPassword requiere un entorno de navegador",400);const i=new URLSearchParams(window.location.search).get("token")||this.C();if(!i){if(this.o||this.i){const t=await this.updateUser({password:e});if(t.error)throw t.error;return this.L(),this.U(),{status:!0}}throw new t("No reset token found in the current URL",400)}const s=this.config.baseUrl||"http://localhost:8000",n=await fetch(`${s}/api/v0/auth/reset-password`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({newPassword:e,token:i})});if(!n.ok){const t=await k(n,"Reset password failed");throw this.B(t),t}this.Y(),this.L(),this.U();const r=await n.json();if(r?.jwt&&r?.session_token&&r?.user){const t={token:r.jwt,sessionToken:r.session_token,user:this.M(r),expiresAt:r.session?.expiresAt?Math.floor(new Date(r.session.expiresAt).getTime()/1e3):void 0};await this.q(t)}return{status:r?.status??!0}})())}async signInWithMagicLink(t,e){return _((async()=>{const i=this.config.baseUrl||"http://localhost:8000",s=e?.redirectTo||("undefined"!=typeof window?window.location.href.split("#")[0]:void 0),n=await fetch(`${i}/api/v0/auth/sign-in/magic-link`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({email:t,callbackURL:s})});if(!n.ok)throw await k(n,"Magic link failed");return{message:"Magic link sent"}})())}async enableTwoFactor(){return _((async()=>{const t=this.o||this.i;if(!t)throw new s("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/two-factor/enable`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await k(i,"Enable 2FA failed");return await i.json()})())}async verifyTwoFactor(t){return _((async()=>{const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/two-factor/verify-totp`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.o||this.i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({code:t})});if(!i.ok)throw await k(i,"2FA verification failed");const s=await i.json(),n={token:s.jwt||s.token||this.i,expiresIn:604800,expiresAt:Math.floor(Date.now()/1e3)+604800,user:this.M(s)};return await this.q(n),n})())}async disableTwoFactor(){return _((async()=>{const t=this.o||this.i;if(!t)throw new s("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/two-factor/disable`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await k(i,"Disable 2FA failed");return{message:"2FA disabled"}})())}async createOrganization(t,e){return _((async()=>{const i=this.o||this.i;if(!i)throw new s("No hay sesión activa");const n=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${n}/api/v0/auth/organization/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({name:t,slug:e})});if(!r.ok)throw await k(r,"Create organization failed");return await r.json()})())}async inviteMember(t,e,i){return _((async()=>{const n=this.o||this.i;if(!n)throw new s("No hay sesión activa");const r=this.config.baseUrl||"http://localhost:8000",o=await fetch(`${r}/api/v0/auth/organization/invite-member`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({organizationId:t,email:e,role:i||"member"})});if(!o.ok)throw await k(o,"Invite member failed");return await o.json()})())}async listOrganizations(){return _((async()=>{const t=this.o||this.i;if(!t)throw new s("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${e}/api/v0/auth/organization/list`,{headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await k(i,"List organizations failed");return await i.json()})())}startAutoRefresh(){this.stopAutoRefresh(),g("log","[DYPAI SDK] ⏱️ Auto-refresh iniciado (tick cada 30s)."),this.Z(),this.k=setInterval(()=>this.Z(),A.AUTO_REFRESH_TICK_DURATION_MS)}stopAutoRefresh(){this.k&&(g("log","[DYPAI SDK] ⏹️ Auto-refresh detenido."),clearInterval(this.k),this.k=null)}async Z(){try{const t=Math.floor(Date.now()/1e3),e=this.l||0;if(!this.h&&!this.o||!e)return;if(1e3*(e-t)>A.AUTO_REFRESH_TICK_THRESHOLD*A.AUTO_REFRESH_TICK_DURATION_MS+1e3*A.CLOCK_SKEW_TOLERANCE_S)return;if(this.D>0){const t=Math.min(A.RETRY_BASE_MS*Math.pow(2,this.D),A.MAX_RETRY_MS);if(Date.now()-this._<t)return}g("log",`[DYPAI SDK] ⏱️ Auto-refresh tick: token expira en ${e-t}s. Refrescando...`),await this.refreshSession()}catch(t){g("error","[DYPAI SDK] ❌ Error en auto-refresh tick:",t)}}M(t){return t?e(t):(g("warn","[DYPAI SDK] ⚠️ Intentando normalizar un usuario inexistente (data es null/undefined)"),{})}W(t,e){const i="number"==typeof t?.expiresIn?t.expiresIn:"number"==typeof t?.expires_in?t.expires_in:604800,s=t?.jwt||void 0,n=t?.session_token||t?.token||t?.access_token||t?.session?.token||t?.session?.accessToken||t?.session?.access_token||e;return{token:s||n,sessionToken:n,refreshToken:t?.refreshToken||t?.refresh_token||t?.session?.refreshToken||t?.session?.refresh_token||void 0,expiresIn:i,expiresAt:Math.floor(Date.now()/1e3)+i,user:this.M(t)}}async q(t){this.p=null,t.token&&(this.i=t.token,this.o=t.sessionToken||t.token,void 0!==t.refreshToken&&(this.h=t.refreshToken||null),void 0!==t.expiresAt&&(this.l=t.expiresAt||null),await this.G(t.user,t.token,t.refreshToken,t.expiresAt),g("log",`[DYPAI SDK] 💾 Sesión persistida en storage (expires_at: ${this.l}, has_refresh: ${!!this.h}, has_session_token: ${!!this.o})`),this.startAutoRefresh())}async G(t,e,i,s){this.t=t,e&&(this.i=e),void 0!==i&&(this.h=i||null),void 0!==s&&(this.l=s||null);try{if(this.i&&this.t){const t={access_token:this.i,session_token:this.o,refresh_token:this.h,expires_at:this.l,user:this.t,version:2};await this.storage.setItem(this.STORAGE_KEY,JSON.stringify(t)),this.R("SESSION_UPDATED")}else g("warn","[DYPAI SDK] ⚠️ _updateUser: No se guardó sesión porque falta token o user.")}catch(t){g("error","[DYPAI SDK] ❌ Error guardando sesión en storage:",t)}if(this.v){const t=e?"SIGNED_IN":"USER_UPDATED";this.I(t)}}async F(t="unknown"){g("log",`[DYPAI SDK] 🧹 Limpiando sesión del estado y storage. Motivo: ${t}`),this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh();try{await this.storage.removeItem(this.STORAGE_KEY),g("log","[DYPAI SDK] ✅ Storage limpiado con éxito.")}catch(t){g("error","[DYPAI SDK] ❌ Error eliminando sesión de storage:",t)}this.R("SIGNED_OUT"),this.I("SIGNED_OUT")}async H(t){const e=this.getBaseUrl(),i=new URL(`${e}/api/v0/auth/magic-link/verify`);i.searchParams.set("token",t);const s=await fetch(i.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!s.ok)throw await k(s,"Magic link verification failed");const n=await s.json(),r=this.W(n);return await this.q(r),r}async X(t){const e=this.getBaseUrl(),i=new URL(`${e}/api/v0/auth/verify-email`);i.searchParams.set("token",t);const s=await fetch(i.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!s.ok)throw await k(s,"Email verification failed");if(!(s.headers.get("content-type")||s.headers.get("Content-Type")||"").includes("application/json"))return null;const n=await s.json(),r=this.W(n);return r.token&&r.user?.id?(await this.q(r),r):null}async tt(t,e){const i=e||("undefined"!=typeof window&&window.location.pathname.includes("reset-password")?"recovery":null);if(!i)return!1;if(g("log",`[DYPAI SDK] 🔗 Auth callback detectado en query (type: ${i}). Procesando...`),this.N(i),"recovery"===i||"invite"===i)return this.K(t),this.U(),this.m="PASSWORD_RECOVERY",!0;if(this.U(),"magiclink"===i)return await this.H(t),this.m="SIGNED_IN",!0;if("signup"===i||"email_change"===i){const e=await this.X(t);return this.m=e?.token?"SIGNED_IN":null,!0}return!1}async T(t=!0){g("log","[DYPAI SDK] 🔍 Iniciando recuperación de sesión...");try{if(await this.et())return void g("log","[DYPAI SDK] ✅ Sesión establecida desde callback URL. Saltando recuperación de localStorage.");const e=await this.storage.getItem(this.STORAGE_KEY);if(!e)return void g("log","[DYPAI SDK] ℹ️ No se encontró ninguna sesión (storage vacío).");let i;try{i=JSON.parse(e)}catch{return g("warn","[DYPAI SDK] ⚠️ Sesión corrupta (JSON inválido). Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY)}if(!i||"object"!=typeof i||"string"!=typeof i.access_token)return g("warn","[DYPAI SDK] ⚠️ Sesión con formato inválido. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if((i.version||1)<2&&!i.session_token)return g("warn","[DYPAI SDK] 🔄 Sesión en formato antiguo (v1) sin session_token. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if(this.i=i.access_token,this.o="string"==typeof i.session_token?i.session_token:i.access_token,this.h="string"==typeof i.refresh_token?i.refresh_token:null,this.l="number"==typeof i.expires_at?i.expires_at:null,this.t=i.user&&"object"==typeof i.user?i.user:null,t){g("log","[DYPAI SDK] 🔄 Validando sesión con el servidor...");const t=await this.refreshSession();if(t.error)return void g("warn","[DYPAI SDK] ⚠️ Sesión inválida. Limpiando:",t.error.message)}this.m="SIGNED_IN"}catch(t){g("error","[DYPAI SDK] ❌ Error crítico durante la recuperación de sesión:",t)}}async et(){if("undefined"==typeof window)return!1;try{const t=new URLSearchParams(window.location.search),e=t.get("error"),i=t.get("token"),s=t.get("type");if(e){const i=t.get("error_code")||e,s=t.get("error_description")?.replace(/\+/g," ")||"Authentication error";return g("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${i} — ${s}`),this.p={code:i,message:s},this.U(),this.m="AUTH_ERROR",!0}if(i)return await this.tt(i,s);const n=window.location.hash.substring(1);if(!n)return!1;const r=new URLSearchParams(n);if(r.get("error")){const t=r.get("error_code")||"unknown",e=r.get("error_description")?.replace(/\+/g," ")||"Authentication error";return g("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${t} — ${e}`),this.p={code:t,message:e},this.U(),this.m="AUTH_ERROR",!0}const o=r.get("access_token");if(!o)return!1;const a=r.get("refresh_token"),c=r.get("type"),h=parseInt(r.get("expires_in")||"3600",10);g("log",`[DYPAI SDK] 🔗 Auth callback detectado en URL hash (type: ${c||"unknown"}). Procesando...`),this.N(c),this.U(),this.o=o,this.i=o,this.h=a||null,this.l=Math.floor(Date.now()/1e3)+h;const l=await fetch(`${this.getBaseUrl()}/api/v0/auth/get-session`,{headers:{Authorization:`Bearer ${o}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!l.ok)return g("error",`[DYPAI SDK] ❌ Callback URL: token inválido o expirado (status: ${l.status}). Limpiando...`),this.i=null,this.o=null,this.h=null,this.l=null,this.p={code:"token_invalid",message:"The authentication link is invalid or has expired"},this.I("AUTH_ERROR"),!0;const u=await l.json(),d=u?.jwt||l.headers.get("set-auth-jwt"),f=this.M(u),p={token:d||o,sessionToken:o,refreshToken:a||void 0,expiresIn:h,expiresAt:this.l,user:f};return await this.q(p),this.m="recovery"===c||"invite"===c?"PASSWORD_RECOVERY":"SIGNED_IN",g("log",`[DYPAI SDK] 🔑 Callback type=${c}. Pending event: ${this.m}`),!0}catch(t){return g("error","[DYPAI SDK] ❌ Error procesando callback URL:",t),!1}}J(){return this.i&&this.t?{access_token:this.i,refresh_token:this.h||void 0,expires_at:this.l||void 0,token_type:"bearer",user:this.t}:null}I(t="USER_UPDATED"){const e=this.J();g("log",`[DYPAI SDK] 📢 Notificando a ${this.u.length} suscriptores: Evento=${t} (Sesión activa: ${!!e})`),this.u.forEach(i=>i(t,e))}handleSessionExpired(){this.F("handleSessionExpired called (likely 401 from API)")}}A.REFRESH_TIMEOUT_MS=15e3,A.MAX_REFRESH_FAILURES=5,A.AUTO_REFRESH_TICK_DURATION_MS=3e4,A.AUTO_REFRESH_TICK_THRESHOLD=3,A.RETRY_BASE_MS=200,A.MAX_RETRY_MS=3e4,A.LOCK_ACQUIRE_TIMEOUT_MS=5e3,A.CLOCK_SKEW_TOLERANCE_S=10,A.CALLBACK_TYPE_STORAGE_KEY="dypai-auth-callback-type",A.CALLBACK_REDIRECT_STORAGE_KEY="dypai-auth-callback-redirect",A.RESET_TOKEN_STORAGE_KEY="dypai-auth-reset-token";class P{constructor(t,e,i){this.ws=null,this.bindings=[],this.it={},this.st=!1,this.topic=t,this.client=e,this.config=i||{}}on(t,e,i){return this.bindings.push({type:t,filter:e,callback:i}),this}subscribe(t){this.nt=t;const e=this.client.rt();this.ws=e;const i=()=>{const i=this.bindings.filter(t=>"postgres_changes"===t.type);for(const t of i)e.send(JSON.stringify({type:"subscribe",table:t.filter.table||"*",event:t.filter.event||"*",filter:t.filter.filter?this.ot(t.filter.filter):void 0,schema:t.filter.schema||"public"}));const s=this.bindings.some(t=>"broadcast"===t.type),n=this.bindings.some(t=>"presence"===t.type);(s||n)&&e.send(JSON.stringify({type:"join",channel:this.topic,presence:!!n||void 0})),this.st=!0,t?.("SUBSCRIBED")};e.readyState===WebSocket.OPEN?i():e.readyState===WebSocket.CONNECTING&&e.addEventListener("open",i,{once:!0}),this.ct&&e.removeEventListener("message",this.ct);const s=t=>{try{const e=JSON.parse("string"==typeof t.data?t.data:"");this.ht(e)}catch{}};return this.ct=s,e.addEventListener("message",s),e.addEventListener("close",()=>{this.st=!1,t?.("CLOSED")},{once:!0}),e.addEventListener("error",()=>{t?.("CHANNEL_ERROR",new Error("WebSocket error"))},{once:!0}),this}ut(t){const e=this.bindings.filter(t=>"postgres_changes"===t.type);for(const i of e)t.send(JSON.stringify({type:"subscribe",table:i.filter.table||"*",event:i.filter.event||"*",filter:i.filter.filter?this.ot(i.filter.filter):void 0,schema:i.filter.schema||"public"}));const i=this.bindings.some(t=>"broadcast"===t.type),s=this.bindings.some(t=>"presence"===t.type);(i||s)&&t.send(JSON.stringify({type:"join",channel:this.topic,presence:!!s||void 0})),this.nt?.("SUBSCRIBED")}async send(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"broadcast",channel:this.topic,event:t.event,payload:t.payload,self:this.config.broadcast?.self||!1})),"ok"}catch{return"error"}}async track(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"presence",channel:this.topic,data:t})),"ok"}catch{return"error"}}async untrack(){return"ok"}presenceState(){return{...this.it}}async unsubscribe(){if(this.ct&&this.ws&&(this.ws.removeEventListener("message",this.ct),this.ct=void 0),this.ws?.readyState===WebSocket.OPEN){const t=this.bindings.filter(t=>"postgres_changes"===t.type);for(const e of t)this.ws.send(JSON.stringify({type:"unsubscribe",table:e.filter.table}));this.ws.send(JSON.stringify({type:"leave",channel:this.topic}))}return this.st=!1,this.bindings=[],this.it={},"ok"}ht(t){if("change"===t.type)for(const e of this.bindings)"postgres_changes"===e.type&&(e.filter.table&&"*"!==e.filter.table&&e.filter.table!==t.table||e.filter.event&&"*"!==e.filter.event&&e.filter.event!==t.event||e.callback({schema:"public",table:t.table,eventType:t.event,new:t.record||{},old:t.old_record||{},commit_timestamp:t.timestamp,truncated:t.truncated||!1}));if("broadcast"===t.type&&t.channel===this.topic)for(const e of this.bindings)"broadcast"===e.type&&("*"!==e.filter.event&&e.filter.event!==t.event||e.callback({event:t.event,payload:t.payload,sender:t.sender,timestamp:t.timestamp}));if("presence_state"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";this.it={};for(const[i,s]of Object.entries(t.presences||{})){const t=s[e]||i;this.it[t]||(this.it[t]=[]),this.it[t].push(s)}for(const t of this.bindings)"presence"===t.type&&("sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"}))}if("presence_diff"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";if(t.joins)for(const[i,s]of Object.entries(t.joins)){const t=s[e]||i;this.it[t]||(this.it[t]=[]),this.it[t].push(s);for(const e of this.bindings)"presence"===e.type&&("join"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"join",key:t,newPresences:[s],currentPresences:this.it[t]}))}if(t.leaves)for(const[i,s]of Object.entries(t.leaves)){const t=s[e]||i;this.it[t]=(this.it[t]||[]).filter(t=>t.userId!==i),0===this.it[t].length&&delete this.it[t];for(const e of this.bindings)"presence"===e.type&&("leave"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"leave",key:t,leftPresences:[s],currentPresences:this.it[t]||[]}))}for(const t of this.bindings)"presence"!==t.type||"sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"})}}ot(t){const e=t.match(/^(\w+)=eq\.(.+)$/);if(e)return{[e[1]]:e[2]}}}class E{constructor(t,e){this.ws=null,this.channels=new Map,this.heartbeatTimer=null,this.reconnectAttempt=0,this.reconnectTimer=null,this.baseUrl=t,this.tokenProvider=e}channel(t,e){const i=this.channels.get(t);if(i)return i;const s=new P(t,this,e?.config);return this.channels.set(t,s),s}getChannels(){return Array.from(this.channels.values())}async removeChannel(t){await t.unsubscribe(),this.channels.delete(t.topic),0===this.channels.size&&this.disconnect()}async removeAllChannels(){for(const t of this.channels.values())await t.unsubscribe();this.channels.clear(),this.disconnect()}async setAuth(t){t&&(this.tokenProvider=()=>t),this.ws&&this.channels.size>0&&(this.disconnect(),this.rt())}disconnect(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.ws?.close(),this.ws=null}rt(){if(this.ws&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING))return this.ws;const t=this.tokenProvider(),e=this.baseUrl.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(t||"")}`,i=new WebSocket(e);return this.ws=i,i.addEventListener("open",()=>{this.reconnectAttempt=0;for(const t of this.channels.values())t.st&&t.ut(i);this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{i.readyState===WebSocket.OPEN&&i.send(JSON.stringify({type:"heartbeat"}))},25e3)}),i.addEventListener("close",()=>{if(this.ws=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.channels.size>0&&!this.reconnectTimer){const t=E.RECONNECT_INTERVALS[Math.min(this.reconnectAttempt,E.RECONNECT_INTERVALS.length-1)];this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.channels.size>0&&this.rt()},t)}}),i}}E.RECONNECT_INTERVALS=[1e3,2e3,5e3,1e4,3e4];class T{get direct(){if(!this.dt)throw new Error("client.db.direct is not available. Pass serviceRoleKey to createClient() to enable direct database access.\n\nExample:\n const client = createClient(url, { serviceRoleKey: process.env.DYPAI_SERVICE_ROLE_KEY });\n\nWARNING: serviceRoleKey grants admin-level access. Only use from server-side code (scripts, migrations, seeds). Never expose it in browser/client-side code.");return this.dt}set direct(t){this.dt=t}constructor(t){this.api=t}from(t){return new I(t,this.api)}}class I{constructor(t,e){this.table=t,this.api=e}async select(t={}){return this.api.get(this.table,{params:t})}async insert(t){return this.api.post(this.table,t)}async update(t,e){return this.api.patch(`${this.table}/${t}`,e)}async delete(t){return this.api.delete(`${this.table}/${t}`)}}const $=1e3;class O{constructor(t){this.api=t}from(t){return new R(t,this.api)}async sql(t,e,i){return this.api.post("/api/v0/admin/db/sql",{query:t,params:e||[],...void 0!==i?.limit&&{limit:i.limit}})}}class R{constructor(t,e){this.table=t,this.api=e,this.ft="public",this.wt=[],this.yt=100,this.vt=0,this.St="ASC"}schema(t){return this.ft=t,this}eq(t,e){return this.wt.push({column:t,operator:"eq",value:e}),this}neq(t,e){return this.wt.push({column:t,operator:"neq",value:e}),this}gt(t,e){return this.wt.push({column:t,operator:"gt",value:e}),this}gte(t,e){return this.wt.push({column:t,operator:"gte",value:e}),this}lt(t,e){return this.wt.push({column:t,operator:"lt",value:e}),this}lte(t,e){return this.wt.push({column:t,operator:"lte",value:e}),this}like(t,e){return this.wt.push({column:t,operator:"ilike",value:e}),this}ilike(t,e){return this.like(t,e)}isNull(t){return this.wt.push({column:t,operator:"is_null",value:null}),this}is(t,e){return this.isNull(t)}notNull(t){return this.wt.push({column:t,operator:"not_null",value:null}),this}in(t,e){return this.wt.push({column:t,operator:"in",value:e}),this}contains(t,e){return this.wt.push({column:t,operator:"array_contains",value:e}),this}containedBy(t,e){return this.wt.push({column:t,operator:"contained_by",value:e}),this}overlaps(t,e){return this.wt.push({column:t,operator:"overlaps",value:e}),this}textSearch(t,e){return this.wt.push({column:t,operator:"fts",value:e}),this}phraseSearch(t,e){return this.wt.push({column:t,operator:"phfts",value:e}),this}not(t,e,i){return this.wt.push({column:t,operator:"not",value:{operator:e,value:i}}),this}or(t){return this.wt.push({operator:"or",or:t}),this}limit(t){return this.yt=t,this}offset(t){return this.vt=t,this}orderBy(t,e="ASC"){return this.kt=t,this.St=e,this}async select(t){const e=[...this.wt];let i=this.bt;if("string"==typeof t)i=t;else if(t&&"object"==typeof t)for(const[i,s]of Object.entries(t))e.push({column:i,operator:"eq",value:s});return this.execute("select",{...i&&{columns:i},filters:e,limit:this.yt,offset:this.vt,sort_by:this.kt,order:this.St})}async single(){const e=await this.select();if(e.error)return e;const i=e.data;return!i||Array.isArray(i)&&0===i.length?{data:null,error:new t("No rows found.",404)}:{data:Array.isArray(i)?i[0]:i,error:null}}async maybeSingle(){const t=await this.select();if(t.error)return t;const e=t.data;return!e||Array.isArray(e)&&0===e.length?{data:null,error:null}:{data:Array.isArray(e)?e[0]:e,error:null}}async count(){const t=await this.execute("select",{columns:"count(*) as count",filters:[...this.wt],limit:1,offset:0});if(t.error)return t;const e=t.data;return{data:Array.isArray(e)&&e.length>0?Number(e[0].count):0,error:null}}async insert(t){if(Array.isArray(t)&&t.length>$){const e=[];for(let i=0;i<t.length;i+=$){const s=t.slice(i,i+$),{data:n,error:r}=await this.execute("insert",{data:s,mode:"bulk"});if(r)return{data:null,error:r};n&&e.push(...Array.isArray(n)?n:[n])}return{data:e,error:null}}const e=Array.isArray(t)?"bulk":"single";return this.execute("insert",{data:t,mode:e})}async update(e){return 0===this.wt.length?{data:null,error:new t("Update requires at least one filter. Use .eq(), .in(), etc.",400)}:this.execute("update",{data:e,filters:this.wt})}async delete(){return 0===this.wt.length?{data:null,error:new t("Delete requires at least one filter.",400)}:this.execute("delete",{filters:this.wt})}async upsert(t,e="id"){return this.execute("upsert",{data:t,conflict_column:e})}async execute(t,e){return this.api.post("/api/v0/admin/db/query",{operation:t,schema_name:this.ft,table_name:this.table,...e})}}class U{constructor(t){this.api=t}async list(t={}){const i=await this.api.get("admin/users",{params:t});return i.data?.users&&(i.data.users=i.data.users.map(e)),i}async create(t){const i=await this.api.post("admin/users",t);return i.data&&(i.data=e(i.data)),i}async update(t,i){const s=await this.api.put(`admin/users/${t}`,i);return s.data&&(s.data=e(s.data)),s}async delete(t){return this.api.delete(`admin/users/${t}`)}}class x{constructor(t){this.api=t}async list(t={}){const e={};void 0!==t.include_global&&(e.include_global=t.include_global);const i=await this.api.get("admin/roles",{params:e});return Array.isArray(i.data)?i.data={roles:i.data,total:i.data.length}:i.data?.roles&&null==i.data.total&&(i.data.total=i.data.roles.length),i}async get(t){return this.api.get(`admin/roles/${t}`)}async create(t){return this.api.post("admin/roles",t)}async update(t,e){return this.api.put(`admin/roles/${t}`,e)}async delete(t){return this.api.delete(`admin/roles/${t}`)}}let N=null;function K(t){N=t}const C=t=>{const{title:e,description:i,variant:s="default"}=t,n=`${"error"===s?"❌":"success"===s?"✅":"warning"===s?"⚠️":"info"===s?"ℹ️":"📢"} ${e}${i?`: ${i}`:""}`;"error"===s?console.error(n):"warning"===s&&console.warn(n)};function Y(){const t=N||C;return{toast:t,toastSuccess:(e,i)=>t({title:e,description:i,variant:"success"}),toastError:(e,i)=>t({title:e,description:i,variant:"error"}),toastWarning:(e,i)=>t({title:e,description:i,variant:"warning"}),toastInfo:(e,i)=>t({title:e,description:i,variant:"info"})}}const L=t=>(N||C)(t),j=(t,e)=>L({title:t,description:e,variant:"success"}),B=(t,e)=>L({title:t,description:e,variant:"error"}),z=(t,e)=>L({title:t,description:e,variant:"warning"}),J=(t,e)=>L({title:t,description:e,variant:"info"});let W={},q=null;function M(t){q=t}function G(t){W={...W,...t}}const F=new Map,V=new Set(["params","token","apiKey","showToasts"]);function H(e,i,s){if(null==s)return;if("object"!=typeof s||Array.isArray(s))throw new t(`Invalid ${e} options for endpoint "${i}": options must be an object. Use { params: { ... } } for query parameters.`,400,"invalid_api_options");const n=Object.keys(s).filter(t=>!V.has(t));if(!n.length)return;const r=n.map(t=>`"${t}"`).join(", "),o=n[0],a=e.toLowerCase();throw new t(`Invalid ${e} options for endpoint "${i}": unknown option ${r}. ${"GET"===e||"DELETE"===e?`Use client.api.${a}("${i}", { params: { ${o}: value } }).`:`Pass request data as the second argument and options as the third argument, for example client.api.${a}("${i}", body, { params: { ${o}: value } }).`}`,400,"invalid_api_options")}function Q(){let t=W;try{const{getGlobalConfig:e}=require("../config/global-config");t={...e(),...W}}catch(t){}return t}async function X(e,i,s,n,r,o,a,c){const h=Q(),l=c||null;if(!l&&!s.startsWith("http"))throw new Error("Base URL no definida. Usa createClient(url[, apiKey]).");if("string"!=typeof s||!s.trim())throw new Error("Endpoint debe ser un string válido");let u;if(s.startsWith("http"))u=s;else{const t=l.replace(/\/+$/,"");u=s.startsWith("/")?t+s:t+"/api/v0/"+s}if(o&&Object.keys(o).length>0){const t=new URLSearchParams,e=(i,s)=>{null!=s&&(Array.isArray(s)?s.forEach((t,s)=>e(`${i}[${s}]`,t)):"object"==typeof s?Object.entries(s).forEach(([t,s])=>e(`${i}[${t}]`,s)):t.append(i,String(s)))};Object.entries(o).forEach(([t,i])=>e(t,i));const i=t.toString();i&&(u+=`?${i}`)}const d="GET"===i?`${i}:${u}:${JSON.stringify(n)}`:null;if(d&&F.has(d))return F.get(d);const f=n instanceof FormData,p={...h.headers||{},...f?{}:{"Content-Type":"application/json"},...e&&{Authorization:`Bearer ${e}`},...a&&{"x-api-key":a}},w={method:i,headers:p,credentials:"include"};n&&"GET"!==i&&"DELETE"!==i&&(w.body=f?n:JSON.stringify(n));const y=h.fetch||("undefined"!=typeof window?window.fetch.bind(window):fetch);if(!y)throw new Error("Fetch no disponible.");const m=(async()=>{const e=Q().toast||L,s=(void 0!==r?r:!1!==h.showToasts)&&e;try{const n=await y(u,w);if(!n.ok){let i,r="Error en la petición";try{const t=await n.text();try{const e=JSON.parse(t);i=e,r=e.message||e.msg||e.error_description||e.error||r}catch{t.length<200&&(r=t)}}catch{}throw 401===n.status&&h.onUnauthorized&&h.onUnauthorized(),s&&e({title:"Error",description:r,variant:"error"}),new t(r,n.status,void 0,i)}!s||"POST"!==i&&"PUT"!==i&&"PATCH"!==i&&"DELETE"!==i||e({title:"Éxito",description:"Operación completada",variant:"success"});const r=n.headers.get("content-type")||"";return r.includes("application/pdf")||r.includes("image/")||r.includes("audio/")||r.includes("video/")||r.includes("application/octet-stream")||r.includes("application/zip")||r.includes("application/vnd.openxmlformats-officedocument")?await n.blob():r.includes("application/json")?await n.json():await n.text()}finally{d&&F.delete(d)}})();return d&&F.set(d,m),m}async function Z(t,e,i,s){const n=s?.method||(i?"POST":"GET"),{data:r,error:o}=await("GET"===n?t.get(e,{params:s?.params}):t.post(e,i,{params:s?.params}));if(o)throw o;if(r instanceof Blob){const t=window.URL.createObjectURL(r),e=document.createElement("a");e.href=t,e.download=s?.fileName||"archivo-descargado",document.body.appendChild(e),e.click(),window.URL.revokeObjectURL(t),document.body.removeChild(e)}else if(r&&"object"==typeof r&&("url"in r||"signed_url"in r||"signedUrl"in r)){const t=r.url||r.signed_url||r.signedUrl;if("string"==typeof t){const e=new URL(t).searchParams.get("response-content-disposition");let i;if(e){const t=decodeURIComponent(e).match(/filename\*?=(?:UTF-8''|")?([^";]+)/i);t?.[1]&&(i=t[1].replace(/"/g,""))}const n=s?.fileName||i;if(n){const e=document.createElement("a");e.href=t,e.download=n,document.body.appendChild(e),e.click(),document.body.removeChild(e)}else window.open(t,"_blank")}}}async function tt(e,i,s,n){const r={...n?.params||{}};delete r.confirm,delete r.client_upload;const{data:o,error:a}=await e.post(i,{file_path:s.name,content_type:s.type||"application/octet-stream",size_bytes:s.size,...r,confirm:!1,client_upload:!0});if(a)throw a;const c=function(t){if(!t||"object"!=typeof t)return null;const e=t.upload_url||t.uploadUrl||t.url?t:null;if(e?.upload_url)return et(e);const i=[t.data,t.result,t.output,t.payload].filter(Boolean);for(const t of i)if(t&&(t.upload_url||t.uploadUrl||t.url))return et(t);const s=[t.steps_results,t.nodes_results].filter(Boolean);for(const t of s){const e=Array.isArray(t)?t:Object.values(t);for(const t of e)if(t&&(t.upload_url||t.uploadUrl||t.url))return et(t)}return null}(o);if(!c?.upload_url)throw new t("The workflow did not return a valid upload URL (missing storage upload node?)",400);const{upload_url:h,method:l="PUT",headers:u={},file_path:d,storage_path:f}=c;n?.onProgress&&n.onProgress(10);const p=await fetch(h,{method:l,headers:{"Content-Type":s.type||"application/octet-stream",...u},body:s});if(!p.ok)throw new t("Direct upload to cloud storage failed",p.status);n?.onProgress&&n.onProgress(90);const w=n?.confirmEndpoint||i,{data:y,error:m}=await e.post(w,{...r,bucket:c.bucket||r.bucket,file_path:d,storage_path:f,filename:s.name,content_type:s.type||"application/octet-stream",size_bytes:s.size,confirm:!0,client_upload:!0});if(m)throw m;return n?.onProgress&&n.onProgress(100),y}function et(t){return t&&"object"==typeof t?{...t,upload_url:t.upload_url||t.uploadUrl||t.url,storage_path:t.storage_path||t.storagePath}:null}function it(e){const i={get:st(e,"GET"),post:st(e,"POST"),put:st(e,"PUT"),patch:st(e,"PATCH"),delete:st(e,"DELETE"),upload:async(e,s,n)=>{try{return{data:await tt(i,e,s,n),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Upload failed",e?.status??500)}}},download:async(e,s,n)=>{try{return await Z(i,e,s,{...n,method:"POST"}),{data:void 0,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Download failed",e?.status??500)}}}};return i}function st(e,i){const s="POST"===i||"PUT"===i||"PATCH"===i;return async(n,r,o)=>{const a=e();let c,h={};s?(c=r,h=o||{}):(h=r||{},c=void 0);try{H(i,n,h);const t=h.token||a.token||(q?q():"")||"",e=h.apiKey||a.apiKey;return{data:await X(t,i,n,c,h.showToasts,h.params,e,a.baseUrl),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Error desconocido",e?.status??500)}}}}function nt(e){const i=()=>"function"==typeof e?e():e,s={get:rt(i,"GET"),post:rt(i,"POST"),put:rt(i,"PUT"),patch:rt(i,"PATCH"),delete:rt(i,"DELETE"),upload:async(e,i,n)=>{try{return{data:await tt(s,e,i,n),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Upload failed",e?.status??500)}}},download:async(e,i,n)=>{try{return await Z(s,e,i,{...n,method:"POST"}),{data:void 0,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Download failed",e?.status??500)}}}};return s}function rt(e,i){return async(s,n,r)=>{try{const t=e(),o=await async function(t,e,i,s){let n,r={};"POST"===t||"PUT"===t||"PATCH"===t?(n=i,r=s||{}):(r=i||{},n=void 0),H(t,e,r);let o=r.token;return!o&&q&&(o=q()||""),o=o||"",{token:o,apiKey:r.apiKey,body:n,params:r.params,showToasts:r.showToasts}}(i,s,n,r),a=o.token||t.token||"",c=o.apiKey||t.apiKey;return{data:await X(a,i,s,o.body,o.showToasts,o.params,c,t.baseUrl),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e?.message??"Error desconocido",e?.status??500)}}}}class ot{get baseUrl(){return this.Dt}getAuthToken(){return this.auth.token||this._t||null}constructor(t){const{baseUrl:e,apiKey:i}=t;this.Dt=e,this._t=i,this.realtime=new E(e,()=>this.getAuthToken());const s=t.auth?.storageKey||t.storageKey;t.global&&function(t){W={...W,...t}}(t.global),this.auth=new A({baseUrl:e,apiKey:i,storageKey:s,storage:t.auth?.storage,autoRefreshToken:t.auth?.autoRefreshToken,persistSession:t.auth?.persistSession,passwordRecoveryRedirect:t.redirects?.passwordRecovery},null);const n=it(()=>({token:this.auth.token,apiKey:i,baseUrl:e}));if(this.auth.api=n,this.db=new T(n),t.serviceRoleKey)if("undefined"!=typeof window&&void 0!==window.document)console.error("[DYPAI SDK] ❌ serviceRoleKey detected in a browser environment. This is a security risk — the key grants admin-level database access. db.direct has been disabled. Use serviceRoleKey only from server-side code (Node.js, Bun, Deno, scripts).");else{const i=it(()=>({token:t.serviceRoleKey,baseUrl:e}));this.db.direct=new O(i)}this.users=new U(n),this.roles=new x(n),this.api=nt(()=>({token:this.auth.token||"",apiKey:i,baseUrl:e})),M(()=>this.auth.token);const r="undefined"!=typeof window?window.location.search:"";if("undefined"!=typeof window&&t.redirects){const e=t.redirects;this.auth.P?.then(()=>{if(this.auth.isPasswordRecoveryCallback&&e.passwordRecovery){if(!window.location.pathname.includes(e.passwordRecovery)){const t=e.passwordRecovery+(r||"");window.location.replace(t)}}else if(this.auth.lastError&&e.authError)window.location.pathname.includes(e.authError)||window.location.replace(e.authError);else if(this.auth.token&&e.signIn){const t=this.auth.consumeCallbackType();t&&"recovery"!==t&&"invite"!==t&&(window.location.pathname.includes(e.signIn)||window.location.replace(e.signIn))}})}G({onUnauthorized:()=>this.auth.handleSessionExpired()})}from(t){return this.db.from(t)}async me(){return this.auth.getUser()}}function at(t,e,i){if(!t)throw new Error("createClient() requiere la URL base");return new ot({baseUrl:t,apiKey:"string"==typeof e?e:void 0,..."string"==typeof e?i:e})}let ct={};function ht(t){ct={...ct,toast:L,...t},ut()}function lt(){return{...ct}}function ut(){try{const{configureApiService:t}=require("../services/ApiService");t(ct)}catch(t){}}function dt(){ct={},ut()}async function ft(){ut()}const pt={name:"@dypai-ai/client-sdk",version:"1.10.5",description:"Official JavaScript/TypeScript SDK for DYPAI",features:["Authentication (email, OAuth, OTP)","Database CRUD","Direct database access (service role)","Custom endpoints (typed API)","File upload/download (Smart Upload)","User management (admin)","React hooks (useAuth, useEndpoint, useAction, useUpload)"]};export{u as AuthEmailNotVerifiedError,r as AuthInvalidCredentialsError,a as AuthInvalidTokenError,s as AuthSessionMissingError,f as AuthUserAlreadyExistsError,h as AuthWeakPasswordError,O as DirectDBModule,R as DirectQueryBuilder,ot as DypaiClient,t as DypaiError,E as DypaiRealtimeClient,pt as PACKAGE_INFO,P as RealtimeChannel,w as RetryableFetchError,X as callApi,v as classifyAuthError,G as configureApiService,ht as configureDypaiServices,nt as createApiClient,at as createClient,lt as getGlobalConfig,d as isAuthEmailNotVerifiedError,o as isAuthInvalidCredentialsError,c as isAuthInvalidTokenError,n as isAuthSessionMissingError,p as isAuthUserAlreadyExistsError,l as isAuthWeakPasswordError,i as isDypaiError,y as isRetryableFetchError,m as isRetryableStatus,ft as reloadDypaiConfig,dt as resetGlobalConfig,K as setToastFunction,M as setTokenProvider,L as toast,B as toastError,J as toastInfo,j as toastSuccess,z as toastWarning,Y as useToast};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";class DypaiError extends Error{constructor(t,e=500,r,i){super(t),this.status=e,this.code=r,this.details=i,this.name="DypaiError"}}function normalizeUser(t){if(!t)return{};const e=t.user||t,r=e.name||e.user_metadata?.full_name||null,i=e.emailVerified??e.confirmed_at??null,s=e.app_metadata||{},o=e.user_metadata||{full_name:r};return{id:e.id,email:e.email,phone:e.phone||null,role:s.role||o.role||e.role||"authenticated",created_at:e.createdAt||e.created_at,updated_at:e.updatedAt||e.updated_at,confirmed_at:i?"boolean"==typeof i?(new Date).toISOString():i:null,last_sign_in_at:e.last_sign_in_at||null,app_metadata:s,user_metadata:{...o,full_name:r},roleDetails:{name:s.role||o.role||null,weight:0},appContext:{app_id:"default"}}}class AuthSessionMissingError extends DypaiError{constructor(t="Auth session missing"){super(t,400,"session_missing"),this.name="AuthSessionMissingError"}}class AuthInvalidCredentialsError extends DypaiError{constructor(t="Invalid login credentials"){super(t,400,"invalid_credentials"),this.name="AuthInvalidCredentialsError"}}class AuthInvalidTokenError extends DypaiError{constructor(t="Invalid or expired token"){super(t,401,"invalid_token"),this.name="AuthInvalidTokenError"}}function isAuthInvalidTokenError(t){return t instanceof AuthInvalidTokenError}class AuthWeakPasswordError extends DypaiError{constructor(t="Password is too weak",e=[]){super(t,400,"weak_password"),this.name="AuthWeakPasswordError",this.reasons=e}}class AuthEmailNotVerifiedError extends DypaiError{constructor(t="Email not verified"){super(t,403,"email_not_verified"),this.name="AuthEmailNotVerifiedError"}}class AuthUserAlreadyExistsError extends DypaiError{constructor(t="User already exists"){super(t,409,"user_already_exists"),this.name="AuthUserAlreadyExistsError"}}class RetryableFetchError extends DypaiError{constructor(t,e=0){super(t,e,"retryable_fetch"),this.name="RetryableFetchError"}}function isRetryableStatus(t){return 0===t||408===t||429===t||t>=500&&t<600}function classifyAuthError(t,e,r){const i=(t||"").toLowerCase(),s=(r||"").toLowerCase();return s.includes("invalid_token")||s.includes("invalid_grant")||i.includes("invalid token")||i.includes("expired")||i.includes("revoked")?new AuthInvalidTokenError(t):s.includes("session_missing")||i.includes("no session")||i.includes("session missing")?new AuthSessionMissingError(t):s.includes("invalid_credentials")||s.includes("invalid_login")||i.includes("invalid credentials")||i.includes("invalid login")?new AuthInvalidCredentialsError(t):s.includes("email_not_verified")||i.includes("email not verified")?new AuthEmailNotVerifiedError(t):s.includes("user_already_exists")||s.includes("user_exists")||i.includes("already exists")||i.includes("already registered")?new AuthUserAlreadyExistsError(t):s.includes("weak_password")||i.includes("password is too weak")||i.includes("password too short")?new AuthWeakPasswordError(t):isRetryableStatus(e)?new RetryableFetchError(t,e):new DypaiError(t,e,r)}let _debugEnabled=!1;function _log(t,...e){"error"===t?console.error(...e):_debugEnabled&&("warn"===t?console.warn(...e):console.log(...e))}async function parseAuthError(t,e){let r={};try{r=await t.json()}catch{}const i=r.msg||r.error_description||r.message||e,s=r.error_code||r.error||r.code||void 0;return classifyAuthError(i,t.status,s)}class Deferred{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const localStorageAdapter={getItem:t=>"undefined"==typeof window?null:window.localStorage.getItem(t),setItem:(t,e)=>{if("undefined"!=typeof window)try{window.localStorage.setItem(t,e)}catch(t){console.error("[DYPAI SDK] ❌ Error crítico guardando en localStorage (¿Quota/Permisos?):",t)}},removeItem:t=>{if("undefined"!=typeof window)try{window.localStorage.removeItem(t)}catch(t){}}};async function wrapAuthResponse(t){try{return{data:await t,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t.message||"Error de autenticación",t.status||400)}}}class AuthModule{constructor(t,e=null){this.config=t,this.t=null,this.i=null,this.o=null,this.h=null,this.l=null,this.u=[],this.p=null,this.A=null,this.m=!1,this.v=null,this.S=null,this.D=0,this._=0,this.k=null,this.storage=t.storage||localStorageAdapter,_debugEnabled=!!t.debug;const r=t.storageKey||this.deriveStorageKey(t.apiKey);if(_log("log",`[DYPAI SDK] 🛠️ Inicializando AuthModule (storageKey: ${r})`),this.STORAGE_KEY=`dypai-${r}-auth-session`,this.T=this.P().then(()=>{this.i&&(this.getUser().catch(()=>{}),this.startAutoRefresh());const t=this.A;this.A=null,t?setTimeout(()=>{this.m=!0,"PASSWORD_RECOVERY"!==t&&"AUTH_ERROR"!==t&&this.R(t)},0):this.m=!0}),"undefined"!=typeof window){if(window.addEventListener("visibilitychange",this.I.bind(this)),window.addEventListener("focus",this.I.bind(this)),"undefined"!=typeof BroadcastChannel)try{this.k=new BroadcastChannel(`bc:${this.STORAGE_KEY}`),this.k.addEventListener("message",t=>{const e=t.data;e&&"object"==typeof e&&(_log("log",`[DYPAI SDK] 📡 BroadcastChannel: ${e.type}`),"SIGNED_OUT"===e.type?(this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh(),this.R("SIGNED_OUT")):"SESSION_UPDATED"===e.type&&this.P(!1))})}catch(t){_log("warn","[DYPAI SDK] BroadcastChannel no disponible, usando storage event:",t)}window.addEventListener("storage",t=>{t.key===this.STORAGE_KEY&&(_log("log","[DYPAI SDK] 🔄 Sesión actualizada (storage event). Sincronizando..."),this.P(!1))})}}C(t){return 0===t||408===t||429===t||t>=500&&t<600}O(t){if(this.k)try{this.k.postMessage({type:t,timestamp:Date.now()})}catch{}}async I(){"undefined"!=typeof document&&"visible"===document.visibilityState&&(_log("log","[DYPAI SDK] 👁️ Ventana visible. Sincronizando estado y reiniciando auto-refresh..."),await this.P(!0),this.i&&this.startAutoRefresh())}deriveStorageKey(t){return t?t.substring(0,8):"default"}getBaseUrl(){return this.config.baseUrl||"http://localhost:8000"}$(){if("undefined"==typeof window)return;const t=new URLSearchParams(window.location.search);["token","type","error","error_code","error_description","access_token","refresh_token","expires_in"].forEach(e=>t.delete(e));const e=t.toString(),r=`${window.location.pathname}${e?`?${e}`:""}`;window.history.replaceState({},document.title,r)}U(t){try{if(!t)return;sessionStorage.setItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY,t),"recovery"===t||"invite"===t?sessionStorage.setItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY,"PASSWORD_RECOVERY"):sessionStorage.removeItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}M(t){try{sessionStorage.setItem(AuthModule.RESET_TOKEN_STORAGE_KEY,t)}catch{}}N(){try{return sessionStorage.getItem(AuthModule.RESET_TOKEN_STORAGE_KEY)}catch{return null}}K(){try{sessionStorage.removeItem(AuthModule.RESET_TOKEN_STORAGE_KEY)}catch{}}Y(){try{sessionStorage.removeItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}L(t){if("undefined"==typeof window)return t;try{return new URL(t,window.location.origin).toString()}catch{return t}}j(t){isAuthInvalidTokenError(t)&&(this.K(),this.Y(),this.p={code:t.code||"invalid_token",message:t.message||"The reset link is invalid or has expired"},this.m&&this.R("AUTH_ERROR"))}get user(){return this.t}get token(){return this.i}get lastError(){return this.p}get hasPasswordRecoveryToken(){return!!this.N()}consumeCallbackType(){try{const t=sessionStorage.getItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY);return sessionStorage.removeItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY),t}catch{return null}}get isPasswordRecoveryCallback(){try{return"PASSWORD_RECOVERY"===sessionStorage.getItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY)||!!sessionStorage.getItem(AuthModule.RESET_TOKEN_STORAGE_KEY)}catch{return!1}}isLoggedIn(){return!(!this.i||!this.t)}onAuthStateChange(t){return _log("log","[DYPAI SDK] 👂 Nuevo suscriptor añadido a onAuthStateChange"),this.u.push(t),this.T.then(()=>{const e=this.B();_log("log",`[DYPAI SDK] 📣 INITIAL_SESSION para suscriptor (Sesión activa: ${!!e})`),t("INITIAL_SESSION",e),this.isPasswordRecoveryCallback?t("PASSWORD_RECOVERY",e):this.p&&t("AUTH_ERROR",null)}).catch(e=>{_log("error","[DYPAI SDK] ❌ Error esperando recuperación de sesión para suscriptor:",e),t("INITIAL_SESSION",null)}),{data:{subscription:{unsubscribe:()=>{this.u=this.u.filter(e=>e!==t)}}}}}async signInWithPassword(t){return wrapAuthResponse((async()=>{const e=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-in/email`,r={email:t.email||t.identifier||"",password:t.password},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(r)});if(!i.ok)throw await parseAuthError(i,"Login failed");const s=await i.json(),o=this.q(s);return await this.W(o),o})())}async login(t){return this.signInWithPassword(t)}async signUp(t,e){return wrapAuthResponse((async()=>{const r=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-up/email`,{email:i,phone:s,password:o,user_data:n,...a}=t,h=i||"",l=a.full_name||a.name||h.split("@")[0],c={...a,...n||{}},u={email:h,password:o,name:l};Object.keys(c).length>0&&Object.assign(u,c),e?.redirectTo?u.callbackURL=e.redirectTo:"undefined"!=typeof window&&(u.callbackURL=window.location.href.split("#")[0]);const d=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(u)});if(!d.ok)throw await parseAuthError(d,"Registration failed");const p=await d.json(),f=this.q(p),y={...f,confirmationRequired:!f.token};return y.token?await this.W(y):_log("log","[DYPAI SDK] 📧 Signup exitoso, se requiere confirmación de email."),y})())}async register(t){return this.signUp(t)}async getSession(){try{if(await this.T,!this.i||!this.t)return{data:null,error:null};const t=Math.floor(Date.now()/1e3);return(this.l||0)-t<30+AuthModule.CLOCK_SKEW_TOLERANCE_S&&(this.h||this.o)&&(_log("log","[DYPAI SDK] ⏳ getSession: Token próximo a expirar. Refrescando..."),await this.refreshSession().catch(t=>_log("warn","[DYPAI SDK] Error refreshing session in getSession:",t))),{data:{access_token:this.i,refresh_token:this.h||void 0,token_type:"bearer",user:this.t},error:null}}catch(t){return{data:null,error:new DypaiError(t.message,500)}}}async getUser(){return wrapAuthResponse((async()=>{await this.T;const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,r=await fetch(e,{headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw new DypaiError("Session invalid",r.status);const i=await r.json(),s=i?.jwt||r.headers.get("set-auth-jwt");s&&(this.i=s);const o=this.F(i);return this.J(o),o})())}async me(){return this.getUser()}async signInWithOAuth(t,e={}){return wrapAuthResponse((async()=>{if("undefined"==typeof window)throw new DypaiError("signInWithOAuth requiere un entorno de navegador (window no está disponible)",400);const{redirectTo:r=window.location.href,scopes:i}=e,s=this.config.baseUrl||"http://localhost:8000",o=new URLSearchParams({provider:t,callbackURL:r});i?.length&&o.set("scopes",i.join(" "));const n=`${s}/api/v0/auth/sign-in/social?${o.toString()}`;window.location.href=n})())}async signOut(){return wrapAuthResponse((async()=>{try{const t=this.o||this.i;if(t){const e=this.config.baseUrl||"http://localhost:8000";await fetch(`${e}/api/v0/auth/sign-out`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}})}}finally{this.G("signOut called")}})())}async logout(){return this.signOut()}async resetPasswordForEmail(t,e){return wrapAuthResponse((async()=>{const r=this.config.baseUrl||"http://localhost:8000",i={email:t};e?.redirectTo?i.redirectTo=e.redirectTo:this.config.passwordRecoveryRedirect&&"undefined"!=typeof window?i.redirectTo=this.L(this.config.passwordRecoveryRedirect):"undefined"!=typeof window&&(i.redirectTo=window.location.href.split("#")[0]);const s=await fetch(`${r}/api/v0/auth/request-password-reset`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(i)});if(!s.ok)throw await parseAuthError(s,"Recovery failed");return await s.json()})())}async recoverPassword(t){return this.resetPasswordForEmail(t.email)}async resendConfirmationEmail(t,e){return wrapAuthResponse((async()=>{const r=this.config.baseUrl||"http://localhost:8000",i={email:t};e?.redirectTo?i.callbackURL=e.redirectTo:"undefined"!=typeof window&&(i.callbackURL=window.location.href.split("#")[0]);const s=await fetch(`${r}/api/v0/auth/send-verification-email`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(i)});if(!s.ok)throw await parseAuthError(s,"Failed to resend confirmation email");return{message:"Confirmation email resent"}})())}async refreshSession(){if(this.v)return _log("log","[DYPAI SDK] 🔄 Refresco de sesión ya en curso. Esperando resolución..."),this.v.promise;const t=new Deferred;this.v=t;const e=`lock:${this.STORAGE_KEY}`;return(async()=>{try{const r=await async function(t,e,r){if("undefined"!=typeof globalThis&&globalThis.navigator?.locks?.request){const i=new AbortController,s=setTimeout(()=>i.abort(),e);try{return await globalThis.navigator.locks.request(t,{signal:i.signal},async()=>(clearTimeout(s),await r()))}catch(i){if("AbortError"===i.name)return console.warn(`[DYPAI SDK] ⚠️ Web Lock "${t}" timeout (${e}ms). Proceeding without lock.`),await r();throw i}}return await r()}(e,AuthModule.LOCK_ACQUIRE_TIMEOUT_MS,()=>this.V());t.resolve(r)}catch(e){const r=e instanceof DypaiError?e:new DypaiError(e.message||"Error refrescando sesión",401);t.resolve({data:null,error:r})}finally{this.v=null}})(),t.promise}async V(){try{const t=await this.storage.getItem(this.STORAGE_KEY);if(t){const e=JSON.parse(t),r=e.expires_at||0,i=Math.floor(Date.now()/1e3);if(e.access_token!==this.i&&r-i>60)return _log("log","[DYPAI SDK] 🔄 Otra pestaña ya refrescó el token. Adoptando sesión del storage."),this.i=e.access_token,this.o=e.session_token||e.access_token,this.h=e.refresh_token,this.l=e.expires_at,this.t=e.user,this.D=0,this.R("TOKEN_REFRESHED"),{data:{token:this.i,sessionToken:this.o||void 0,refreshToken:this.h||void 0,expiresAt:this.l||void 0,user:this.t},error:null}}const e=this.o||this.i;if(!e)throw new AuthSessionMissingError("No hay sesión activa");_log("log","[DYPAI SDK] 🔄 Verificando sesión activa con session_token...");const r=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,i=new AbortController,s=setTimeout(()=>i.abort(),AuthModule.REFRESH_TIMEOUT_MS);let o;try{o=await fetch(r,{method:"GET",headers:{Authorization:`Bearer ${e}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},signal:i.signal})}finally{clearTimeout(s)}if(!o.ok){let t={};try{t=await o.json()}catch{}const e=o.status,r=t.error||t.code||"",i=(t.error_description||t.msg||"").toLowerCase(),s=this.C(e),n=e>=400&&e<500&&!s,a="invalid_grant"===r||i.includes("invalid refresh token")||i.includes("revoked")||i.includes("expired")||i.includes("invalid token");throw n&&a?(_log("error",`[DYPAI SDK] ❌ Token inválido/revocado (${e}). Limpiando sesión.`),this.G(`refreshSession failed (${e}: ${r})`)):n?(_log("error",`[DYPAI SDK] ❌ Error 4xx en refresh (${e}). Limpiando sesión.`),this.G(`refreshSession failed (${e})`)):_log("warn",`[DYPAI SDK] ⚠️ Error transitorio en refresh (${e}). Manteniendo sesión.`),new DypaiError(t.msg||t.error_description||"Refresh session failed",e)}const n=await o.json(),a=o.headers.get("set-auth-jwt"),h=n?.jwt,l=h||a,c=this.q(n,this.i);return l&&(c.token=l),c.sessionToken=e,this.D=0,await this.W(c),_log("log",`[DYPAI SDK] ✅ Session refresh exitoso (fresh JWT: ${!!l}).`),{data:c,error:null}}catch(t){this.D++,this._=Date.now();const e=t instanceof DOMException&&"AbortError"===t.name,r=e?new DypaiError("Refresh token timeout (servidor no responde)",408):t instanceof DypaiError?t:new DypaiError(t.message||"Error refrescando sesión",401),i=Math.min(AuthModule.RETRY_BASE_MS*Math.pow(2,this.D),AuthModule.MAX_RETRY_MS);return e?_log("error",`[DYPAI SDK] ⏱️ Timeout en refresh después de ${AuthModule.REFRESH_TIMEOUT_MS/1e3}s. Backoff: ${i}ms (intento ${this.D})`):_log("error","[DYPAI SDK] ❌ Refresh falló:",r.message,`(status: ${r.status}). Backoff: ${i}ms (intento ${this.D})`),this.D>=AuthModule.MAX_REFRESH_FAILURES&&(_log("error",`[DYPAI SDK] 🗑️ ${this.D} fallos consecutivos de refresh. Sesión irrecuperable. Limpiando...`),this.G(`${this.D} consecutive refresh failures`),this.D=0),{data:null,error:r}}}async signInWithOtp(t){return wrapAuthResponse((async()=>{if(t.phone)throw new DypaiError("Phone OTP is not supported by the current DYPAI auth backend",400);if(!t.email)throw new DypaiError("Email is required for magic link sign-in",400);const e=await this.signInWithMagicLink(t.email);if(e.error)throw e.error;return{status:!0}})())}async verifyOtp(t){return wrapAuthResponse((async()=>{if(!t.token)throw new DypaiError("Verification token is required",400);if("magiclink"===t.type)return await this.H(t.token);if("signup"===t.type||"email_change"===t.type){const e=await this.X(t.token);if(e)return e;throw new DypaiError("Verification completed but no session was returned",400)}if("recovery"===t.type||"invite"===t.type)throw new DypaiError("Recovery links do not use verifyOtp(). Use setPassword() after the PASSWORD_RECOVERY callback.",400);throw new DypaiError("Unsupported verification type",400)})())}async updateUser(t){return wrapAuthResponse((async()=>{const e=this.o||this.i;if(!e)throw new AuthSessionMissingError("No hay sesión activa");const r=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/update-user`,i=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(t)});if(!i.ok){const t=await i.json();throw new DypaiError(t.detail||"Update user failed",i.status)}const s=await i.json(),o=this.F(s);return this.J(o),o})())}async setPassword(t){return wrapAuthResponse((async()=>{if("undefined"==typeof window)throw new DypaiError("setPassword requiere un entorno de navegador",400);const e=new URLSearchParams(window.location.search).get("token")||this.N();if(!e){if(this.o||this.i){const e=await this.updateUser({password:t});if(e.error)throw e.error;return this.Y(),this.$(),{status:!0}}throw new DypaiError("No reset token found in the current URL",400)}const r=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${r}/api/v0/auth/reset-password`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({newPassword:t,token:e})});if(!i.ok){const t=await parseAuthError(i,"Reset password failed");throw this.j(t),t}this.K(),this.Y(),this.$();const s=await i.json();if(s?.jwt&&s?.session_token&&s?.user){const t={token:s.jwt,sessionToken:s.session_token,user:this.F(s),expiresAt:s.session?.expiresAt?Math.floor(new Date(s.session.expiresAt).getTime()/1e3):void 0};await this.W(t)}return{status:s?.status??!0}})())}async signInWithMagicLink(t,e){return wrapAuthResponse((async()=>{const r=this.config.baseUrl||"http://localhost:8000",i=e?.redirectTo||("undefined"!=typeof window?window.location.href.split("#")[0]:void 0),s=await fetch(`${r}/api/v0/auth/sign-in/magic-link`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({email:t,callbackURL:i})});if(!s.ok)throw await parseAuthError(s,"Magic link failed");return{message:"Magic link sent"}})())}async enableTwoFactor(){return wrapAuthResponse((async()=>{const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/two-factor/enable`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw await parseAuthError(r,"Enable 2FA failed");return await r.json()})())}async verifyTwoFactor(t){return wrapAuthResponse((async()=>{const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/two-factor/verify-totp`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.o||this.i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({code:t})});if(!r.ok)throw await parseAuthError(r,"2FA verification failed");const i=await r.json(),s={token:i.jwt||i.token||this.i,expiresIn:604800,expiresAt:Math.floor(Date.now()/1e3)+604800,user:this.F(i)};return await this.W(s),s})())}async disableTwoFactor(){return wrapAuthResponse((async()=>{const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/two-factor/disable`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw await parseAuthError(r,"Disable 2FA failed");return{message:"2FA disabled"}})())}async createOrganization(t,e){return wrapAuthResponse((async()=>{const r=this.o||this.i;if(!r)throw new AuthSessionMissingError("No hay sesión activa");const i=this.config.baseUrl||"http://localhost:8000",s=await fetch(`${i}/api/v0/auth/organization/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({name:t,slug:e})});if(!s.ok)throw await parseAuthError(s,"Create organization failed");return await s.json()})())}async inviteMember(t,e,r){return wrapAuthResponse((async()=>{const i=this.o||this.i;if(!i)throw new AuthSessionMissingError("No hay sesión activa");const s=this.config.baseUrl||"http://localhost:8000",o=await fetch(`${s}/api/v0/auth/organization/invite-member`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({organizationId:t,email:e,role:r||"member"})});if(!o.ok)throw await parseAuthError(o,"Invite member failed");return await o.json()})())}async listOrganizations(){return wrapAuthResponse((async()=>{const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/organization/list`,{headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw await parseAuthError(r,"List organizations failed");return await r.json()})())}startAutoRefresh(){this.stopAutoRefresh(),_log("log","[DYPAI SDK] ⏱️ Auto-refresh iniciado (tick cada 30s)."),this.Z(),this.S=setInterval(()=>this.Z(),AuthModule.AUTO_REFRESH_TICK_DURATION_MS)}stopAutoRefresh(){this.S&&(_log("log","[DYPAI SDK] ⏹️ Auto-refresh detenido."),clearInterval(this.S),this.S=null)}async Z(){try{const t=Math.floor(Date.now()/1e3),e=this.l||0;if(!this.h&&!this.o||!e)return;if(1e3*(e-t)>AuthModule.AUTO_REFRESH_TICK_THRESHOLD*AuthModule.AUTO_REFRESH_TICK_DURATION_MS+1e3*AuthModule.CLOCK_SKEW_TOLERANCE_S)return;if(this.D>0){const t=Math.min(AuthModule.RETRY_BASE_MS*Math.pow(2,this.D),AuthModule.MAX_RETRY_MS);if(Date.now()-this._<t)return}_log("log",`[DYPAI SDK] ⏱️ Auto-refresh tick: token expira en ${e-t}s. Refrescando...`),await this.refreshSession()}catch(t){_log("error","[DYPAI SDK] ❌ Error en auto-refresh tick:",t)}}F(t){return t?normalizeUser(t):(_log("warn","[DYPAI SDK] ⚠️ Intentando normalizar un usuario inexistente (data es null/undefined)"),{})}q(t,e){const r="number"==typeof t?.expiresIn?t.expiresIn:"number"==typeof t?.expires_in?t.expires_in:604800,i=t?.jwt||void 0,s=t?.session_token||t?.token||t?.access_token||t?.session?.token||t?.session?.accessToken||t?.session?.access_token||e;return{token:i||s,sessionToken:s,refreshToken:t?.refreshToken||t?.refresh_token||t?.session?.refreshToken||t?.session?.refresh_token||void 0,expiresIn:r,expiresAt:Math.floor(Date.now()/1e3)+r,user:this.F(t)}}async W(t){this.p=null,t.token&&(this.i=t.token,this.o=t.sessionToken||t.token,void 0!==t.refreshToken&&(this.h=t.refreshToken||null),void 0!==t.expiresAt&&(this.l=t.expiresAt||null),await this.J(t.user,t.token,t.refreshToken,t.expiresAt),_log("log",`[DYPAI SDK] 💾 Sesión persistida en storage (expires_at: ${this.l}, has_refresh: ${!!this.h}, has_session_token: ${!!this.o})`),this.startAutoRefresh())}async J(t,e,r,i){this.t=t,e&&(this.i=e),void 0!==r&&(this.h=r||null),void 0!==i&&(this.l=i||null);try{if(this.i&&this.t){const t={access_token:this.i,session_token:this.o,refresh_token:this.h,expires_at:this.l,user:this.t,version:2};await this.storage.setItem(this.STORAGE_KEY,JSON.stringify(t)),this.O("SESSION_UPDATED")}else _log("warn","[DYPAI SDK] ⚠️ _updateUser: No se guardó sesión porque falta token o user.")}catch(t){_log("error","[DYPAI SDK] ❌ Error guardando sesión en storage:",t)}if(this.m){const t=e?"SIGNED_IN":"USER_UPDATED";this.R(t)}}async G(t="unknown"){_log("log",`[DYPAI SDK] 🧹 Limpiando sesión del estado y storage. Motivo: ${t}`),this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh();try{await this.storage.removeItem(this.STORAGE_KEY),_log("log","[DYPAI SDK] ✅ Storage limpiado con éxito.")}catch(t){_log("error","[DYPAI SDK] ❌ Error eliminando sesión de storage:",t)}this.O("SIGNED_OUT"),this.R("SIGNED_OUT")}async H(t){const e=this.getBaseUrl(),r=new URL(`${e}/api/v0/auth/magic-link/verify`);r.searchParams.set("token",t);const i=await fetch(r.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await parseAuthError(i,"Magic link verification failed");const s=await i.json(),o=this.q(s);return await this.W(o),o}async X(t){const e=this.getBaseUrl(),r=new URL(`${e}/api/v0/auth/verify-email`);r.searchParams.set("token",t);const i=await fetch(r.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await parseAuthError(i,"Email verification failed");if(!(i.headers.get("content-type")||i.headers.get("Content-Type")||"").includes("application/json"))return null;const s=await i.json(),o=this.q(s);return o.token&&o.user?.id?(await this.W(o),o):null}async tt(t,e){const r=e||("undefined"!=typeof window&&window.location.pathname.includes("reset-password")?"recovery":null);if(!r)return!1;if(_log("log",`[DYPAI SDK] 🔗 Auth callback detectado en query (type: ${r}). Procesando...`),this.U(r),"recovery"===r||"invite"===r)return this.M(t),this.$(),this.A="PASSWORD_RECOVERY",!0;if(this.$(),"magiclink"===r)return await this.H(t),this.A="SIGNED_IN",!0;if("signup"===r||"email_change"===r){const e=await this.X(t);return this.A=e?.token?"SIGNED_IN":null,!0}return!1}async P(t=!0){_log("log","[DYPAI SDK] 🔍 Iniciando recuperación de sesión...");try{if(await this.et())return void _log("log","[DYPAI SDK] ✅ Sesión establecida desde callback URL. Saltando recuperación de localStorage.");const e=await this.storage.getItem(this.STORAGE_KEY);if(!e)return void _log("log","[DYPAI SDK] ℹ️ No se encontró ninguna sesión (storage vacío).");let r;try{r=JSON.parse(e)}catch{return _log("warn","[DYPAI SDK] ⚠️ Sesión corrupta (JSON inválido). Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY)}if(!r||"object"!=typeof r||"string"!=typeof r.access_token)return _log("warn","[DYPAI SDK] ⚠️ Sesión con formato inválido. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if((r.version||1)<2&&!r.session_token)return _log("warn","[DYPAI SDK] 🔄 Sesión en formato antiguo (v1) sin session_token. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if(this.i=r.access_token,this.o="string"==typeof r.session_token?r.session_token:r.access_token,this.h="string"==typeof r.refresh_token?r.refresh_token:null,this.l="number"==typeof r.expires_at?r.expires_at:null,this.t=r.user&&"object"==typeof r.user?r.user:null,t){_log("log","[DYPAI SDK] 🔄 Validando sesión con el servidor...");const t=await this.refreshSession();if(t.error)return void _log("warn","[DYPAI SDK] ⚠️ Sesión inválida. Limpiando:",t.error.message)}this.A="SIGNED_IN"}catch(t){_log("error","[DYPAI SDK] ❌ Error crítico durante la recuperación de sesión:",t)}}async et(){if("undefined"==typeof window)return!1;try{const t=new URLSearchParams(window.location.search),e=t.get("error"),r=t.get("token"),i=t.get("type");if(e){const r=t.get("error_code")||e,i=t.get("error_description")?.replace(/\+/g," ")||"Authentication error";return _log("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${r} — ${i}`),this.p={code:r,message:i},this.$(),this.A="AUTH_ERROR",!0}if(r)return await this.tt(r,i);const s=window.location.hash.substring(1);if(!s)return!1;const o=new URLSearchParams(s);if(o.get("error")){const t=o.get("error_code")||"unknown",e=o.get("error_description")?.replace(/\+/g," ")||"Authentication error";return _log("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${t} — ${e}`),this.p={code:t,message:e},this.$(),this.A="AUTH_ERROR",!0}const n=o.get("access_token");if(!n)return!1;const a=o.get("refresh_token"),h=o.get("type"),l=parseInt(o.get("expires_in")||"3600",10);_log("log",`[DYPAI SDK] 🔗 Auth callback detectado en URL hash (type: ${h||"unknown"}). Procesando...`),this.U(h),this.$(),this.o=n,this.i=n,this.h=a||null,this.l=Math.floor(Date.now()/1e3)+l;const c=await fetch(`${this.getBaseUrl()}/api/v0/auth/get-session`,{headers:{Authorization:`Bearer ${n}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!c.ok)return _log("error",`[DYPAI SDK] ❌ Callback URL: token inválido o expirado (status: ${c.status}). Limpiando...`),this.i=null,this.o=null,this.h=null,this.l=null,this.p={code:"token_invalid",message:"The authentication link is invalid or has expired"},this.R("AUTH_ERROR"),!0;const u=await c.json(),d=u?.jwt||c.headers.get("set-auth-jwt"),p=this.F(u),f={token:d||n,sessionToken:n,refreshToken:a||void 0,expiresIn:l,expiresAt:this.l,user:p};return await this.W(f),this.A="recovery"===h||"invite"===h?"PASSWORD_RECOVERY":"SIGNED_IN",_log("log",`[DYPAI SDK] 🔑 Callback type=${h}. Pending event: ${this.A}`),!0}catch(t){return _log("error","[DYPAI SDK] ❌ Error procesando callback URL:",t),!1}}B(){return this.i&&this.t?{access_token:this.i,refresh_token:this.h||void 0,expires_at:this.l||void 0,token_type:"bearer",user:this.t}:null}R(t="USER_UPDATED"){const e=this.B();_log("log",`[DYPAI SDK] 📢 Notificando a ${this.u.length} suscriptores: Evento=${t} (Sesión activa: ${!!e})`),this.u.forEach(r=>r(t,e))}handleSessionExpired(){this.G("handleSessionExpired called (likely 401 from API)")}}AuthModule.REFRESH_TIMEOUT_MS=15e3,AuthModule.MAX_REFRESH_FAILURES=5,AuthModule.AUTO_REFRESH_TICK_DURATION_MS=3e4,AuthModule.AUTO_REFRESH_TICK_THRESHOLD=3,AuthModule.RETRY_BASE_MS=200,AuthModule.MAX_RETRY_MS=3e4,AuthModule.LOCK_ACQUIRE_TIMEOUT_MS=5e3,AuthModule.CLOCK_SKEW_TOLERANCE_S=10,AuthModule.CALLBACK_TYPE_STORAGE_KEY="dypai-auth-callback-type",AuthModule.CALLBACK_REDIRECT_STORAGE_KEY="dypai-auth-callback-redirect",AuthModule.RESET_TOKEN_STORAGE_KEY="dypai-auth-reset-token";class RealtimeChannel{constructor(t,e,r){this.ws=null,this.bindings=[],this.rt={},this.it=!1,this.topic=t,this.client=e,this.config=r||{}}on(t,e,r){return this.bindings.push({type:t,filter:e,callback:r}),this}subscribe(t){this.st=t;const e=this.client.ot();this.ws=e;const r=()=>{const r=this.bindings.filter(t=>"postgres_changes"===t.type);for(const t of r)e.send(JSON.stringify({type:"subscribe",table:t.filter.table||"*",event:t.filter.event||"*",filter:t.filter.filter?this.nt(t.filter.filter):void 0,schema:t.filter.schema||"public"}));const i=this.bindings.some(t=>"broadcast"===t.type),s=this.bindings.some(t=>"presence"===t.type);(i||s)&&e.send(JSON.stringify({type:"join",channel:this.topic,presence:!!s||void 0})),this.it=!0,t?.("SUBSCRIBED")};e.readyState===WebSocket.OPEN?r():e.readyState===WebSocket.CONNECTING&&e.addEventListener("open",r,{once:!0}),this.ht&&e.removeEventListener("message",this.ht);const i=t=>{try{const e=JSON.parse("string"==typeof t.data?t.data:"");this.ct(e)}catch{}};return this.ht=i,e.addEventListener("message",i),e.addEventListener("close",()=>{this.it=!1,t?.("CLOSED")},{once:!0}),e.addEventListener("error",()=>{t?.("CHANNEL_ERROR",new Error("WebSocket error"))},{once:!0}),this}ut(t){const e=this.bindings.filter(t=>"postgres_changes"===t.type);for(const r of e)t.send(JSON.stringify({type:"subscribe",table:r.filter.table||"*",event:r.filter.event||"*",filter:r.filter.filter?this.nt(r.filter.filter):void 0,schema:r.filter.schema||"public"}));const r=this.bindings.some(t=>"broadcast"===t.type),i=this.bindings.some(t=>"presence"===t.type);(r||i)&&t.send(JSON.stringify({type:"join",channel:this.topic,presence:!!i||void 0})),this.st?.("SUBSCRIBED")}async send(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"broadcast",channel:this.topic,event:t.event,payload:t.payload,self:this.config.broadcast?.self||!1})),"ok"}catch{return"error"}}async track(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"presence",channel:this.topic,data:t})),"ok"}catch{return"error"}}async untrack(){return"ok"}presenceState(){return{...this.rt}}async unsubscribe(){if(this.ht&&this.ws&&(this.ws.removeEventListener("message",this.ht),this.ht=void 0),this.ws?.readyState===WebSocket.OPEN){const t=this.bindings.filter(t=>"postgres_changes"===t.type);for(const e of t)this.ws.send(JSON.stringify({type:"unsubscribe",table:e.filter.table}));this.ws.send(JSON.stringify({type:"leave",channel:this.topic}))}return this.it=!1,this.bindings=[],this.rt={},"ok"}ct(t){if("change"===t.type)for(const e of this.bindings)"postgres_changes"===e.type&&(e.filter.table&&"*"!==e.filter.table&&e.filter.table!==t.table||e.filter.event&&"*"!==e.filter.event&&e.filter.event!==t.event||e.callback({schema:"public",table:t.table,eventType:t.event,new:t.record||{},old:t.old_record||{},commit_timestamp:t.timestamp,truncated:t.truncated||!1}));if("broadcast"===t.type&&t.channel===this.topic)for(const e of this.bindings)"broadcast"===e.type&&("*"!==e.filter.event&&e.filter.event!==t.event||e.callback({event:t.event,payload:t.payload,sender:t.sender,timestamp:t.timestamp}));if("presence_state"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";this.rt={};for(const[r,i]of Object.entries(t.presences||{})){const t=i[e]||r;this.rt[t]||(this.rt[t]=[]),this.rt[t].push(i)}for(const t of this.bindings)"presence"===t.type&&("sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"}))}if("presence_diff"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";if(t.joins)for(const[r,i]of Object.entries(t.joins)){const t=i[e]||r;this.rt[t]||(this.rt[t]=[]),this.rt[t].push(i);for(const e of this.bindings)"presence"===e.type&&("join"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"join",key:t,newPresences:[i],currentPresences:this.rt[t]}))}if(t.leaves)for(const[r,i]of Object.entries(t.leaves)){const t=i[e]||r;this.rt[t]=(this.rt[t]||[]).filter(t=>t.userId!==r),0===this.rt[t].length&&delete this.rt[t];for(const e of this.bindings)"presence"===e.type&&("leave"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"leave",key:t,leftPresences:[i],currentPresences:this.rt[t]||[]}))}for(const t of this.bindings)"presence"!==t.type||"sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"})}}nt(t){const e=t.match(/^(\w+)=eq\.(.+)$/);if(e)return{[e[1]]:e[2]}}}class DypaiRealtimeClient{constructor(t,e){this.ws=null,this.channels=new Map,this.heartbeatTimer=null,this.reconnectAttempt=0,this.reconnectTimer=null,this.baseUrl=t,this.tokenProvider=e}channel(t,e){const r=this.channels.get(t);if(r)return r;const i=new RealtimeChannel(t,this,e?.config);return this.channels.set(t,i),i}getChannels(){return Array.from(this.channels.values())}async removeChannel(t){await t.unsubscribe(),this.channels.delete(t.topic),0===this.channels.size&&this.disconnect()}async removeAllChannels(){for(const t of this.channels.values())await t.unsubscribe();this.channels.clear(),this.disconnect()}async setAuth(t){t&&(this.tokenProvider=()=>t),this.ws&&this.channels.size>0&&(this.disconnect(),this.ot())}disconnect(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.ws?.close(),this.ws=null}ot(){if(this.ws&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING))return this.ws;const t=this.tokenProvider(),e=this.baseUrl.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(t||"")}`,r=new WebSocket(e);return this.ws=r,r.addEventListener("open",()=>{this.reconnectAttempt=0;for(const t of this.channels.values())t.it&&t.ut(r);this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{r.readyState===WebSocket.OPEN&&r.send(JSON.stringify({type:"heartbeat"}))},25e3)}),r.addEventListener("close",()=>{if(this.ws=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.channels.size>0&&!this.reconnectTimer){const t=DypaiRealtimeClient.RECONNECT_INTERVALS[Math.min(this.reconnectAttempt,DypaiRealtimeClient.RECONNECT_INTERVALS.length-1)];this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.channels.size>0&&this.ot()},t)}}),r}}DypaiRealtimeClient.RECONNECT_INTERVALS=[1e3,2e3,5e3,1e4,3e4];class DataModule{get direct(){if(!this.dt)throw new Error("client.db.direct is not available. Pass serviceRoleKey to createClient() to enable direct database access.\n\nExample:\n const client = createClient(url, { serviceRoleKey: process.env.DYPAI_SERVICE_ROLE_KEY });\n\nWARNING: serviceRoleKey grants admin-level access. Only use from server-side code (scripts, migrations, seeds). Never expose it in browser/client-side code.");return this.dt}set direct(t){this.dt=t}constructor(t){this.api=t}from(t){return new QueryBuilder(t,this.api)}}class QueryBuilder{constructor(t,e){this.table=t,this.api=e}async select(t={}){return this.api.get(this.table,{params:t})}async insert(t){return this.api.post(this.table,t)}async update(t,e){return this.api.patch(`${this.table}/${t}`,e)}async delete(t){return this.api.delete(`${this.table}/${t}`)}}class DirectDBModule{constructor(t){this.api=t}from(t){return new DirectQueryBuilder(t,this.api)}async sql(t,e,r){return this.api.post("/api/v0/admin/db/sql",{query:t,params:e||[],...void 0!==r?.limit&&{limit:r.limit}})}}class DirectQueryBuilder{constructor(t,e){this.table=t,this.api=e,this.ft="public",this.yt=[],this.wt=100,this.At=0,this.vt="ASC"}schema(t){return this.ft=t,this}eq(t,e){return this.yt.push({column:t,operator:"eq",value:e}),this}neq(t,e){return this.yt.push({column:t,operator:"neq",value:e}),this}gt(t,e){return this.yt.push({column:t,operator:"gt",value:e}),this}gte(t,e){return this.yt.push({column:t,operator:"gte",value:e}),this}lt(t,e){return this.yt.push({column:t,operator:"lt",value:e}),this}lte(t,e){return this.yt.push({column:t,operator:"lte",value:e}),this}like(t,e){return this.yt.push({column:t,operator:"ilike",value:e}),this}ilike(t,e){return this.like(t,e)}isNull(t){return this.yt.push({column:t,operator:"is_null",value:null}),this}is(t,e){return this.isNull(t)}notNull(t){return this.yt.push({column:t,operator:"not_null",value:null}),this}in(t,e){return this.yt.push({column:t,operator:"in",value:e}),this}contains(t,e){return this.yt.push({column:t,operator:"array_contains",value:e}),this}containedBy(t,e){return this.yt.push({column:t,operator:"contained_by",value:e}),this}overlaps(t,e){return this.yt.push({column:t,operator:"overlaps",value:e}),this}textSearch(t,e){return this.yt.push({column:t,operator:"fts",value:e}),this}phraseSearch(t,e){return this.yt.push({column:t,operator:"phfts",value:e}),this}not(t,e,r){return this.yt.push({column:t,operator:"not",value:{operator:e,value:r}}),this}or(t){return this.yt.push({operator:"or",or:t}),this}limit(t){return this.wt=t,this}offset(t){return this.At=t,this}orderBy(t,e="ASC"){return this.St=t,this.vt=e,this}async select(t){const e=[...this.yt];let r=this.Et;if("string"==typeof t)r=t;else if(t&&"object"==typeof t)for(const[r,i]of Object.entries(t))e.push({column:r,operator:"eq",value:i});return this.execute("select",{...r&&{columns:r},filters:e,limit:this.wt,offset:this.At,sort_by:this.St,order:this.vt})}async single(){const t=await this.select();if(t.error)return t;const e=t.data;return!e||Array.isArray(e)&&0===e.length?{data:null,error:new DypaiError("No rows found.",404)}:{data:Array.isArray(e)?e[0]:e,error:null}}async maybeSingle(){const t=await this.select();if(t.error)return t;const e=t.data;return!e||Array.isArray(e)&&0===e.length?{data:null,error:null}:{data:Array.isArray(e)?e[0]:e,error:null}}async count(){const t=await this.execute("select",{columns:"count(*) as count",filters:[...this.yt],limit:1,offset:0});if(t.error)return t;const e=t.data;return{data:Array.isArray(e)&&e.length>0?Number(e[0].count):0,error:null}}async insert(t){if(Array.isArray(t)&&t.length>1e3){const e=[];for(let r=0;r<t.length;r+=1e3){const i=t.slice(r,r+1e3),{data:s,error:o}=await this.execute("insert",{data:i,mode:"bulk"});if(o)return{data:null,error:o};s&&e.push(...Array.isArray(s)?s:[s])}return{data:e,error:null}}const e=Array.isArray(t)?"bulk":"single";return this.execute("insert",{data:t,mode:e})}async update(t){return 0===this.yt.length?{data:null,error:new DypaiError("Update requires at least one filter. Use .eq(), .in(), etc.",400)}:this.execute("update",{data:t,filters:this.yt})}async delete(){return 0===this.yt.length?{data:null,error:new DypaiError("Delete requires at least one filter.",400)}:this.execute("delete",{filters:this.yt})}async upsert(t,e="id"){return this.execute("upsert",{data:t,conflict_column:e})}async execute(t,e){return this.api.post("/api/v0/admin/db/query",{operation:t,schema_name:this.ft,table_name:this.table,...e})}}class UsersModule{constructor(t){this.api=t}async list(t={}){const e=await this.api.get("admin/users",{params:t});return e.data?.users&&(e.data.users=e.data.users.map(normalizeUser)),e}async create(t){const e=await this.api.post("admin/users",t);return e.data&&(e.data=normalizeUser(e.data)),e}async update(t,e){const r=await this.api.put(`admin/users/${t}`,e);return r.data&&(r.data=normalizeUser(r.data)),r}async delete(t){return this.api.delete(`admin/users/${t}`)}}class RolesModule{constructor(t){this.api=t}async list(t={}){const e={};void 0!==t.include_global&&(e.include_global=t.include_global);const r=await this.api.get("admin/roles",{params:e});return Array.isArray(r.data)?r.data={roles:r.data,total:r.data.length}:r.data?.roles&&null==r.data.total&&(r.data.total=r.data.roles.length),r}async get(t){return this.api.get(`admin/roles/${t}`)}async create(t){return this.api.post("admin/roles",t)}async update(t,e){return this.api.put(`admin/roles/${t}`,e)}async delete(t){return this.api.delete(`admin/roles/${t}`)}}let customToastFunction=null;const fallbackToast=t=>{const{title:e,description:r,variant:i="default"}=t,s=`${"error"===i?"❌":"success"===i?"✅":"warning"===i?"⚠️":"info"===i?"ℹ️":"📢"} ${e}${r?`: ${r}`:""}`;"error"===i?console.error(s):"warning"===i&&console.warn(s)},toast=t=>(customToastFunction||fallbackToast)(t);let serviceConfig={},globalTokenProvider=null;function setTokenProvider(t){globalTokenProvider=t}function configureApiService(t){serviceConfig={...serviceConfig,...t}}const pendingRequests=new Map,API_OPTION_KEYS=new Set(["params","token","apiKey","showToasts"]);function validateApiOptionsShape(t,e,r){if(null==r)return;if("object"!=typeof r||Array.isArray(r))throw new DypaiError(`Invalid ${t} options for endpoint "${e}": options must be an object. Use { params: { ... } } for query parameters.`,400,"invalid_api_options");const i=Object.keys(r).filter(t=>!API_OPTION_KEYS.has(t));if(!i.length)return;const s=i.map(t=>`"${t}"`).join(", "),o=i[0],n=t.toLowerCase();throw new DypaiError(`Invalid ${t} options for endpoint "${e}": unknown option ${s}. ${"GET"===t||"DELETE"===t?`Use client.api.${n}("${e}", { params: { ${o}: value } }).`:`Pass request data as the second argument and options as the third argument, for example client.api.${n}("${e}", body, { params: { ${o}: value } }).`}`,400,"invalid_api_options")}function getCompleteConfig(){let t=serviceConfig;try{const{getGlobalConfig:e}=require("../config/global-config");t={...e(),...serviceConfig}}catch(t){}return t}async function callApi(t,e,r,i,s,o,n,a){const h=getCompleteConfig(),l=a||null;if(!l&&!r.startsWith("http"))throw new Error("Base URL no definida. Usa createClient(url[, apiKey]).");if("string"!=typeof r||!r.trim())throw new Error("Endpoint debe ser un string válido");let c;if(r.startsWith("http"))c=r;else{const t=l.replace(/\/+$/,"");c=r.startsWith("/")?t+r:t+"/api/v0/"+r}if(o&&Object.keys(o).length>0){const t=new URLSearchParams,e=(r,i)=>{null!=i&&(Array.isArray(i)?i.forEach((t,i)=>e(`${r}[${i}]`,t)):"object"==typeof i?Object.entries(i).forEach(([t,i])=>e(`${r}[${t}]`,i)):t.append(r,String(i)))};Object.entries(o).forEach(([t,r])=>e(t,r));const r=t.toString();r&&(c+=`?${r}`)}const u="GET"===e?`${e}:${c}:${JSON.stringify(i)}`:null;if(u&&pendingRequests.has(u))return pendingRequests.get(u);const d=i instanceof FormData,p={...h.headers||{},...d?{}:{"Content-Type":"application/json"},...t&&{Authorization:`Bearer ${t}`},...n&&{"x-api-key":n}},f={method:e,headers:p,credentials:"include"};i&&"GET"!==e&&"DELETE"!==e&&(f.body=d?i:JSON.stringify(i));const y=h.fetch||("undefined"!=typeof window?window.fetch.bind(window):fetch);if(!y)throw new Error("Fetch no disponible.");const w=(async()=>{const t=getCompleteConfig().toast||toast,r=(void 0!==s?s:!1!==h.showToasts)&&t;try{const i=await y(c,f);if(!i.ok){let e,s="Error en la petición";try{const t=await i.text();try{const r=JSON.parse(t);e=r,s=r.message||r.msg||r.error_description||r.error||s}catch{t.length<200&&(s=t)}}catch{}throw 401===i.status&&h.onUnauthorized&&h.onUnauthorized(),r&&t({title:"Error",description:s,variant:"error"}),new DypaiError(s,i.status,void 0,e)}!r||"POST"!==e&&"PUT"!==e&&"PATCH"!==e&&"DELETE"!==e||t({title:"Éxito",description:"Operación completada",variant:"success"});const s=i.headers.get("content-type")||"";return s.includes("application/pdf")||s.includes("image/")||s.includes("audio/")||s.includes("video/")||s.includes("application/octet-stream")||s.includes("application/zip")||s.includes("application/vnd.openxmlformats-officedocument")?await i.blob():s.includes("application/json")?await i.json():await i.text()}finally{u&&pendingRequests.delete(u)}})();return u&&pendingRequests.set(u,w),w}async function handleSmartDownload(t,e,r,i){const s=i?.method||(r?"POST":"GET"),{data:o,error:n}=await("GET"===s?t.get(e,{params:i?.params}):t.post(e,r,{params:i?.params}));if(n)throw n;if(o instanceof Blob){const t=window.URL.createObjectURL(o),e=document.createElement("a");e.href=t,e.download=i?.fileName||"archivo-descargado",document.body.appendChild(e),e.click(),window.URL.revokeObjectURL(t),document.body.removeChild(e)}else if(o&&"object"==typeof o&&("url"in o||"signed_url"in o||"signedUrl"in o)){const t=o.url||o.signed_url||o.signedUrl;if("string"==typeof t){const e=new URL(t).searchParams.get("response-content-disposition");let r;if(e){const t=decodeURIComponent(e).match(/filename\*?=(?:UTF-8''|")?([^";]+)/i);t?.[1]&&(r=t[1].replace(/"/g,""))}const s=i?.fileName||r;if(s){const e=document.createElement("a");e.href=t,e.download=s,document.body.appendChild(e),e.click(),document.body.removeChild(e)}else window.open(t,"_blank")}}}async function handleSmartUpload(t,e,r,i){const{data:s,error:o}=await t.post(e,{file_path:r.name,content_type:r.type||"application/octet-stream",size_bytes:r.size,confirm:!1,client_upload:!0,...i?.params||{}});if(o)throw o;const n=function(t){if(!t||"object"!=typeof t)return null;const e=t.upload_url||t.uploadUrl||t.url?t:null;if(e?.upload_url)return normalizeUploadPayload(e);const r=[t.data,t.result,t.output,t.payload].filter(Boolean);for(const t of r)if(t&&(t.upload_url||t.uploadUrl||t.url))return normalizeUploadPayload(t);const i=[t.steps_results,t.nodes_results].filter(Boolean);for(const t of i){const e=Array.isArray(t)?t:Object.values(t);for(const t of e)if(t&&(t.upload_url||t.uploadUrl||t.url))return normalizeUploadPayload(t)}return null}(s);if(!n?.upload_url)throw new DypaiError("The workflow did not return a valid upload URL (missing storage upload node?)",400);const{upload_url:a,method:h="PUT",headers:l={},file_path:c,storage_path:u}=n;i?.onProgress&&i.onProgress(10);const d=await fetch(a,{method:h,headers:{"Content-Type":r.type||"application/octet-stream",...l},body:r});if(!d.ok)throw new DypaiError("Direct upload to cloud storage failed",d.status);i?.onProgress&&i.onProgress(90);const p=i?.confirmEndpoint||e,{data:f,error:y}=await t.post(p,{...i?.params,bucket:n.bucket||i?.params?.bucket,file_path:c,storage_path:u,filename:r.name,content_type:r.type||"application/octet-stream",size_bytes:r.size,confirm:!0,client_upload:!0});if(y)throw y;return i?.onProgress&&i.onProgress(100),f}function normalizeUploadPayload(t){return t&&"object"==typeof t?{...t,upload_url:t.upload_url||t.uploadUrl||t.url,storage_path:t.storage_path||t.storagePath}:null}function createInternalApiClient(t){const e={get:createMethod(t,"GET"),post:createMethod(t,"POST"),put:createMethod(t,"PUT"),patch:createMethod(t,"PATCH"),delete:createMethod(t,"DELETE"),upload:async(t,r,i)=>{try{return{data:await handleSmartUpload(e,t,r,i),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Upload failed",t?.status??500)}}},download:async(t,r,i)=>{try{return await handleSmartDownload(e,t,r,{...i,method:"POST"}),{data:void 0,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Download failed",t?.status??500)}}}};return e}function createMethod(t,e){const r="POST"===e||"PUT"===e||"PATCH"===e;return async(i,s,o)=>{const n=t();let a,h={};r?(a=s,h=o||{}):(h=s||{},a=void 0);try{validateApiOptionsShape(e,i,h);const t=h.token||n.token||(globalTokenProvider?globalTokenProvider():"")||"",r=h.apiKey||n.apiKey;return{data:await callApi(t,e,i,a,h.showToasts,h.params,r,n.baseUrl),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Error desconocido",t?.status??500)}}}}function createApiClient(t){const e=()=>"function"==typeof t?t():t,r={get:createMethodFromCtx(e,"GET"),post:createMethodFromCtx(e,"POST"),put:createMethodFromCtx(e,"PUT"),patch:createMethodFromCtx(e,"PATCH"),delete:createMethodFromCtx(e,"DELETE"),upload:async(t,e,i)=>{try{return{data:await handleSmartUpload(r,t,e,i),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Upload failed",t?.status??500)}}},download:async(t,e,i)=>{try{return await handleSmartDownload(r,t,e,{...i,method:"POST"}),{data:void 0,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Download failed",t?.status??500)}}}};return r}function createMethodFromCtx(t,e){return async(r,i,s)=>{try{const o=t(),n=await async function(t,e,r,i){let s,o={};"POST"===t||"PUT"===t||"PATCH"===t?(s=r,o=i||{}):(o=r||{},s=void 0),validateApiOptionsShape(t,e,o);let n=o.token;return!n&&globalTokenProvider&&(n=globalTokenProvider()||""),n=n||"",{token:n,apiKey:o.apiKey,body:s,params:o.params,showToasts:o.showToasts}}(e,r,i,s),a=n.token||o.token||"",h=n.apiKey||o.apiKey;return{data:await callApi(a,e,r,n.body,n.showToasts,n.params,h,o.baseUrl),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Error desconocido",t?.status??500)}}}}class DypaiClient{get baseUrl(){return this.Dt}getAuthToken(){return this.auth.token||this._t||null}constructor(t){const{baseUrl:e,apiKey:r}=t;this.Dt=e,this._t=r,this.realtime=new DypaiRealtimeClient(e,()=>this.getAuthToken());const i=t.auth?.storageKey||t.storageKey;t.global&&function(t){serviceConfig={...serviceConfig,...t}}(t.global),this.auth=new AuthModule({baseUrl:e,apiKey:r,storageKey:i,storage:t.auth?.storage,autoRefreshToken:t.auth?.autoRefreshToken,persistSession:t.auth?.persistSession,passwordRecoveryRedirect:t.redirects?.passwordRecovery},null);const s=createInternalApiClient(()=>({token:this.auth.token,apiKey:r,baseUrl:e}));if(this.auth.api=s,this.db=new DataModule(s),t.serviceRoleKey)if("undefined"!=typeof window&&void 0!==window.document)console.error("[DYPAI SDK] ❌ serviceRoleKey detected in a browser environment. This is a security risk — the key grants admin-level database access. db.direct has been disabled. Use serviceRoleKey only from server-side code (Node.js, Bun, Deno, scripts).");else{const r=createInternalApiClient(()=>({token:t.serviceRoleKey,baseUrl:e}));this.db.direct=new DirectDBModule(r)}this.users=new UsersModule(s),this.roles=new RolesModule(s),this.api=createApiClient(()=>({token:this.auth.token||"",apiKey:r,baseUrl:e})),setTokenProvider(()=>this.auth.token);const o="undefined"!=typeof window?window.location.search:"";if("undefined"!=typeof window&&t.redirects){const e=t.redirects;this.auth.T?.then(()=>{if(this.auth.isPasswordRecoveryCallback&&e.passwordRecovery){if(!window.location.pathname.includes(e.passwordRecovery)){const t=e.passwordRecovery+(o||"");window.location.replace(t)}}else if(this.auth.lastError&&e.authError)window.location.pathname.includes(e.authError)||window.location.replace(e.authError);else if(this.auth.token&&e.signIn){const t=this.auth.consumeCallbackType();t&&"recovery"!==t&&"invite"!==t&&(window.location.pathname.includes(e.signIn)||window.location.replace(e.signIn))}})}configureApiService({onUnauthorized:()=>this.auth.handleSessionExpired()})}from(t){return this.db.from(t)}async me(){return this.auth.getUser()}}let globalConfig={};function applyConfigToAllServices(){try{const{configureApiService:t}=require("../services/ApiService");t(globalConfig)}catch(t){}}exports.AuthEmailNotVerifiedError=AuthEmailNotVerifiedError,exports.AuthInvalidCredentialsError=AuthInvalidCredentialsError,exports.AuthInvalidTokenError=AuthInvalidTokenError,exports.AuthSessionMissingError=AuthSessionMissingError,exports.AuthUserAlreadyExistsError=AuthUserAlreadyExistsError,exports.AuthWeakPasswordError=AuthWeakPasswordError,exports.DirectDBModule=DirectDBModule,exports.DirectQueryBuilder=DirectQueryBuilder,exports.DypaiClient=DypaiClient,exports.DypaiError=DypaiError,exports.DypaiRealtimeClient=DypaiRealtimeClient,exports.PACKAGE_INFO={name:"@dypai-ai/client-sdk",version:"1.10.3",description:"Official JavaScript/TypeScript SDK for DYPAI",features:["Authentication (email, OAuth, OTP)","Database CRUD","Direct database access (service role)","Custom endpoints (typed API)","File upload/download (Smart Upload)","User management (admin)","React hooks (useAuth, useEndpoint, useAction, useUpload)"]},exports.RealtimeChannel=RealtimeChannel,exports.RetryableFetchError=RetryableFetchError,exports.callApi=callApi,exports.classifyAuthError=classifyAuthError,exports.configureApiService=configureApiService,exports.configureDypaiServices=function(t){globalConfig={...globalConfig,toast:toast,...t},applyConfigToAllServices()},exports.createApiClient=createApiClient,exports.createClient=function(t,e,r){if(!t)throw new Error("createClient() requiere la URL base");return new DypaiClient({baseUrl:t,apiKey:"string"==typeof e?e:void 0,..."string"==typeof e?r:e})},exports.getGlobalConfig=function(){return{...globalConfig}},exports.isAuthEmailNotVerifiedError=function(t){return t instanceof AuthEmailNotVerifiedError},exports.isAuthInvalidCredentialsError=function(t){return t instanceof AuthInvalidCredentialsError},exports.isAuthInvalidTokenError=isAuthInvalidTokenError,exports.isAuthSessionMissingError=function(t){return t instanceof AuthSessionMissingError},exports.isAuthUserAlreadyExistsError=function(t){return t instanceof AuthUserAlreadyExistsError},exports.isAuthWeakPasswordError=function(t){return t instanceof AuthWeakPasswordError},exports.isDypaiError=function(t){return t instanceof DypaiError},exports.isRetryableFetchError=function(t){return t instanceof RetryableFetchError},exports.isRetryableStatus=isRetryableStatus,exports.reloadDypaiConfig=async function(){applyConfigToAllServices()},exports.resetGlobalConfig=function(){globalConfig={},applyConfigToAllServices()},exports.setToastFunction=function(t){customToastFunction=t},exports.setTokenProvider=setTokenProvider,exports.toast=toast,exports.toastError=(t,e)=>toast({title:t,description:e,variant:"error"}),exports.toastInfo=(t,e)=>toast({title:t,description:e,variant:"info"}),exports.toastSuccess=(t,e)=>toast({title:t,description:e,variant:"success"}),exports.toastWarning=(t,e)=>toast({title:t,description:e,variant:"warning"}),exports.useToast=function(){const t=customToastFunction||fallbackToast;return{toast:t,toastSuccess:(e,r)=>t({title:e,description:r,variant:"success"}),toastError:(e,r)=>t({title:e,description:r,variant:"error"}),toastWarning:(e,r)=>t({title:e,description:r,variant:"warning"}),toastInfo:(e,r)=>t({title:e,description:r,variant:"info"})}};
|
|
1
|
+
"use strict";class DypaiError extends Error{constructor(t,e=500,r,i){super(t),this.status=e,this.code=r,this.details=i,this.name="DypaiError"}}function normalizeUser(t){if(!t)return{};const e=t.user||t,r=e.name||e.user_metadata?.full_name||null,i=e.emailVerified??e.confirmed_at??null,s=e.app_metadata||{},o=e.user_metadata||{full_name:r};return{id:e.id,email:e.email,phone:e.phone||null,role:s.role||o.role||e.role||"authenticated",created_at:e.createdAt||e.created_at,updated_at:e.updatedAt||e.updated_at,confirmed_at:i?"boolean"==typeof i?(new Date).toISOString():i:null,last_sign_in_at:e.last_sign_in_at||null,app_metadata:s,user_metadata:{...o,full_name:r},roleDetails:{name:s.role||o.role||null,weight:0},appContext:{app_id:"default"}}}class AuthSessionMissingError extends DypaiError{constructor(t="Auth session missing"){super(t,400,"session_missing"),this.name="AuthSessionMissingError"}}class AuthInvalidCredentialsError extends DypaiError{constructor(t="Invalid login credentials"){super(t,400,"invalid_credentials"),this.name="AuthInvalidCredentialsError"}}class AuthInvalidTokenError extends DypaiError{constructor(t="Invalid or expired token"){super(t,401,"invalid_token"),this.name="AuthInvalidTokenError"}}function isAuthInvalidTokenError(t){return t instanceof AuthInvalidTokenError}class AuthWeakPasswordError extends DypaiError{constructor(t="Password is too weak",e=[]){super(t,400,"weak_password"),this.name="AuthWeakPasswordError",this.reasons=e}}class AuthEmailNotVerifiedError extends DypaiError{constructor(t="Email not verified"){super(t,403,"email_not_verified"),this.name="AuthEmailNotVerifiedError"}}class AuthUserAlreadyExistsError extends DypaiError{constructor(t="User already exists"){super(t,409,"user_already_exists"),this.name="AuthUserAlreadyExistsError"}}class RetryableFetchError extends DypaiError{constructor(t,e=0){super(t,e,"retryable_fetch"),this.name="RetryableFetchError"}}function isRetryableStatus(t){return 0===t||408===t||429===t||t>=500&&t<600}function classifyAuthError(t,e,r){const i=(t||"").toLowerCase(),s=(r||"").toLowerCase();return s.includes("invalid_token")||s.includes("invalid_grant")||i.includes("invalid token")||i.includes("expired")||i.includes("revoked")?new AuthInvalidTokenError(t):s.includes("session_missing")||i.includes("no session")||i.includes("session missing")?new AuthSessionMissingError(t):s.includes("invalid_credentials")||s.includes("invalid_login")||i.includes("invalid credentials")||i.includes("invalid login")?new AuthInvalidCredentialsError(t):s.includes("email_not_verified")||i.includes("email not verified")?new AuthEmailNotVerifiedError(t):s.includes("user_already_exists")||s.includes("user_exists")||i.includes("already exists")||i.includes("already registered")?new AuthUserAlreadyExistsError(t):s.includes("weak_password")||i.includes("password is too weak")||i.includes("password too short")?new AuthWeakPasswordError(t):isRetryableStatus(e)?new RetryableFetchError(t,e):new DypaiError(t,e,r)}let _debugEnabled=!1;function _log(t,...e){"error"===t?console.error(...e):_debugEnabled&&("warn"===t?console.warn(...e):console.log(...e))}async function parseAuthError(t,e){let r={};try{r=await t.json()}catch{}const i=r.msg||r.error_description||r.message||e,s=r.error_code||r.error||r.code||void 0;return classifyAuthError(i,t.status,s)}class Deferred{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const localStorageAdapter={getItem:t=>"undefined"==typeof window?null:window.localStorage.getItem(t),setItem:(t,e)=>{if("undefined"!=typeof window)try{window.localStorage.setItem(t,e)}catch(t){console.error("[DYPAI SDK] ❌ Error crítico guardando en localStorage (¿Quota/Permisos?):",t)}},removeItem:t=>{if("undefined"!=typeof window)try{window.localStorage.removeItem(t)}catch(t){}}};async function wrapAuthResponse(t){try{return{data:await t,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t.message||"Error de autenticación",t.status||400)}}}class AuthModule{constructor(t,e=null){this.config=t,this.t=null,this.i=null,this.o=null,this.h=null,this.l=null,this.u=[],this.p=null,this.A=null,this.m=!1,this.v=null,this.S=null,this.D=0,this._=0,this.k=null,this.storage=t.storage||localStorageAdapter,_debugEnabled=!!t.debug;const r=t.storageKey||this.deriveStorageKey(t.apiKey);if(_log("log",`[DYPAI SDK] 🛠️ Inicializando AuthModule (storageKey: ${r})`),this.STORAGE_KEY=`dypai-${r}-auth-session`,this.T=this.P().then(()=>{this.i&&(this.getUser().catch(()=>{}),this.startAutoRefresh());const t=this.A;this.A=null,t?setTimeout(()=>{this.m=!0,"PASSWORD_RECOVERY"!==t&&"AUTH_ERROR"!==t&&this.R(t)},0):this.m=!0}),"undefined"!=typeof window){if(window.addEventListener("visibilitychange",this.I.bind(this)),window.addEventListener("focus",this.I.bind(this)),"undefined"!=typeof BroadcastChannel)try{this.k=new BroadcastChannel(`bc:${this.STORAGE_KEY}`),this.k.addEventListener("message",t=>{const e=t.data;e&&"object"==typeof e&&(_log("log",`[DYPAI SDK] 📡 BroadcastChannel: ${e.type}`),"SIGNED_OUT"===e.type?(this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh(),this.R("SIGNED_OUT")):"SESSION_UPDATED"===e.type&&this.P(!1))})}catch(t){_log("warn","[DYPAI SDK] BroadcastChannel no disponible, usando storage event:",t)}window.addEventListener("storage",t=>{t.key===this.STORAGE_KEY&&(_log("log","[DYPAI SDK] 🔄 Sesión actualizada (storage event). Sincronizando..."),this.P(!1))})}}C(t){return 0===t||408===t||429===t||t>=500&&t<600}O(t){if(this.k)try{this.k.postMessage({type:t,timestamp:Date.now()})}catch{}}async I(){"undefined"!=typeof document&&"visible"===document.visibilityState&&(_log("log","[DYPAI SDK] 👁️ Ventana visible. Sincronizando estado y reiniciando auto-refresh..."),await this.P(!0),this.i&&this.startAutoRefresh())}deriveStorageKey(t){return t?t.substring(0,8):"default"}getBaseUrl(){return this.config.baseUrl||"http://localhost:8000"}$(){if("undefined"==typeof window)return;const t=new URLSearchParams(window.location.search);["token","type","error","error_code","error_description","access_token","refresh_token","expires_in"].forEach(e=>t.delete(e));const e=t.toString(),r=`${window.location.pathname}${e?`?${e}`:""}`;window.history.replaceState({},document.title,r)}U(t){try{if(!t)return;sessionStorage.setItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY,t),"recovery"===t||"invite"===t?sessionStorage.setItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY,"PASSWORD_RECOVERY"):sessionStorage.removeItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}M(t){try{sessionStorage.setItem(AuthModule.RESET_TOKEN_STORAGE_KEY,t)}catch{}}N(){try{return sessionStorage.getItem(AuthModule.RESET_TOKEN_STORAGE_KEY)}catch{return null}}K(){try{sessionStorage.removeItem(AuthModule.RESET_TOKEN_STORAGE_KEY)}catch{}}Y(){try{sessionStorage.removeItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY)}catch{}}L(t){if("undefined"==typeof window)return t;try{return new URL(t,window.location.origin).toString()}catch{return t}}j(t){isAuthInvalidTokenError(t)&&(this.K(),this.Y(),this.p={code:t.code||"invalid_token",message:t.message||"The reset link is invalid or has expired"},this.m&&this.R("AUTH_ERROR"))}get user(){return this.t}get token(){return this.i}get lastError(){return this.p}get hasPasswordRecoveryToken(){return!!this.N()}consumeCallbackType(){try{const t=sessionStorage.getItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY);return sessionStorage.removeItem(AuthModule.CALLBACK_TYPE_STORAGE_KEY),sessionStorage.removeItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY),t}catch{return null}}get isPasswordRecoveryCallback(){try{return"PASSWORD_RECOVERY"===sessionStorage.getItem(AuthModule.CALLBACK_REDIRECT_STORAGE_KEY)||!!sessionStorage.getItem(AuthModule.RESET_TOKEN_STORAGE_KEY)}catch{return!1}}isLoggedIn(){return!(!this.i||!this.t)}onAuthStateChange(t){return _log("log","[DYPAI SDK] 👂 Nuevo suscriptor añadido a onAuthStateChange"),this.u.push(t),this.T.then(()=>{const e=this.B();_log("log",`[DYPAI SDK] 📣 INITIAL_SESSION para suscriptor (Sesión activa: ${!!e})`),t("INITIAL_SESSION",e),this.isPasswordRecoveryCallback?t("PASSWORD_RECOVERY",e):this.p&&t("AUTH_ERROR",null)}).catch(e=>{_log("error","[DYPAI SDK] ❌ Error esperando recuperación de sesión para suscriptor:",e),t("INITIAL_SESSION",null)}),{data:{subscription:{unsubscribe:()=>{this.u=this.u.filter(e=>e!==t)}}}}}async signInWithPassword(t){return wrapAuthResponse((async()=>{const e=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-in/email`,r={email:t.email||t.identifier||"",password:t.password},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(r)});if(!i.ok)throw await parseAuthError(i,"Login failed");const s=await i.json(),o=this.q(s);return await this.W(o),o})())}async login(t){return this.signInWithPassword(t)}async signUp(t,e){return wrapAuthResponse((async()=>{const r=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/sign-up/email`,{email:i,phone:s,password:o,user_data:n,...a}=t,h=i||"",l=a.full_name||a.name||h.split("@")[0],c={...a,...n||{}},u={email:h,password:o,name:l};Object.keys(c).length>0&&Object.assign(u,c),e?.redirectTo?u.callbackURL=e.redirectTo:"undefined"!=typeof window&&(u.callbackURL=window.location.href.split("#")[0]);const d=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(u)});if(!d.ok)throw await parseAuthError(d,"Registration failed");const p=await d.json(),f=this.q(p),y={...f,confirmationRequired:!f.token};return y.token?await this.W(y):_log("log","[DYPAI SDK] 📧 Signup exitoso, se requiere confirmación de email."),y})())}async register(t){return this.signUp(t)}async getSession(){try{if(await this.T,!this.i||!this.t)return{data:null,error:null};const t=Math.floor(Date.now()/1e3);return(this.l||0)-t<30+AuthModule.CLOCK_SKEW_TOLERANCE_S&&(this.h||this.o)&&(_log("log","[DYPAI SDK] ⏳ getSession: Token próximo a expirar. Refrescando..."),await this.refreshSession().catch(t=>_log("warn","[DYPAI SDK] Error refreshing session in getSession:",t))),{data:{access_token:this.i,refresh_token:this.h||void 0,token_type:"bearer",user:this.t},error:null}}catch(t){return{data:null,error:new DypaiError(t.message,500)}}}async getUser(){return wrapAuthResponse((async()=>{await this.T;const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,r=await fetch(e,{headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw new DypaiError("Session invalid",r.status);const i=await r.json(),s=i?.jwt||r.headers.get("set-auth-jwt");s&&(this.i=s);const o=this.F(i);return this.J(o),o})())}async me(){return this.getUser()}async signInWithOAuth(t,e={}){return wrapAuthResponse((async()=>{if("undefined"==typeof window)throw new DypaiError("signInWithOAuth requiere un entorno de navegador (window no está disponible)",400);const{redirectTo:r=window.location.href,scopes:i}=e,s=this.config.baseUrl||"http://localhost:8000",o=new URLSearchParams({provider:t,callbackURL:r});i?.length&&o.set("scopes",i.join(" "));const n=`${s}/api/v0/auth/sign-in/social?${o.toString()}`;window.location.href=n})())}async signOut(){return wrapAuthResponse((async()=>{try{const t=this.o||this.i;if(t){const e=this.config.baseUrl||"http://localhost:8000";await fetch(`${e}/api/v0/auth/sign-out`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}})}}finally{this.G("signOut called")}})())}async logout(){return this.signOut()}async resetPasswordForEmail(t,e){return wrapAuthResponse((async()=>{const r=this.config.baseUrl||"http://localhost:8000",i={email:t};e?.redirectTo?i.redirectTo=e.redirectTo:this.config.passwordRecoveryRedirect&&"undefined"!=typeof window?i.redirectTo=this.L(this.config.passwordRecoveryRedirect):"undefined"!=typeof window&&(i.redirectTo=window.location.href.split("#")[0]);const s=await fetch(`${r}/api/v0/auth/request-password-reset`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(i)});if(!s.ok)throw await parseAuthError(s,"Recovery failed");return await s.json()})())}async recoverPassword(t){return this.resetPasswordForEmail(t.email)}async resendConfirmationEmail(t,e){return wrapAuthResponse((async()=>{const r=this.config.baseUrl||"http://localhost:8000",i={email:t};e?.redirectTo?i.callbackURL=e.redirectTo:"undefined"!=typeof window&&(i.callbackURL=window.location.href.split("#")[0]);const s=await fetch(`${r}/api/v0/auth/send-verification-email`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(i)});if(!s.ok)throw await parseAuthError(s,"Failed to resend confirmation email");return{message:"Confirmation email resent"}})())}async refreshSession(){if(this.v)return _log("log","[DYPAI SDK] 🔄 Refresco de sesión ya en curso. Esperando resolución..."),this.v.promise;const t=new Deferred;this.v=t;const e=`lock:${this.STORAGE_KEY}`;return(async()=>{try{const r=await async function(t,e,r){if("undefined"!=typeof globalThis&&globalThis.navigator?.locks?.request){const i=new AbortController,s=setTimeout(()=>i.abort(),e);try{return await globalThis.navigator.locks.request(t,{signal:i.signal},async()=>(clearTimeout(s),await r()))}catch(i){if("AbortError"===i.name)return console.warn(`[DYPAI SDK] ⚠️ Web Lock "${t}" timeout (${e}ms). Proceeding without lock.`),await r();throw i}}return await r()}(e,AuthModule.LOCK_ACQUIRE_TIMEOUT_MS,()=>this.V());t.resolve(r)}catch(e){const r=e instanceof DypaiError?e:new DypaiError(e.message||"Error refrescando sesión",401);t.resolve({data:null,error:r})}finally{this.v=null}})(),t.promise}async V(){try{const t=await this.storage.getItem(this.STORAGE_KEY);if(t){const e=JSON.parse(t),r=e.expires_at||0,i=Math.floor(Date.now()/1e3);if(e.access_token!==this.i&&r-i>60)return _log("log","[DYPAI SDK] 🔄 Otra pestaña ya refrescó el token. Adoptando sesión del storage."),this.i=e.access_token,this.o=e.session_token||e.access_token,this.h=e.refresh_token,this.l=e.expires_at,this.t=e.user,this.D=0,this.R("TOKEN_REFRESHED"),{data:{token:this.i,sessionToken:this.o||void 0,refreshToken:this.h||void 0,expiresAt:this.l||void 0,user:this.t},error:null}}const e=this.o||this.i;if(!e)throw new AuthSessionMissingError("No hay sesión activa");_log("log","[DYPAI SDK] 🔄 Verificando sesión activa con session_token...");const r=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/get-session`,i=new AbortController,s=setTimeout(()=>i.abort(),AuthModule.REFRESH_TIMEOUT_MS);let o;try{o=await fetch(r,{method:"GET",headers:{Authorization:`Bearer ${e}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},signal:i.signal})}finally{clearTimeout(s)}if(!o.ok){let t={};try{t=await o.json()}catch{}const e=o.status,r=t.error||t.code||"",i=(t.error_description||t.msg||"").toLowerCase(),s=this.C(e),n=e>=400&&e<500&&!s,a="invalid_grant"===r||i.includes("invalid refresh token")||i.includes("revoked")||i.includes("expired")||i.includes("invalid token");throw n&&a?(_log("error",`[DYPAI SDK] ❌ Token inválido/revocado (${e}). Limpiando sesión.`),this.G(`refreshSession failed (${e}: ${r})`)):n?(_log("error",`[DYPAI SDK] ❌ Error 4xx en refresh (${e}). Limpiando sesión.`),this.G(`refreshSession failed (${e})`)):_log("warn",`[DYPAI SDK] ⚠️ Error transitorio en refresh (${e}). Manteniendo sesión.`),new DypaiError(t.msg||t.error_description||"Refresh session failed",e)}const n=await o.json(),a=o.headers.get("set-auth-jwt"),h=n?.jwt,l=h||a,c=this.q(n,this.i);return l&&(c.token=l),c.sessionToken=e,this.D=0,await this.W(c),_log("log",`[DYPAI SDK] ✅ Session refresh exitoso (fresh JWT: ${!!l}).`),{data:c,error:null}}catch(t){this.D++,this._=Date.now();const e=t instanceof DOMException&&"AbortError"===t.name,r=e?new DypaiError("Refresh token timeout (servidor no responde)",408):t instanceof DypaiError?t:new DypaiError(t.message||"Error refrescando sesión",401),i=Math.min(AuthModule.RETRY_BASE_MS*Math.pow(2,this.D),AuthModule.MAX_RETRY_MS);return e?_log("error",`[DYPAI SDK] ⏱️ Timeout en refresh después de ${AuthModule.REFRESH_TIMEOUT_MS/1e3}s. Backoff: ${i}ms (intento ${this.D})`):_log("error","[DYPAI SDK] ❌ Refresh falló:",r.message,`(status: ${r.status}). Backoff: ${i}ms (intento ${this.D})`),this.D>=AuthModule.MAX_REFRESH_FAILURES&&(_log("error",`[DYPAI SDK] 🗑️ ${this.D} fallos consecutivos de refresh. Sesión irrecuperable. Limpiando...`),this.G(`${this.D} consecutive refresh failures`),this.D=0),{data:null,error:r}}}async signInWithOtp(t){return wrapAuthResponse((async()=>{if(t.phone)throw new DypaiError("Phone OTP is not supported by the current DYPAI auth backend",400);if(!t.email)throw new DypaiError("Email is required for magic link sign-in",400);const e=await this.signInWithMagicLink(t.email);if(e.error)throw e.error;return{status:!0}})())}async verifyOtp(t){return wrapAuthResponse((async()=>{if(!t.token)throw new DypaiError("Verification token is required",400);if("magiclink"===t.type)return await this.H(t.token);if("signup"===t.type||"email_change"===t.type){const e=await this.X(t.token);if(e)return e;throw new DypaiError("Verification completed but no session was returned",400)}if("recovery"===t.type||"invite"===t.type)throw new DypaiError("Recovery links do not use verifyOtp(). Use setPassword() after the PASSWORD_RECOVERY callback.",400);throw new DypaiError("Unsupported verification type",400)})())}async updateUser(t){return wrapAuthResponse((async()=>{const e=this.o||this.i;if(!e)throw new AuthSessionMissingError("No hay sesión activa");const r=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/update-user`,i=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify(t)});if(!i.ok){const t=await i.json();throw new DypaiError(t.detail||"Update user failed",i.status)}const s=await i.json(),o=this.F(s);return this.J(o),o})())}async setPassword(t){return wrapAuthResponse((async()=>{if("undefined"==typeof window)throw new DypaiError("setPassword requiere un entorno de navegador",400);const e=new URLSearchParams(window.location.search).get("token")||this.N();if(!e){if(this.o||this.i){const e=await this.updateUser({password:t});if(e.error)throw e.error;return this.Y(),this.$(),{status:!0}}throw new DypaiError("No reset token found in the current URL",400)}const r=this.config.baseUrl||"http://localhost:8000",i=await fetch(`${r}/api/v0/auth/reset-password`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({newPassword:t,token:e})});if(!i.ok){const t=await parseAuthError(i,"Reset password failed");throw this.j(t),t}this.K(),this.Y(),this.$();const s=await i.json();if(s?.jwt&&s?.session_token&&s?.user){const t={token:s.jwt,sessionToken:s.session_token,user:this.F(s),expiresAt:s.session?.expiresAt?Math.floor(new Date(s.session.expiresAt).getTime()/1e3):void 0};await this.W(t)}return{status:s?.status??!0}})())}async signInWithMagicLink(t,e){return wrapAuthResponse((async()=>{const r=this.config.baseUrl||"http://localhost:8000",i=e?.redirectTo||("undefined"!=typeof window?window.location.href.split("#")[0]:void 0),s=await fetch(`${r}/api/v0/auth/sign-in/magic-link`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({email:t,callbackURL:i})});if(!s.ok)throw await parseAuthError(s,"Magic link failed");return{message:"Magic link sent"}})())}async enableTwoFactor(){return wrapAuthResponse((async()=>{const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/two-factor/enable`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw await parseAuthError(r,"Enable 2FA failed");return await r.json()})())}async verifyTwoFactor(t){return wrapAuthResponse((async()=>{const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/two-factor/verify-totp`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.o||this.i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({code:t})});if(!r.ok)throw await parseAuthError(r,"2FA verification failed");const i=await r.json(),s={token:i.jwt||i.token||this.i,expiresIn:604800,expiresAt:Math.floor(Date.now()/1e3)+604800,user:this.F(i)};return await this.W(s),s})())}async disableTwoFactor(){return wrapAuthResponse((async()=>{const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/two-factor/disable`,{method:"POST",headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw await parseAuthError(r,"Disable 2FA failed");return{message:"2FA disabled"}})())}async createOrganization(t,e){return wrapAuthResponse((async()=>{const r=this.o||this.i;if(!r)throw new AuthSessionMissingError("No hay sesión activa");const i=this.config.baseUrl||"http://localhost:8000",s=await fetch(`${i}/api/v0/auth/organization/create`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({name:t,slug:e})});if(!s.ok)throw await parseAuthError(s,"Create organization failed");return await s.json()})())}async inviteMember(t,e,r){return wrapAuthResponse((async()=>{const i=this.o||this.i;if(!i)throw new AuthSessionMissingError("No hay sesión activa");const s=this.config.baseUrl||"http://localhost:8000",o=await fetch(`${s}/api/v0/auth/organization/invite-member`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}},body:JSON.stringify({organizationId:t,email:e,role:r||"member"})});if(!o.ok)throw await parseAuthError(o,"Invite member failed");return await o.json()})())}async listOrganizations(){return wrapAuthResponse((async()=>{const t=this.o||this.i;if(!t)throw new AuthSessionMissingError("No hay sesión activa");const e=this.config.baseUrl||"http://localhost:8000",r=await fetch(`${e}/api/v0/auth/organization/list`,{headers:{Authorization:`Bearer ${t}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!r.ok)throw await parseAuthError(r,"List organizations failed");return await r.json()})())}startAutoRefresh(){this.stopAutoRefresh(),_log("log","[DYPAI SDK] ⏱️ Auto-refresh iniciado (tick cada 30s)."),this.Z(),this.S=setInterval(()=>this.Z(),AuthModule.AUTO_REFRESH_TICK_DURATION_MS)}stopAutoRefresh(){this.S&&(_log("log","[DYPAI SDK] ⏹️ Auto-refresh detenido."),clearInterval(this.S),this.S=null)}async Z(){try{const t=Math.floor(Date.now()/1e3),e=this.l||0;if(!this.h&&!this.o||!e)return;if(1e3*(e-t)>AuthModule.AUTO_REFRESH_TICK_THRESHOLD*AuthModule.AUTO_REFRESH_TICK_DURATION_MS+1e3*AuthModule.CLOCK_SKEW_TOLERANCE_S)return;if(this.D>0){const t=Math.min(AuthModule.RETRY_BASE_MS*Math.pow(2,this.D),AuthModule.MAX_RETRY_MS);if(Date.now()-this._<t)return}_log("log",`[DYPAI SDK] ⏱️ Auto-refresh tick: token expira en ${e-t}s. Refrescando...`),await this.refreshSession()}catch(t){_log("error","[DYPAI SDK] ❌ Error en auto-refresh tick:",t)}}F(t){return t?normalizeUser(t):(_log("warn","[DYPAI SDK] ⚠️ Intentando normalizar un usuario inexistente (data es null/undefined)"),{})}q(t,e){const r="number"==typeof t?.expiresIn?t.expiresIn:"number"==typeof t?.expires_in?t.expires_in:604800,i=t?.jwt||void 0,s=t?.session_token||t?.token||t?.access_token||t?.session?.token||t?.session?.accessToken||t?.session?.access_token||e;return{token:i||s,sessionToken:s,refreshToken:t?.refreshToken||t?.refresh_token||t?.session?.refreshToken||t?.session?.refresh_token||void 0,expiresIn:r,expiresAt:Math.floor(Date.now()/1e3)+r,user:this.F(t)}}async W(t){this.p=null,t.token&&(this.i=t.token,this.o=t.sessionToken||t.token,void 0!==t.refreshToken&&(this.h=t.refreshToken||null),void 0!==t.expiresAt&&(this.l=t.expiresAt||null),await this.J(t.user,t.token,t.refreshToken,t.expiresAt),_log("log",`[DYPAI SDK] 💾 Sesión persistida en storage (expires_at: ${this.l}, has_refresh: ${!!this.h}, has_session_token: ${!!this.o})`),this.startAutoRefresh())}async J(t,e,r,i){this.t=t,e&&(this.i=e),void 0!==r&&(this.h=r||null),void 0!==i&&(this.l=i||null);try{if(this.i&&this.t){const t={access_token:this.i,session_token:this.o,refresh_token:this.h,expires_at:this.l,user:this.t,version:2};await this.storage.setItem(this.STORAGE_KEY,JSON.stringify(t)),this.O("SESSION_UPDATED")}else _log("warn","[DYPAI SDK] ⚠️ _updateUser: No se guardó sesión porque falta token o user.")}catch(t){_log("error","[DYPAI SDK] ❌ Error guardando sesión en storage:",t)}if(this.m){const t=e?"SIGNED_IN":"USER_UPDATED";this.R(t)}}async G(t="unknown"){_log("log",`[DYPAI SDK] 🧹 Limpiando sesión del estado y storage. Motivo: ${t}`),this.i=null,this.o=null,this.h=null,this.t=null,this.l=null,this.stopAutoRefresh();try{await this.storage.removeItem(this.STORAGE_KEY),_log("log","[DYPAI SDK] ✅ Storage limpiado con éxito.")}catch(t){_log("error","[DYPAI SDK] ❌ Error eliminando sesión de storage:",t)}this.O("SIGNED_OUT"),this.R("SIGNED_OUT")}async H(t){const e=this.getBaseUrl(),r=new URL(`${e}/api/v0/auth/magic-link/verify`);r.searchParams.set("token",t);const i=await fetch(r.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await parseAuthError(i,"Magic link verification failed");const s=await i.json(),o=this.q(s);return await this.W(o),o}async X(t){const e=this.getBaseUrl(),r=new URL(`${e}/api/v0/auth/verify-email`);r.searchParams.set("token",t);const i=await fetch(r.toString(),{method:"GET",headers:{...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!i.ok)throw await parseAuthError(i,"Email verification failed");if(!(i.headers.get("content-type")||i.headers.get("Content-Type")||"").includes("application/json"))return null;const s=await i.json(),o=this.q(s);return o.token&&o.user?.id?(await this.W(o),o):null}async tt(t,e){const r=e||("undefined"!=typeof window&&window.location.pathname.includes("reset-password")?"recovery":null);if(!r)return!1;if(_log("log",`[DYPAI SDK] 🔗 Auth callback detectado en query (type: ${r}). Procesando...`),this.U(r),"recovery"===r||"invite"===r)return this.M(t),this.$(),this.A="PASSWORD_RECOVERY",!0;if(this.$(),"magiclink"===r)return await this.H(t),this.A="SIGNED_IN",!0;if("signup"===r||"email_change"===r){const e=await this.X(t);return this.A=e?.token?"SIGNED_IN":null,!0}return!1}async P(t=!0){_log("log","[DYPAI SDK] 🔍 Iniciando recuperación de sesión...");try{if(await this.et())return void _log("log","[DYPAI SDK] ✅ Sesión establecida desde callback URL. Saltando recuperación de localStorage.");const e=await this.storage.getItem(this.STORAGE_KEY);if(!e)return void _log("log","[DYPAI SDK] ℹ️ No se encontró ninguna sesión (storage vacío).");let r;try{r=JSON.parse(e)}catch{return _log("warn","[DYPAI SDK] ⚠️ Sesión corrupta (JSON inválido). Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY)}if(!r||"object"!=typeof r||"string"!=typeof r.access_token)return _log("warn","[DYPAI SDK] ⚠️ Sesión con formato inválido. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if((r.version||1)<2&&!r.session_token)return _log("warn","[DYPAI SDK] 🔄 Sesión en formato antiguo (v1) sin session_token. Limpiando..."),void await this.storage.removeItem(this.STORAGE_KEY);if(this.i=r.access_token,this.o="string"==typeof r.session_token?r.session_token:r.access_token,this.h="string"==typeof r.refresh_token?r.refresh_token:null,this.l="number"==typeof r.expires_at?r.expires_at:null,this.t=r.user&&"object"==typeof r.user?r.user:null,t){_log("log","[DYPAI SDK] 🔄 Validando sesión con el servidor...");const t=await this.refreshSession();if(t.error)return void _log("warn","[DYPAI SDK] ⚠️ Sesión inválida. Limpiando:",t.error.message)}this.A="SIGNED_IN"}catch(t){_log("error","[DYPAI SDK] ❌ Error crítico durante la recuperación de sesión:",t)}}async et(){if("undefined"==typeof window)return!1;try{const t=new URLSearchParams(window.location.search),e=t.get("error"),r=t.get("token"),i=t.get("type");if(e){const r=t.get("error_code")||e,i=t.get("error_description")?.replace(/\+/g," ")||"Authentication error";return _log("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${r} — ${i}`),this.p={code:r,message:i},this.$(),this.A="AUTH_ERROR",!0}if(r)return await this.tt(r,i);const s=window.location.hash.substring(1);if(!s)return!1;const o=new URLSearchParams(s);if(o.get("error")){const t=o.get("error_code")||"unknown",e=o.get("error_description")?.replace(/\+/g," ")||"Authentication error";return _log("warn",`[DYPAI SDK] ⚠️ Auth callback error: ${t} — ${e}`),this.p={code:t,message:e},this.$(),this.A="AUTH_ERROR",!0}const n=o.get("access_token");if(!n)return!1;const a=o.get("refresh_token"),h=o.get("type"),l=parseInt(o.get("expires_in")||"3600",10);_log("log",`[DYPAI SDK] 🔗 Auth callback detectado en URL hash (type: ${h||"unknown"}). Procesando...`),this.U(h),this.$(),this.o=n,this.i=n,this.h=a||null,this.l=Math.floor(Date.now()/1e3)+l;const c=await fetch(`${this.getBaseUrl()}/api/v0/auth/get-session`,{headers:{Authorization:`Bearer ${n}`,...this.config.apiKey&&{"x-api-key":this.config.apiKey}}});if(!c.ok)return _log("error",`[DYPAI SDK] ❌ Callback URL: token inválido o expirado (status: ${c.status}). Limpiando...`),this.i=null,this.o=null,this.h=null,this.l=null,this.p={code:"token_invalid",message:"The authentication link is invalid or has expired"},this.R("AUTH_ERROR"),!0;const u=await c.json(),d=u?.jwt||c.headers.get("set-auth-jwt"),p=this.F(u),f={token:d||n,sessionToken:n,refreshToken:a||void 0,expiresIn:l,expiresAt:this.l,user:p};return await this.W(f),this.A="recovery"===h||"invite"===h?"PASSWORD_RECOVERY":"SIGNED_IN",_log("log",`[DYPAI SDK] 🔑 Callback type=${h}. Pending event: ${this.A}`),!0}catch(t){return _log("error","[DYPAI SDK] ❌ Error procesando callback URL:",t),!1}}B(){return this.i&&this.t?{access_token:this.i,refresh_token:this.h||void 0,expires_at:this.l||void 0,token_type:"bearer",user:this.t}:null}R(t="USER_UPDATED"){const e=this.B();_log("log",`[DYPAI SDK] 📢 Notificando a ${this.u.length} suscriptores: Evento=${t} (Sesión activa: ${!!e})`),this.u.forEach(r=>r(t,e))}handleSessionExpired(){this.G("handleSessionExpired called (likely 401 from API)")}}AuthModule.REFRESH_TIMEOUT_MS=15e3,AuthModule.MAX_REFRESH_FAILURES=5,AuthModule.AUTO_REFRESH_TICK_DURATION_MS=3e4,AuthModule.AUTO_REFRESH_TICK_THRESHOLD=3,AuthModule.RETRY_BASE_MS=200,AuthModule.MAX_RETRY_MS=3e4,AuthModule.LOCK_ACQUIRE_TIMEOUT_MS=5e3,AuthModule.CLOCK_SKEW_TOLERANCE_S=10,AuthModule.CALLBACK_TYPE_STORAGE_KEY="dypai-auth-callback-type",AuthModule.CALLBACK_REDIRECT_STORAGE_KEY="dypai-auth-callback-redirect",AuthModule.RESET_TOKEN_STORAGE_KEY="dypai-auth-reset-token";class RealtimeChannel{constructor(t,e,r){this.ws=null,this.bindings=[],this.rt={},this.it=!1,this.topic=t,this.client=e,this.config=r||{}}on(t,e,r){return this.bindings.push({type:t,filter:e,callback:r}),this}subscribe(t){this.st=t;const e=this.client.ot();this.ws=e;const r=()=>{const r=this.bindings.filter(t=>"postgres_changes"===t.type);for(const t of r)e.send(JSON.stringify({type:"subscribe",table:t.filter.table||"*",event:t.filter.event||"*",filter:t.filter.filter?this.nt(t.filter.filter):void 0,schema:t.filter.schema||"public"}));const i=this.bindings.some(t=>"broadcast"===t.type),s=this.bindings.some(t=>"presence"===t.type);(i||s)&&e.send(JSON.stringify({type:"join",channel:this.topic,presence:!!s||void 0})),this.it=!0,t?.("SUBSCRIBED")};e.readyState===WebSocket.OPEN?r():e.readyState===WebSocket.CONNECTING&&e.addEventListener("open",r,{once:!0}),this.ht&&e.removeEventListener("message",this.ht);const i=t=>{try{const e=JSON.parse("string"==typeof t.data?t.data:"");this.ct(e)}catch{}};return this.ht=i,e.addEventListener("message",i),e.addEventListener("close",()=>{this.it=!1,t?.("CLOSED")},{once:!0}),e.addEventListener("error",()=>{t?.("CHANNEL_ERROR",new Error("WebSocket error"))},{once:!0}),this}ut(t){const e=this.bindings.filter(t=>"postgres_changes"===t.type);for(const r of e)t.send(JSON.stringify({type:"subscribe",table:r.filter.table||"*",event:r.filter.event||"*",filter:r.filter.filter?this.nt(r.filter.filter):void 0,schema:r.filter.schema||"public"}));const r=this.bindings.some(t=>"broadcast"===t.type),i=this.bindings.some(t=>"presence"===t.type);(r||i)&&t.send(JSON.stringify({type:"join",channel:this.topic,presence:!!i||void 0})),this.st?.("SUBSCRIBED")}async send(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"broadcast",channel:this.topic,event:t.event,payload:t.payload,self:this.config.broadcast?.self||!1})),"ok"}catch{return"error"}}async track(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return"error";try{return this.ws.send(JSON.stringify({type:"presence",channel:this.topic,data:t})),"ok"}catch{return"error"}}async untrack(){return"ok"}presenceState(){return{...this.rt}}async unsubscribe(){if(this.ht&&this.ws&&(this.ws.removeEventListener("message",this.ht),this.ht=void 0),this.ws?.readyState===WebSocket.OPEN){const t=this.bindings.filter(t=>"postgres_changes"===t.type);for(const e of t)this.ws.send(JSON.stringify({type:"unsubscribe",table:e.filter.table}));this.ws.send(JSON.stringify({type:"leave",channel:this.topic}))}return this.it=!1,this.bindings=[],this.rt={},"ok"}ct(t){if("change"===t.type)for(const e of this.bindings)"postgres_changes"===e.type&&(e.filter.table&&"*"!==e.filter.table&&e.filter.table!==t.table||e.filter.event&&"*"!==e.filter.event&&e.filter.event!==t.event||e.callback({schema:"public",table:t.table,eventType:t.event,new:t.record||{},old:t.old_record||{},commit_timestamp:t.timestamp,truncated:t.truncated||!1}));if("broadcast"===t.type&&t.channel===this.topic)for(const e of this.bindings)"broadcast"===e.type&&("*"!==e.filter.event&&e.filter.event!==t.event||e.callback({event:t.event,payload:t.payload,sender:t.sender,timestamp:t.timestamp}));if("presence_state"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";this.rt={};for(const[r,i]of Object.entries(t.presences||{})){const t=i[e]||r;this.rt[t]||(this.rt[t]=[]),this.rt[t].push(i)}for(const t of this.bindings)"presence"===t.type&&("sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"}))}if("presence_diff"===t.type&&t.channel===this.topic){const e=this.config.presence?.key||"userId";if(t.joins)for(const[r,i]of Object.entries(t.joins)){const t=i[e]||r;this.rt[t]||(this.rt[t]=[]),this.rt[t].push(i);for(const e of this.bindings)"presence"===e.type&&("join"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"join",key:t,newPresences:[i],currentPresences:this.rt[t]}))}if(t.leaves)for(const[r,i]of Object.entries(t.leaves)){const t=i[e]||r;this.rt[t]=(this.rt[t]||[]).filter(t=>t.userId!==r),0===this.rt[t].length&&delete this.rt[t];for(const e of this.bindings)"presence"===e.type&&("leave"!==e.filter.event&&"*"!==e.filter.event||e.callback({event:"leave",key:t,leftPresences:[i],currentPresences:this.rt[t]||[]}))}for(const t of this.bindings)"presence"!==t.type||"sync"!==t.filter.event&&"*"!==t.filter.event||t.callback({event:"sync"})}}nt(t){const e=t.match(/^(\w+)=eq\.(.+)$/);if(e)return{[e[1]]:e[2]}}}class DypaiRealtimeClient{constructor(t,e){this.ws=null,this.channels=new Map,this.heartbeatTimer=null,this.reconnectAttempt=0,this.reconnectTimer=null,this.baseUrl=t,this.tokenProvider=e}channel(t,e){const r=this.channels.get(t);if(r)return r;const i=new RealtimeChannel(t,this,e?.config);return this.channels.set(t,i),i}getChannels(){return Array.from(this.channels.values())}async removeChannel(t){await t.unsubscribe(),this.channels.delete(t.topic),0===this.channels.size&&this.disconnect()}async removeAllChannels(){for(const t of this.channels.values())await t.unsubscribe();this.channels.clear(),this.disconnect()}async setAuth(t){t&&(this.tokenProvider=()=>t),this.ws&&this.channels.size>0&&(this.disconnect(),this.ot())}disconnect(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.ws?.close(),this.ws=null}ot(){if(this.ws&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING))return this.ws;const t=this.tokenProvider(),e=this.baseUrl.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(t||"")}`,r=new WebSocket(e);return this.ws=r,r.addEventListener("open",()=>{this.reconnectAttempt=0;for(const t of this.channels.values())t.it&&t.ut(r);this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{r.readyState===WebSocket.OPEN&&r.send(JSON.stringify({type:"heartbeat"}))},25e3)}),r.addEventListener("close",()=>{if(this.ws=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.channels.size>0&&!this.reconnectTimer){const t=DypaiRealtimeClient.RECONNECT_INTERVALS[Math.min(this.reconnectAttempt,DypaiRealtimeClient.RECONNECT_INTERVALS.length-1)];this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.channels.size>0&&this.ot()},t)}}),r}}DypaiRealtimeClient.RECONNECT_INTERVALS=[1e3,2e3,5e3,1e4,3e4];class DataModule{get direct(){if(!this.dt)throw new Error("client.db.direct is not available. Pass serviceRoleKey to createClient() to enable direct database access.\n\nExample:\n const client = createClient(url, { serviceRoleKey: process.env.DYPAI_SERVICE_ROLE_KEY });\n\nWARNING: serviceRoleKey grants admin-level access. Only use from server-side code (scripts, migrations, seeds). Never expose it in browser/client-side code.");return this.dt}set direct(t){this.dt=t}constructor(t){this.api=t}from(t){return new QueryBuilder(t,this.api)}}class QueryBuilder{constructor(t,e){this.table=t,this.api=e}async select(t={}){return this.api.get(this.table,{params:t})}async insert(t){return this.api.post(this.table,t)}async update(t,e){return this.api.patch(`${this.table}/${t}`,e)}async delete(t){return this.api.delete(`${this.table}/${t}`)}}class DirectDBModule{constructor(t){this.api=t}from(t){return new DirectQueryBuilder(t,this.api)}async sql(t,e,r){return this.api.post("/api/v0/admin/db/sql",{query:t,params:e||[],...void 0!==r?.limit&&{limit:r.limit}})}}class DirectQueryBuilder{constructor(t,e){this.table=t,this.api=e,this.ft="public",this.yt=[],this.wt=100,this.At=0,this.vt="ASC"}schema(t){return this.ft=t,this}eq(t,e){return this.yt.push({column:t,operator:"eq",value:e}),this}neq(t,e){return this.yt.push({column:t,operator:"neq",value:e}),this}gt(t,e){return this.yt.push({column:t,operator:"gt",value:e}),this}gte(t,e){return this.yt.push({column:t,operator:"gte",value:e}),this}lt(t,e){return this.yt.push({column:t,operator:"lt",value:e}),this}lte(t,e){return this.yt.push({column:t,operator:"lte",value:e}),this}like(t,e){return this.yt.push({column:t,operator:"ilike",value:e}),this}ilike(t,e){return this.like(t,e)}isNull(t){return this.yt.push({column:t,operator:"is_null",value:null}),this}is(t,e){return this.isNull(t)}notNull(t){return this.yt.push({column:t,operator:"not_null",value:null}),this}in(t,e){return this.yt.push({column:t,operator:"in",value:e}),this}contains(t,e){return this.yt.push({column:t,operator:"array_contains",value:e}),this}containedBy(t,e){return this.yt.push({column:t,operator:"contained_by",value:e}),this}overlaps(t,e){return this.yt.push({column:t,operator:"overlaps",value:e}),this}textSearch(t,e){return this.yt.push({column:t,operator:"fts",value:e}),this}phraseSearch(t,e){return this.yt.push({column:t,operator:"phfts",value:e}),this}not(t,e,r){return this.yt.push({column:t,operator:"not",value:{operator:e,value:r}}),this}or(t){return this.yt.push({operator:"or",or:t}),this}limit(t){return this.wt=t,this}offset(t){return this.At=t,this}orderBy(t,e="ASC"){return this.St=t,this.vt=e,this}async select(t){const e=[...this.yt];let r=this.Et;if("string"==typeof t)r=t;else if(t&&"object"==typeof t)for(const[r,i]of Object.entries(t))e.push({column:r,operator:"eq",value:i});return this.execute("select",{...r&&{columns:r},filters:e,limit:this.wt,offset:this.At,sort_by:this.St,order:this.vt})}async single(){const t=await this.select();if(t.error)return t;const e=t.data;return!e||Array.isArray(e)&&0===e.length?{data:null,error:new DypaiError("No rows found.",404)}:{data:Array.isArray(e)?e[0]:e,error:null}}async maybeSingle(){const t=await this.select();if(t.error)return t;const e=t.data;return!e||Array.isArray(e)&&0===e.length?{data:null,error:null}:{data:Array.isArray(e)?e[0]:e,error:null}}async count(){const t=await this.execute("select",{columns:"count(*) as count",filters:[...this.yt],limit:1,offset:0});if(t.error)return t;const e=t.data;return{data:Array.isArray(e)&&e.length>0?Number(e[0].count):0,error:null}}async insert(t){if(Array.isArray(t)&&t.length>1e3){const e=[];for(let r=0;r<t.length;r+=1e3){const i=t.slice(r,r+1e3),{data:s,error:o}=await this.execute("insert",{data:i,mode:"bulk"});if(o)return{data:null,error:o};s&&e.push(...Array.isArray(s)?s:[s])}return{data:e,error:null}}const e=Array.isArray(t)?"bulk":"single";return this.execute("insert",{data:t,mode:e})}async update(t){return 0===this.yt.length?{data:null,error:new DypaiError("Update requires at least one filter. Use .eq(), .in(), etc.",400)}:this.execute("update",{data:t,filters:this.yt})}async delete(){return 0===this.yt.length?{data:null,error:new DypaiError("Delete requires at least one filter.",400)}:this.execute("delete",{filters:this.yt})}async upsert(t,e="id"){return this.execute("upsert",{data:t,conflict_column:e})}async execute(t,e){return this.api.post("/api/v0/admin/db/query",{operation:t,schema_name:this.ft,table_name:this.table,...e})}}class UsersModule{constructor(t){this.api=t}async list(t={}){const e=await this.api.get("admin/users",{params:t});return e.data?.users&&(e.data.users=e.data.users.map(normalizeUser)),e}async create(t){const e=await this.api.post("admin/users",t);return e.data&&(e.data=normalizeUser(e.data)),e}async update(t,e){const r=await this.api.put(`admin/users/${t}`,e);return r.data&&(r.data=normalizeUser(r.data)),r}async delete(t){return this.api.delete(`admin/users/${t}`)}}class RolesModule{constructor(t){this.api=t}async list(t={}){const e={};void 0!==t.include_global&&(e.include_global=t.include_global);const r=await this.api.get("admin/roles",{params:e});return Array.isArray(r.data)?r.data={roles:r.data,total:r.data.length}:r.data?.roles&&null==r.data.total&&(r.data.total=r.data.roles.length),r}async get(t){return this.api.get(`admin/roles/${t}`)}async create(t){return this.api.post("admin/roles",t)}async update(t,e){return this.api.put(`admin/roles/${t}`,e)}async delete(t){return this.api.delete(`admin/roles/${t}`)}}let customToastFunction=null;const fallbackToast=t=>{const{title:e,description:r,variant:i="default"}=t,s=`${"error"===i?"❌":"success"===i?"✅":"warning"===i?"⚠️":"info"===i?"ℹ️":"📢"} ${e}${r?`: ${r}`:""}`;"error"===i?console.error(s):"warning"===i&&console.warn(s)},toast=t=>(customToastFunction||fallbackToast)(t);let serviceConfig={},globalTokenProvider=null;function setTokenProvider(t){globalTokenProvider=t}function configureApiService(t){serviceConfig={...serviceConfig,...t}}const pendingRequests=new Map,API_OPTION_KEYS=new Set(["params","token","apiKey","showToasts"]);function validateApiOptionsShape(t,e,r){if(null==r)return;if("object"!=typeof r||Array.isArray(r))throw new DypaiError(`Invalid ${t} options for endpoint "${e}": options must be an object. Use { params: { ... } } for query parameters.`,400,"invalid_api_options");const i=Object.keys(r).filter(t=>!API_OPTION_KEYS.has(t));if(!i.length)return;const s=i.map(t=>`"${t}"`).join(", "),o=i[0],n=t.toLowerCase();throw new DypaiError(`Invalid ${t} options for endpoint "${e}": unknown option ${s}. ${"GET"===t||"DELETE"===t?`Use client.api.${n}("${e}", { params: { ${o}: value } }).`:`Pass request data as the second argument and options as the third argument, for example client.api.${n}("${e}", body, { params: { ${o}: value } }).`}`,400,"invalid_api_options")}function getCompleteConfig(){let t=serviceConfig;try{const{getGlobalConfig:e}=require("../config/global-config");t={...e(),...serviceConfig}}catch(t){}return t}async function callApi(t,e,r,i,s,o,n,a){const h=getCompleteConfig(),l=a||null;if(!l&&!r.startsWith("http"))throw new Error("Base URL no definida. Usa createClient(url[, apiKey]).");if("string"!=typeof r||!r.trim())throw new Error("Endpoint debe ser un string válido");let c;if(r.startsWith("http"))c=r;else{const t=l.replace(/\/+$/,"");c=r.startsWith("/")?t+r:t+"/api/v0/"+r}if(o&&Object.keys(o).length>0){const t=new URLSearchParams,e=(r,i)=>{null!=i&&(Array.isArray(i)?i.forEach((t,i)=>e(`${r}[${i}]`,t)):"object"==typeof i?Object.entries(i).forEach(([t,i])=>e(`${r}[${t}]`,i)):t.append(r,String(i)))};Object.entries(o).forEach(([t,r])=>e(t,r));const r=t.toString();r&&(c+=`?${r}`)}const u="GET"===e?`${e}:${c}:${JSON.stringify(i)}`:null;if(u&&pendingRequests.has(u))return pendingRequests.get(u);const d=i instanceof FormData,p={...h.headers||{},...d?{}:{"Content-Type":"application/json"},...t&&{Authorization:`Bearer ${t}`},...n&&{"x-api-key":n}},f={method:e,headers:p,credentials:"include"};i&&"GET"!==e&&"DELETE"!==e&&(f.body=d?i:JSON.stringify(i));const y=h.fetch||("undefined"!=typeof window?window.fetch.bind(window):fetch);if(!y)throw new Error("Fetch no disponible.");const w=(async()=>{const t=getCompleteConfig().toast||toast,r=(void 0!==s?s:!1!==h.showToasts)&&t;try{const i=await y(c,f);if(!i.ok){let e,s="Error en la petición";try{const t=await i.text();try{const r=JSON.parse(t);e=r,s=r.message||r.msg||r.error_description||r.error||s}catch{t.length<200&&(s=t)}}catch{}throw 401===i.status&&h.onUnauthorized&&h.onUnauthorized(),r&&t({title:"Error",description:s,variant:"error"}),new DypaiError(s,i.status,void 0,e)}!r||"POST"!==e&&"PUT"!==e&&"PATCH"!==e&&"DELETE"!==e||t({title:"Éxito",description:"Operación completada",variant:"success"});const s=i.headers.get("content-type")||"";return s.includes("application/pdf")||s.includes("image/")||s.includes("audio/")||s.includes("video/")||s.includes("application/octet-stream")||s.includes("application/zip")||s.includes("application/vnd.openxmlformats-officedocument")?await i.blob():s.includes("application/json")?await i.json():await i.text()}finally{u&&pendingRequests.delete(u)}})();return u&&pendingRequests.set(u,w),w}async function handleSmartDownload(t,e,r,i){const s=i?.method||(r?"POST":"GET"),{data:o,error:n}=await("GET"===s?t.get(e,{params:i?.params}):t.post(e,r,{params:i?.params}));if(n)throw n;if(o instanceof Blob){const t=window.URL.createObjectURL(o),e=document.createElement("a");e.href=t,e.download=i?.fileName||"archivo-descargado",document.body.appendChild(e),e.click(),window.URL.revokeObjectURL(t),document.body.removeChild(e)}else if(o&&"object"==typeof o&&("url"in o||"signed_url"in o||"signedUrl"in o)){const t=o.url||o.signed_url||o.signedUrl;if("string"==typeof t){const e=new URL(t).searchParams.get("response-content-disposition");let r;if(e){const t=decodeURIComponent(e).match(/filename\*?=(?:UTF-8''|")?([^";]+)/i);t?.[1]&&(r=t[1].replace(/"/g,""))}const s=i?.fileName||r;if(s){const e=document.createElement("a");e.href=t,e.download=s,document.body.appendChild(e),e.click(),document.body.removeChild(e)}else window.open(t,"_blank")}}}async function handleSmartUpload(t,e,r,i){const s={...i?.params||{}};delete s.confirm,delete s.client_upload;const{data:o,error:n}=await t.post(e,{file_path:r.name,content_type:r.type||"application/octet-stream",size_bytes:r.size,...s,confirm:!1,client_upload:!0});if(n)throw n;const a=function(t){if(!t||"object"!=typeof t)return null;const e=t.upload_url||t.uploadUrl||t.url?t:null;if(e?.upload_url)return normalizeUploadPayload(e);const r=[t.data,t.result,t.output,t.payload].filter(Boolean);for(const t of r)if(t&&(t.upload_url||t.uploadUrl||t.url))return normalizeUploadPayload(t);const i=[t.steps_results,t.nodes_results].filter(Boolean);for(const t of i){const e=Array.isArray(t)?t:Object.values(t);for(const t of e)if(t&&(t.upload_url||t.uploadUrl||t.url))return normalizeUploadPayload(t)}return null}(o);if(!a?.upload_url)throw new DypaiError("The workflow did not return a valid upload URL (missing storage upload node?)",400);const{upload_url:h,method:l="PUT",headers:c={},file_path:u,storage_path:d}=a;i?.onProgress&&i.onProgress(10);const p=await fetch(h,{method:l,headers:{"Content-Type":r.type||"application/octet-stream",...c},body:r});if(!p.ok)throw new DypaiError("Direct upload to cloud storage failed",p.status);i?.onProgress&&i.onProgress(90);const f=i?.confirmEndpoint||e,{data:y,error:w}=await t.post(f,{...s,bucket:a.bucket||s.bucket,file_path:u,storage_path:d,filename:r.name,content_type:r.type||"application/octet-stream",size_bytes:r.size,confirm:!0,client_upload:!0});if(w)throw w;return i?.onProgress&&i.onProgress(100),y}function normalizeUploadPayload(t){return t&&"object"==typeof t?{...t,upload_url:t.upload_url||t.uploadUrl||t.url,storage_path:t.storage_path||t.storagePath}:null}function createInternalApiClient(t){const e={get:createMethod(t,"GET"),post:createMethod(t,"POST"),put:createMethod(t,"PUT"),patch:createMethod(t,"PATCH"),delete:createMethod(t,"DELETE"),upload:async(t,r,i)=>{try{return{data:await handleSmartUpload(e,t,r,i),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Upload failed",t?.status??500)}}},download:async(t,r,i)=>{try{return await handleSmartDownload(e,t,r,{...i,method:"POST"}),{data:void 0,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Download failed",t?.status??500)}}}};return e}function createMethod(t,e){const r="POST"===e||"PUT"===e||"PATCH"===e;return async(i,s,o)=>{const n=t();let a,h={};r?(a=s,h=o||{}):(h=s||{},a=void 0);try{validateApiOptionsShape(e,i,h);const t=h.token||n.token||(globalTokenProvider?globalTokenProvider():"")||"",r=h.apiKey||n.apiKey;return{data:await callApi(t,e,i,a,h.showToasts,h.params,r,n.baseUrl),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Error desconocido",t?.status??500)}}}}function createApiClient(t){const e=()=>"function"==typeof t?t():t,r={get:createMethodFromCtx(e,"GET"),post:createMethodFromCtx(e,"POST"),put:createMethodFromCtx(e,"PUT"),patch:createMethodFromCtx(e,"PATCH"),delete:createMethodFromCtx(e,"DELETE"),upload:async(t,e,i)=>{try{return{data:await handleSmartUpload(r,t,e,i),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Upload failed",t?.status??500)}}},download:async(t,e,i)=>{try{return await handleSmartDownload(r,t,e,{...i,method:"POST"}),{data:void 0,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Download failed",t?.status??500)}}}};return r}function createMethodFromCtx(t,e){return async(r,i,s)=>{try{const o=t(),n=await async function(t,e,r,i){let s,o={};"POST"===t||"PUT"===t||"PATCH"===t?(s=r,o=i||{}):(o=r||{},s=void 0),validateApiOptionsShape(t,e,o);let n=o.token;return!n&&globalTokenProvider&&(n=globalTokenProvider()||""),n=n||"",{token:n,apiKey:o.apiKey,body:s,params:o.params,showToasts:o.showToasts}}(e,r,i,s),a=n.token||o.token||"",h=n.apiKey||o.apiKey;return{data:await callApi(a,e,r,n.body,n.showToasts,n.params,h,o.baseUrl),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t?.message??"Error desconocido",t?.status??500)}}}}class DypaiClient{get baseUrl(){return this.Dt}getAuthToken(){return this.auth.token||this._t||null}constructor(t){const{baseUrl:e,apiKey:r}=t;this.Dt=e,this._t=r,this.realtime=new DypaiRealtimeClient(e,()=>this.getAuthToken());const i=t.auth?.storageKey||t.storageKey;t.global&&function(t){serviceConfig={...serviceConfig,...t}}(t.global),this.auth=new AuthModule({baseUrl:e,apiKey:r,storageKey:i,storage:t.auth?.storage,autoRefreshToken:t.auth?.autoRefreshToken,persistSession:t.auth?.persistSession,passwordRecoveryRedirect:t.redirects?.passwordRecovery},null);const s=createInternalApiClient(()=>({token:this.auth.token,apiKey:r,baseUrl:e}));if(this.auth.api=s,this.db=new DataModule(s),t.serviceRoleKey)if("undefined"!=typeof window&&void 0!==window.document)console.error("[DYPAI SDK] ❌ serviceRoleKey detected in a browser environment. This is a security risk — the key grants admin-level database access. db.direct has been disabled. Use serviceRoleKey only from server-side code (Node.js, Bun, Deno, scripts).");else{const r=createInternalApiClient(()=>({token:t.serviceRoleKey,baseUrl:e}));this.db.direct=new DirectDBModule(r)}this.users=new UsersModule(s),this.roles=new RolesModule(s),this.api=createApiClient(()=>({token:this.auth.token||"",apiKey:r,baseUrl:e})),setTokenProvider(()=>this.auth.token);const o="undefined"!=typeof window?window.location.search:"";if("undefined"!=typeof window&&t.redirects){const e=t.redirects;this.auth.T?.then(()=>{if(this.auth.isPasswordRecoveryCallback&&e.passwordRecovery){if(!window.location.pathname.includes(e.passwordRecovery)){const t=e.passwordRecovery+(o||"");window.location.replace(t)}}else if(this.auth.lastError&&e.authError)window.location.pathname.includes(e.authError)||window.location.replace(e.authError);else if(this.auth.token&&e.signIn){const t=this.auth.consumeCallbackType();t&&"recovery"!==t&&"invite"!==t&&(window.location.pathname.includes(e.signIn)||window.location.replace(e.signIn))}})}configureApiService({onUnauthorized:()=>this.auth.handleSessionExpired()})}from(t){return this.db.from(t)}async me(){return this.auth.getUser()}}let globalConfig={};function applyConfigToAllServices(){try{const{configureApiService:t}=require("../services/ApiService");t(globalConfig)}catch(t){}}exports.AuthEmailNotVerifiedError=AuthEmailNotVerifiedError,exports.AuthInvalidCredentialsError=AuthInvalidCredentialsError,exports.AuthInvalidTokenError=AuthInvalidTokenError,exports.AuthSessionMissingError=AuthSessionMissingError,exports.AuthUserAlreadyExistsError=AuthUserAlreadyExistsError,exports.AuthWeakPasswordError=AuthWeakPasswordError,exports.DirectDBModule=DirectDBModule,exports.DirectQueryBuilder=DirectQueryBuilder,exports.DypaiClient=DypaiClient,exports.DypaiError=DypaiError,exports.DypaiRealtimeClient=DypaiRealtimeClient,exports.PACKAGE_INFO={name:"@dypai-ai/client-sdk",version:"1.10.5",description:"Official JavaScript/TypeScript SDK for DYPAI",features:["Authentication (email, OAuth, OTP)","Database CRUD","Direct database access (service role)","Custom endpoints (typed API)","File upload/download (Smart Upload)","User management (admin)","React hooks (useAuth, useEndpoint, useAction, useUpload)"]},exports.RealtimeChannel=RealtimeChannel,exports.RetryableFetchError=RetryableFetchError,exports.callApi=callApi,exports.classifyAuthError=classifyAuthError,exports.configureApiService=configureApiService,exports.configureDypaiServices=function(t){globalConfig={...globalConfig,toast:toast,...t},applyConfigToAllServices()},exports.createApiClient=createApiClient,exports.createClient=function(t,e,r){if(!t)throw new Error("createClient() requiere la URL base");return new DypaiClient({baseUrl:t,apiKey:"string"==typeof e?e:void 0,..."string"==typeof e?r:e})},exports.getGlobalConfig=function(){return{...globalConfig}},exports.isAuthEmailNotVerifiedError=function(t){return t instanceof AuthEmailNotVerifiedError},exports.isAuthInvalidCredentialsError=function(t){return t instanceof AuthInvalidCredentialsError},exports.isAuthInvalidTokenError=isAuthInvalidTokenError,exports.isAuthSessionMissingError=function(t){return t instanceof AuthSessionMissingError},exports.isAuthUserAlreadyExistsError=function(t){return t instanceof AuthUserAlreadyExistsError},exports.isAuthWeakPasswordError=function(t){return t instanceof AuthWeakPasswordError},exports.isDypaiError=function(t){return t instanceof DypaiError},exports.isRetryableFetchError=function(t){return t instanceof RetryableFetchError},exports.isRetryableStatus=isRetryableStatus,exports.reloadDypaiConfig=async function(){applyConfigToAllServices()},exports.resetGlobalConfig=function(){globalConfig={},applyConfigToAllServices()},exports.setToastFunction=function(t){customToastFunction=t},exports.setTokenProvider=setTokenProvider,exports.toast=toast,exports.toastError=(t,e)=>toast({title:t,description:e,variant:"error"}),exports.toastInfo=(t,e)=>toast({title:t,description:e,variant:"info"}),exports.toastSuccess=(t,e)=>toast({title:t,description:e,variant:"success"}),exports.toastWarning=(t,e)=>toast({title:t,description:e,variant:"warning"}),exports.useToast=function(){const t=customToastFunction||fallbackToast;return{toast:t,toastSuccess:(e,r)=>t({title:e,description:r,variant:"success"}),toastError:(e,r)=>t({title:e,description:r,variant:"error"}),toastWarning:(e,r)=>t({title:e,description:r,variant:"warning"}),toastInfo:(e,r)=>t({title:e,description:r,variant:"info"})}};
|
package/dist/react/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as t,Fragment as e}from"react/jsx-runtime";import{createContext as n,useState as r,useEffect as a,useContext as o,useCallback as s,useRef as i}from"react";const c=n(null);function l({client:e,children:n}){const[o,s]=r(null),[i,l]=r(null),[u,d]=r(!0),[h,f]=r(null);a(()=>{const{data:{subscription:t}}=e.auth.onAuthStateChange((t,e)=>{f(t),"SIGNED_IN"===t||"TOKEN_REFRESHED"===t||"INITIAL_SESSION"===t?(l(e??null),s(e?.user??null),d(!1)):"SIGNED_OUT"===t?(l(null),s(null),d(!1)):"PASSWORD_RECOVERY"===t?(l(e??null),s(e?.user??null),d(!1)):"AUTH_ERROR"===t?d(!1):"USER_UPDATED"===t&&e&&(l(e),s(e.user??null))});return()=>{t?.unsubscribe()}},[e]);const p={client:e,user:o,session:i,isLoading:u,isAuthenticated:!!o,authEvent:h};return t(c.Provider,{value:p,children:n})}function u(){const t=o(c);if(!t)throw new Error('useDypai must be used within a <DypaiProvider>. Wrap your app with <DypaiProvider url="...">.');return t}function d(){return u().client}function h(){const{client:t,user:e,isLoading:n,isAuthenticated:r,authEvent:a}=u(),o=s(async(e,n)=>{const{error:r}=await t.auth.signInWithPassword({email:e,password:n});return{error:r}},[t]),i=s(async(e,n,r)=>{const{data:a,error:o}=await t.auth.signUp({email:e,password:n,...r});return{error:o,confirmationRequired:a?.confirmationRequired}},[t]),c=s(async()=>{await t.auth.signOut()},[t]),l=s(async(e,n)=>{const{error:r}=await t.auth.resetPasswordForEmail(e,n);return{error:r}},[t]),d=s(async e=>{const{error:n}=await t.auth.setPassword(e);return{error:n}},[t]),h=s(async e=>{await t.auth.signInWithOAuth(e)},[t]),f=s(async e=>{const{error:n}=await t.auth.signInWithOtp({email:e});return{error:n}},[t]),p=s(async e=>{const{error:n}=await t.auth.signInWithMagicLink(e);return{error:n}},[t]),w=s(async()=>{const{data:e,error:n}=await t.auth.enableTwoFactor();return{data:e,error:n}},[t]),y=s(async e=>{const{error:n}=await t.auth.verifyTwoFactor(e);return{error:n}},[t]),m=s(async()=>{const{error:e}=await t.auth.disableTwoFactor();return{error:e}},[t]),b=s(async(e,n)=>{const{data:r,error:a}=await t.auth.createOrganization(e,n);return{data:r,error:a}},[t]),T=s(async()=>{const{data:e,error:n}=await t.auth.listOrganizations();return{data:e,error:n}},[t]),E=s(async(e,n,r)=>{const{error:a}=await t.auth.inviteMember(e,n,r);return{error:a}},[t]);return{user:e,isLoading:n,isAuthenticated:r,authEvent:a,lastError:t.auth.lastError,isPasswordRecovery:t.auth.isPasswordRecoveryCallback||t.auth.hasPasswordRecoveryToken,signIn:o,signUp:i,signOut:c,resetPassword:l,setPassword:d,signInWithOAuth:h,signInWithOtp:f,signInWithMagicLink:p,enableTwoFactor:w,verifyTwoFactor:y,disableTwoFactor:m,createOrganization:b,listOrganizations:T,inviteMember:E}}function f(t,e={}){const n=d(),{params:o,enabled:c=!0,refetchInterval:l=0}=e,[u,h]=r(null),[f,p]=r(null),[w,y]=r(!0),[m,b]=r(!1),T=i(!0),E=JSON.stringify(o||{}),g=s(async(e=!1)=>{if(!c)return void y(!1);e?b(!0):y(!0),p(null);const{data:r,error:a}=await n.api.get(t,{params:o});T.current&&(a?(p(a),h(null)):h(r),y(!1),b(!1))},[n,t,E,c]);return a(()=>(T.current=!0,g(),()=>{T.current=!1}),[g]),a(()=>{if(!l||!c)return;const t=setInterval(()=>g(!0),l);return()=>clearInterval(t)},[g,l,c]),{data:u,error:f,isLoading:w,isRefetching:m,refetch:s(async()=>{await g(!0)},[g])}}function p(t,e={}){const n=d(),{method:a="POST",onSuccess:o,onError:i}=e,[c,l]=r(null),[u,h]=r(null),[f,p]=r(!1);return{mutate:s(async e=>{let r;switch(p(!0),h(null),a){case"PUT":r=await n.api.put(t,e);break;case"PATCH":r=await n.api.patch(t,e);break;case"DELETE":r=await n.api.delete(t,{params:e});break;default:r=await n.api.post(t,e)}return p(!1),r.error?(h(r.error),i?.(r.error),{data:null,error:r.error}):(l(r.data),o?.(r.data),{data:r.data,error:null})},[n,t,a,o,i]),data:c,error:u,isLoading:f,reset:s(()=>{l(null),h(null),p(!1)},[])}}function w(t,e={}){const n=d(),{onSuccess:a,onError:o}=e,[i,c]=r(0),[l,u]=r(!1),[h,f]=r(null),[p,w]=r(null);return{upload:s(async(e,r)=>{u(!0),c(0),f(null);const{data:s,error:i}=await n.api.upload(t,e,{params:r,onProgress:c});return u(!1),i?(f(i),o?.(i),{data:null,error:i}):(w(s),a?.(s),{data:s,error:null})},[n,t,a,o]),progress:i,isUploading:l,error:h,data:p,reset:s(()=>{c(0),u(!1),f(null),w(null)},[])}}function y(){return crypto.randomUUID?.()||`${Date.now()}-${Math.random().toString(36).slice(2)}`}function m(t){return t.parts?.length?t.parts.filter(t=>"text"===t.type).map(t=>t.text||"").join(""):t.content||""}function b(t){return t.parts?.length?t.parts.filter(t=>"tool-call"===t.type).map(t=>({toolName:t.toolName||"unknown",args:t.args})):[]}function T(){return y()}function E(t,e={}){const n=d(),[o,c]=r(e.id||T),[l,u]=r(e.initialMessages||[]),[h,f]=r("ready"),[p,w]=r(""),[y,m]=r(null),b=i(null);a(()=>{if(!1===e.loadHistory)return;if(!e.id)return;let t=!1;return(async()=>{try{const e=n.baseUrl,r=n.getAuthToken()||"",a=await fetch(`${e}/api/v0/chats/${o}/messages?limit=50`,{headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"}});if(!a.ok||t)return;const s=await a.json();if(s.messages&&!t){const t=s.messages.map(t=>({id:t.id,role:t.role,content:(t.parts||[]).filter(t=>"text"===t.type).map(t=>t.text).join(""),parts:t.parts,metadata:t.metadata,createdAt:new Date(t.created_at)}));u(t)}}catch{}})(),()=>{t=!0}},[o,e.id,e.loadHistory]);const E=s(async r=>{const a=r||p;if(!a.trim())return;r||w(""),m(null);const s={id:T(),role:"user",content:a,parts:[{type:"text",text:a}],createdAt:new Date},i=[...l,s];u(i),f("submitted");const c={id:T(),role:"assistant",content:"",parts:[],createdAt:new Date};try{const r=n.baseUrl,s=n.getAuthToken()||"",l=new AbortController;b.current=l;const d={session_id:o,messages:[{role:"user",content:a}],...e.body||{}},h=await fetch(`${r}/api/v0/${t}`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream",...e.headers||{}},body:JSON.stringify(d),signal:l.signal});if(!h.ok){const t=await h.json().catch(()=>({error:h.statusText}));throw new Error(t.error||`HTTP ${h.status}`)}if((h.headers.get("content-type")||"").includes("application/json")){const t=await h.json(),n=t.content||t.text||JSON.stringify(t);c.content=n,c.parts=[{type:"text",text:n}];const r=[...i,{...c}];return u(r),f("ready"),void e.onFinish?.(c,r)}f("streaming"),u([...i,c]);const p=h.body?.getReader();if(!p)throw new Error("No response body");const w=new TextDecoder;let y="";for(;;){const{done:t,value:e}=await p.read();if(t)break;const n=w.decode(e,{stream:!0}).split("\n").filter(t=>!/^[A-Z][a-zA-Z-]+:\s/.test(t)).join("\n");n&&(y+=n,c.content=y,c.parts=[{type:"text",text:y}],u([...i,{...c}]))}const m={...c,content:y,parts:[{type:"text",text:y}]},T=[...i,m];u(T),f("ready"),e.onFinish?.(m,T)}catch(t){if("AbortError"===t.name)return void f("ready");m(t),f("error"),e.onError?.(t)}finally{b.current=null}},[p,l,o,t,n,e]),g=s(()=>{b.current?.abort(),b.current=null,f("ready")},[]),S=s(()=>{g(),u([]),m(null),f("ready"),w(""),c(T())},[g]);return{messages:l,status:h,input:p,setInput:w,sendMessage:E,setMessages:u,stop:g,newChat:S,isLoading:"submitted"===h||"streaming"===h,error:y,chatId:o}}function g(t={}){const e=d(),{endpointId:n,limit:o=20,enabled:i=!0}=t,[c,l]=r([]),[u,h]=r(!1),[f,p]=r(null),w=s(async()=>{h(!0),p(null);try{const t=e.baseUrl,r=e.getAuthToken()||"",a=new URLSearchParams({limit:String(o)});n&&a.set("endpoint_id",n);const s=await fetch(`${t}/api/v0/chats?${a}`,{headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"}});if(!s.ok){const t=await s.json().catch(()=>({}));throw new Error(t.error||`HTTP ${s.status}`)}const i=((await s.json()).chats||[]).map(t=>({id:t.id,title:t.title,visibility:t.visibility,messageCount:parseInt(t.message_count||"0"),createdAt:t.created_at,updatedAt:t.updated_at}));l(i)}catch(t){p(t)}finally{h(!1)}},[e,n,o]),y=s(async t=>{const n=e.baseUrl,r=e.getAuthToken()||"",a=await fetch(`${n}/api/v0/chats/${t}`,{method:"DELETE",headers:{Authorization:`Bearer ${r}`}});if(!a.ok){const t=await a.json().catch(()=>({}));throw new Error(t.error||`HTTP ${a.status}`)}l(e=>e.filter(e=>e.id!==t))},[e]),m=s(async(t,n)=>{const r=e.baseUrl,a=e.getAuthToken()||"",o=await fetch(`${r}/api/v0/chats/${t}`,{method:"PATCH",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify({title:n})});if(!o.ok){const t=await o.json().catch(()=>({}));throw new Error(t.error||`HTTP ${o.status}`)}l(e=>e.map(e=>e.id===t?{...e,title:n}:e))},[e]);return a(()=>{i&&w()},[i,w]),{chats:c,isLoading:u,error:f,refetch:w,deleteChat:y,renameChat:m}}let S=null,v="disconnected",$=null,A=null,O=0,I="",P="";const C=[1e3,2e3,5e3,1e4,3e4],N=new Set,D=[],L=[];function R(t,e){if(S&&(S.readyState===WebSocket.OPEN||S.readyState===WebSocket.CONNECTING))return S;I=t,P=e;const n=t.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(e)}`,r=new WebSocket(n);return S=r,v="connecting",r.onopen=()=>{v="connected",O=0;for(const t of D)r.send(JSON.stringify({type:"subscribe",...t}));for(const t of L)r.send(JSON.stringify({type:"join",...t}));A&&clearInterval(A),A=setInterval(()=>{r.readyState===WebSocket.OPEN&&r.send(JSON.stringify({type:"heartbeat"}))},25e3)},r.onmessage=t=>{try{const e=JSON.parse("string"==typeof t.data?t.data:"");for(const t of N)t(e)}catch{}},r.onclose=()=>{if(v="disconnected",S=null,A&&clearInterval(A),N.size>0&&!$){const t=C[Math.min(O,C.length-1)];O++,$=setTimeout(()=>{$=null,N.size>0&&I&&P&&R(I,P)},t)}},r.onerror=()=>{v="error"},r}function k(t){return N.add(t),()=>{N.delete(t),0===N.size&&S&&(S.close(),S=null)}}function j(t){return!(!S||S.readyState!==WebSocket.OPEN||(S.send(JSON.stringify(t)),0))}function x(t,e={}){const n=d(),{event:o="*",filter:s,enabled:c=!0}=e,[l,u]=r([]),[h,f]=r("disconnected"),[p,w]=r(null),y=i(e);return y.current=e,a(()=>{if(!c)return;const e=n.baseUrl,r=n.getAuthToken()||"";if(!r)return void w(new Error("Authentication required for realtime"));const a=R(e,r);f(v),function(t){const e=D.findIndex(e=>e.table===t.table);e>=0&&D.splice(e,1),D.push(t)}({table:t,event:o,filter:s});const i=()=>{a.readyState===WebSocket.OPEN&&(a.send(JSON.stringify({type:"subscribe",table:t,event:o,filter:s})),f("connected"))};a.readyState===WebSocket.OPEN?i():a.addEventListener("open",i,{once:!0});const l=k(e=>{if("connected"!==e.type&&"subscribe"!==e.type||f("connected"),"change"!==e.type||e.table!==t)return;if("*"!==o&&e.event!==o)return;const n={table:e.table,event:e.event,record:e.record??null,old_record:e.old_record??null,timestamp:e.timestamp,truncated:e.truncated};u(t=>[...t.slice(-99),n]),y.current.onChange?.(n),e.truncated?("INSERT"===e.event&&y.current.onInsert?.(null),"UPDATE"===e.event&&y.current.onUpdate?.(null,null),"DELETE"===e.event&&y.current.onDelete?.(null)):("INSERT"===e.event&&e.record&&y.current.onInsert?.(e.record),"UPDATE"===e.event&&e.record&&y.current.onUpdate?.(e.record,e.old_record),"DELETE"===e.event&&e.old_record&&y.current.onDelete?.(e.old_record))});return()=>{l(),function(t){const e=D.findIndex(e=>e.table===t);e>=0&&D.splice(e,1)}(t),j({type:"unsubscribe",table:t})}},[t,o,c,n]),{changes:l,isConnected:"connected"===h,status:h,error:p}}function W(t,e={}){const n=d(),{presence:o,enabled:c=!0}=e,[l,u]=r({}),[h,f]=r(!1),p=i(e);return p.current=e,a(()=>{if(!c||!t)return;const e=n.baseUrl,r=n.getAuthToken()||"";if(!r)return;const a=R(e,r);!function(t){const e=L.findIndex(e=>e.channel===t.channel);e>=0&&L.splice(e,1),L.push(t)}({channel:t,presence:o||void 0});const s=()=>{a.readyState===WebSocket.OPEN&&a.send(JSON.stringify({type:"join",channel:t,presence:o||void 0}))};a.readyState===WebSocket.OPEN?s():a.addEventListener("open",s,{once:!0});const i=k(e=>{"connected"!==e.type&&"join"!==e.type||f(!0),"broadcast"===e.type&&e.channel===t&&p.current.onBroadcast?.({event:e.event,payload:e.payload,sender:e.sender,timestamp:e.timestamp}),"presence_state"===e.type&&e.channel===t&&u(e.presences||{}),"presence_diff"===e.type&&e.channel===t&&u(t=>{const n={...t};if(e.joins)for(const[t,r]of Object.entries(e.joins))n[t]=r,p.current.onJoin?.(t,r);if(e.leaves)for(const[t,r]of Object.entries(e.leaves))delete n[t],p.current.onLeave?.(t,r);return n})});return()=>{i(),function(t){const e=L.findIndex(e=>e.channel===t);e>=0&&L.splice(e,1)}(t),j({type:"leave",channel:t}),f(!1)}},[t,c,n]),{broadcast:s((e,n)=>{j({type:"broadcast",channel:t,event:e,payload:n})},[t]),presences:l,isConnected:h,memberCount:Object.keys(l).length}}function _(t,e={}){const n=d(),{loadHistory:o=!0,limit:c=50}=e,[l,u]=r([]),[h,f]=r(!1),[p,w]=r(null),y=i(new Set);a(()=>{if(!o||!t)return;let e=!1;return(async()=>{f(!0);try{const r=n.baseUrl,a=n.getAuthToken()||"",o=await fetch(`${r}/api/v0/channels/${t}/messages?limit=${c}`,{headers:{Authorization:`Bearer ${a}`}});if(!o.ok)throw new Error(`HTTP ${o.status}`);const s=await o.json();if(!e&&s.messages){const t=s.messages;t.forEach(t=>y.current.add(t.id)),u(t)}}catch(t){e||w(t)}finally{e||f(!1)}})(),()=>{e=!0}},[t,o,c,n]);const{isConnected:m}=x("channel_messages",{event:"INSERT",filter:{channel_id:t},onInsert:t=>{t&&(y.current.has(t.id)||(y.current.add(t.id),u(e=>[...e,t])))}});return{messages:l,send:s(async(e,r)=>{try{const a=n.baseUrl,o=n.getAuthToken()||"",s=await fetch(`${a}/api/v0/channels/${t}/messages`,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify({content:e,metadata:r})});if(!s.ok){const t=await s.json().catch(()=>({}));throw new Error(t.error||`HTTP ${s.status}`)}const i=await s.json();i.message&&(y.current.add(i.message.id),u(t=>[...t,i.message]))}catch(t){throw w(t),t}},[t,n]),isLoading:h,isConnected:m,error:p}}function z(){const t=d(),[e,n]=r([]),[o,i]=r(!1),[c,l]=r(null),u=s(async()=>{i(!0);try{const e=t.baseUrl,r=t.getAuthToken()||"",a=await fetch(`${e}/api/v0/channels`,{headers:{Authorization:`Bearer ${r}`}});if(!a.ok)throw new Error(`HTTP ${a.status}`);const o=await a.json();n(o.channels||[])}catch(t){l(t)}finally{i(!1)}},[t]);return a(()=>{u()},[u]),{channels:e,isLoading:o,error:c,createChannel:s(async(e,r=[],a="group")=>{const o=t.baseUrl,s=t.getAuthToken()||"",i=await fetch(`${o}/api/v0/channels`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({name:e,type:a,members:r})});if(!i.ok){const t=await i.json().catch(()=>({}));throw new Error(t.error||`HTTP ${i.status}`)}const c=await i.json();return n(t=>[c,...t]),c},[t]),refetch:u}}function H({children:n,redirectTo:r,roles:a,loadingComponent:o,unauthorizedComponent:s,unauthenticatedComponent:i}){const{user:c,isLoading:l,isAuthenticated:d}=u();if(l)return t(e,{children:o||null});if(!d)return r&&"undefined"!=typeof window?(window.location.replace(r),null):t(e,{children:i||null});if(a&&a.length>0){const n=c?.role;if(!n||!a.includes(n))return t(e,{children:s||null})}return t(e,{children:n})}export{l as DypaiProvider,H as ProtectedRoute,y as generateChatId,m as getMessageText,b as getToolCalls,p as useAction,h as useAuth,W as useChannel,_ as useChannelMessages,z as useChannels,E as useChat,g as useChatList,u as useDypai,d as useDypaiClient,f as useEndpoint,x as useRealtime,w as useUpload};
|
|
1
|
+
import{jsx as t,Fragment as e}from"react/jsx-runtime";import{createContext as n,useState as r,useEffect as o,useContext as a,useCallback as s,useRef as i}from"react";const c=n(null);function l({client:e,children:n}){const[a,s]=r(null),[i,l]=r(null),[u,d]=r(!0),[f,p]=r(null);o(()=>{const{data:{subscription:t}}=e.auth.onAuthStateChange((t,e)=>{p(t),"SIGNED_IN"===t||"TOKEN_REFRESHED"===t||"INITIAL_SESSION"===t?(l(e??null),s(e?.user??null),d(!1)):"SIGNED_OUT"===t?(l(null),s(null),d(!1)):"PASSWORD_RECOVERY"===t?(l(e??null),s(e?.user??null),d(!1)):"AUTH_ERROR"===t?d(!1):"USER_UPDATED"===t&&e&&(l(e),s(e.user??null))});return()=>{t?.unsubscribe()}},[e]);const h={client:e,user:a,session:i,isLoading:u,isAuthenticated:!!a,authEvent:f};return t(c.Provider,{value:h,children:n})}function u(){const t=a(c);if(!t)throw new Error('useDypai must be used within a <DypaiProvider>. Wrap your app with <DypaiProvider url="...">.');return t}function d(){return u().client}function f(){const{client:t,user:e,isLoading:n,isAuthenticated:r,authEvent:o}=u(),a=s(async(e,n)=>{const{error:r}=await t.auth.signInWithPassword({email:e,password:n});return{error:r}},[t]),i=s(async(e,n,r)=>{const{data:o,error:a}=await t.auth.signUp({email:e,password:n,...r});return{error:a,confirmationRequired:o?.confirmationRequired}},[t]),c=s(async()=>{await t.auth.signOut()},[t]),l=s(async(e,n)=>{const{error:r}=await t.auth.resetPasswordForEmail(e,n);return{error:r}},[t]),d=s(async e=>{const{error:n}=await t.auth.setPassword(e);return{error:n}},[t]),f=s(async e=>{await t.auth.signInWithOAuth(e)},[t]),p=s(async e=>{const{error:n}=await t.auth.signInWithOtp({email:e});return{error:n}},[t]),h=s(async e=>{const{error:n}=await t.auth.signInWithMagicLink(e);return{error:n}},[t]),y=s(async()=>{const{data:e,error:n}=await t.auth.enableTwoFactor();return{data:e,error:n}},[t]),w=s(async e=>{const{error:n}=await t.auth.verifyTwoFactor(e);return{error:n}},[t]),m=s(async()=>{const{error:e}=await t.auth.disableTwoFactor();return{error:e}},[t]),g=s(async(e,n)=>{const{data:r,error:o}=await t.auth.createOrganization(e,n);return{data:r,error:o}},[t]),b=s(async()=>{const{data:e,error:n}=await t.auth.listOrganizations();return{data:e,error:n}},[t]),v=s(async(e,n,r)=>{const{error:o}=await t.auth.inviteMember(e,n,r);return{error:o}},[t]);return{user:e,isLoading:n,isAuthenticated:r,authEvent:o,lastError:t.auth.lastError,isPasswordRecovery:t.auth.isPasswordRecoveryCallback||t.auth.hasPasswordRecoveryToken,signIn:a,signUp:i,signOut:c,resetPassword:l,setPassword:d,signInWithOAuth:f,signInWithOtp:p,signInWithMagicLink:h,enableTwoFactor:y,verifyTwoFactor:w,disableTwoFactor:m,createOrganization:g,listOrganizations:b,inviteMember:v}}function p(t,e={}){const n=d(),{params:a,enabled:c=!0,refetchInterval:l=0}=e,[u,f]=r(null),[p,h]=r(null),[y,w]=r(!0),[m,g]=r(!1),b=i(!0),v=JSON.stringify(a||{}),S=s(async(e=!1)=>{if(!c)return void w(!1);e?g(!0):w(!0),h(null);const{data:r,error:o}=await n.api.get(t,{params:a});b.current&&(o?(h(o),f(null)):f(r),w(!1),g(!1))},[n,t,v,c]);return o(()=>(b.current=!0,S(),()=>{b.current=!1}),[S]),o(()=>{if(!l||!c)return;const t=setInterval(()=>S(!0),l);return()=>clearInterval(t)},[S,l,c]),{data:u,error:p,isLoading:y,isRefetching:m,refetch:s(async()=>{await S(!0)},[S])}}function h(t,e={}){const n=d(),{method:o="POST",onSuccess:a,onError:i}=e,[c,l]=r(null),[u,f]=r(null),[p,h]=r(!1);return{mutate:s(async e=>{let r;switch(h(!0),f(null),o){case"PUT":r=await n.api.put(t,e);break;case"PATCH":r=await n.api.patch(t,e);break;case"DELETE":r=await n.api.delete(t,{params:e});break;default:r=await n.api.post(t,e)}return h(!1),r.error?(f(r.error),i?.(r.error),{data:null,error:r.error}):(l(r.data),a?.(r.data),{data:r.data,error:null})},[n,t,o,a,i]),data:c,error:u,isLoading:p,reset:s(()=>{l(null),f(null),h(!1)},[])}}function y(t,e={}){const n=d(),{onSuccess:o,onError:a}=e,[i,c]=r(0),[l,u]=r(!1),[f,p]=r(null),[h,y]=r(null);return{upload:s(async(e,r)=>{u(!0),c(0),p(null);const{data:s,error:i}=await n.api.upload(t,e,{params:r,onProgress:c});return u(!1),i?(p(i),a?.(i),{data:null,error:i}):(y(s),o?.(s),{data:s,error:null})},[n,t,o,a]),progress:i,isUploading:l,error:f,data:h,reset:s(()=>{c(0),u(!1),p(null),y(null)},[])}}function w(){return crypto.randomUUID?.()||`${Date.now()}-${Math.random().toString(36).slice(2)}`}function m(t){return t.parts?.length?t.parts.filter(t=>"text"===t.type).map(t=>t.text||"").join(""):t.content||""}function g(t){return t.parts?.length?t.parts.filter(t=>"tool-call"===t.type).map(t=>({toolName:t.toolName||"unknown",args:t.args})):[]}function b(){return w()}function v(t){const e=t.trim();if(!e||"[DONE]"===e)return null;if(/^[a-z0-9]:/.test(e)){const t=e[0],n=e.slice(2);if("0"===t){const t=JSON.parse(n);return"string"==typeof t?{type:"text-delta",delta:t}:null}if("9"===t){const t=JSON.parse(n);return{type:"tool-call",toolName:String(t?.toolName||t?.function?.name||"tool"),args:t?.args??t?.arguments??t?.input??{}}}if("e"===t){const t=JSON.parse(n);throw new Error(t?.errorText||t?.message||"AI stream error")}return null}const n=JSON.parse(e);switch(n?.type){case"text-delta":return"string"==typeof n.delta?{type:"text-delta",delta:n.delta}:null;case"text":return"string"==typeof n.text?{type:"text-delta",delta:n.text}:null;case"tool-input-available":return{type:"tool-call",toolName:String(n.toolName||"tool"),args:n.input??{}};case"tool-output-available":return{type:"tool-result",toolName:String(n.toolName||n.toolCallId||"tool"),result:n.output};case"error":throw new Error(n.errorText||n.message||"AI stream error");default:return null}}function S(t){const e=t.split(/\r?\n/).filter(t=>t.startsWith("data:")).map(t=>t.slice(5).trimStart());return e.length>0?[e.join("\n")]:[]}function E(t){const e=t.split(/\r?\n/),n=e.pop()||"";return{payloads:e.map(t=>t.trim()).filter(Boolean),rest:n}}function T(t,e={}){const n=d(),[a,c]=r(e.id||b),[l,u]=r(e.initialMessages||[]),[f,p]=r("ready"),[h,y]=r(""),[w,m]=r(null),g=i(null);o(()=>{if(!1===e.loadHistory)return;if(!e.id)return;let t=!1;return(async()=>{try{const e=n.baseUrl,r=n.getAuthToken()||"",o=await fetch(`${e}/api/v0/chats/${a}/messages?limit=50`,{headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"}});if(!o.ok||t)return;const s=await o.json();if(s.messages&&!t){const t=s.messages.map(t=>({id:t.id,role:t.role,content:(t.parts||[]).filter(t=>"text"===t.type).map(t=>t.text).join(""),parts:t.parts,metadata:t.metadata,createdAt:new Date(t.created_at)}));u(t)}}catch{}})(),()=>{t=!0}},[a,e.id,e.loadHistory]);const T=s(async r=>{const o=r||h;if(!o.trim())return;r||y(""),m(null);const s={id:b(),role:"user",content:o,parts:[{type:"text",text:o}],createdAt:new Date},i=[...l,s];u(i),p("submitted");const c={id:b(),role:"assistant",content:"",parts:[],createdAt:new Date};try{const r=n.baseUrl,s=n.getAuthToken()||"",l=new AbortController;g.current=l;const f={session_id:a,messages:[{role:"user",content:o}],...e.body||{}},h=await fetch(`${r}/api/v0/${t}`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream",...e.headers||{}},body:JSON.stringify(f),signal:l.signal});if(!h.ok){const t=await h.json().catch(()=>({error:h.statusText}));throw new Error(t.error||`HTTP ${h.status}`)}if((h.headers.get("content-type")||"").includes("application/json")){const t=function(t){if("string"==typeof t?.content)return t.content;if("string"==typeof t?.text)return t.text;const e=Array.isArray(t?.messages)?t.messages:[],n=e[e.length-1],r=n?.content;if("string"==typeof r)return r;if(Array.isArray(r)){const t=r.filter(t=>"text"===t?.type).map(t=>t.text||"").join("");if(t)return t}return JSON.stringify(t)}(await h.json());c.content=t,c.parts=[{type:"text",text:t}];const n=[...i,{...c}];return u(n),p("ready"),void e.onFinish?.(c,n)}p("streaming"),u([...i,c]);const y=h.body?.getReader();if(!y)throw new Error("No response body");const w=new TextDecoder;let m="",b="";const T=[],A="v1"===(d=h.headers).get("x-vercel-ai-ui-message-stream")?"ui-message":"v1"===d.get("x-vercel-ai-data-stream")?"legacy-data":null,$=()=>{c.content=m,c.parts=[...m?[{type:"text",text:m}]:[],...T],u([...i,{...c}])},O=t=>{if(t)return"text-delta"===t.type?(m+=t.delta,void $()):"tool-call"===t.type?(T.push({type:"tool-call",toolName:t.toolName,args:t.args}),e.onToolCall?.({toolName:t.toolName,args:t.args}),void $()):void("tool-result"===t.type&&(T.push({type:"tool-result",toolName:t.toolName,result:t.result}),$()))};for(;;){const{done:t,value:e}=await y.read();if(t)break;const n=w.decode(e,{stream:!0});if("ui-message"===A){b+=n;const t=b.split(/\r?\n\r?\n/);b=t.pop()||"";for(const e of t)for(const t of S(e))O(v(t));continue}if("legacy-data"===A){b+=n;const{payloads:t,rest:e}=E(b);b=e;for(const e of t)O(v(e));continue}const r=n.split("\n").filter(t=>!/^[A-Z][a-zA-Z-]+:\s/.test(t)).join("\n");r&&(m+=r,$())}if("ui-message"===A&&b.trim())for(const t of S(b))O(v(t));else if("legacy-data"===A&&b.trim())for(const t of[b.trim()])O(v(t));const N={...c,content:m,parts:c.parts?.length?c.parts:[{type:"text",text:m}]},I=[...i,N];u(I),p("ready"),e.onFinish?.(N,I)}catch(t){if("AbortError"===t.name)return void p("ready");m(t),p("error"),e.onError?.(t)}finally{g.current=null}var d},[h,l,a,t,n,e]),A=s(()=>{g.current?.abort(),g.current=null,p("ready")},[]),$=s(()=>{A(),u([]),m(null),p("ready"),y(""),c(b())},[A]);return{messages:l,status:f,input:h,setInput:y,sendMessage:T,setMessages:u,stop:A,newChat:$,isLoading:"submitted"===f||"streaming"===f,error:w,chatId:a}}function A(t={}){const e=d(),{endpointId:n,limit:a=20,enabled:i=!0}=t,[c,l]=r([]),[u,f]=r(!1),[p,h]=r(null),y=s(async()=>{f(!0),h(null);try{const t=e.baseUrl,r=e.getAuthToken()||"",o=new URLSearchParams({limit:String(a)});n&&o.set("endpoint_id",n);const s=await fetch(`${t}/api/v0/chats?${o}`,{headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"}});if(!s.ok){const t=await s.json().catch(()=>({}));throw new Error(t.error||`HTTP ${s.status}`)}const i=((await s.json()).chats||[]).map(t=>({id:t.id,title:t.title,visibility:t.visibility,messageCount:parseInt(t.message_count||"0"),createdAt:t.created_at,updatedAt:t.updated_at}));l(i)}catch(t){h(t)}finally{f(!1)}},[e,n,a]),w=s(async t=>{const n=e.baseUrl,r=e.getAuthToken()||"",o=await fetch(`${n}/api/v0/chats/${t}`,{method:"DELETE",headers:{Authorization:`Bearer ${r}`}});if(!o.ok){const t=await o.json().catch(()=>({}));throw new Error(t.error||`HTTP ${o.status}`)}l(e=>e.filter(e=>e.id!==t))},[e]),m=s(async(t,n)=>{const r=e.baseUrl,o=e.getAuthToken()||"",a=await fetch(`${r}/api/v0/chats/${t}`,{method:"PATCH",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify({title:n})});if(!a.ok){const t=await a.json().catch(()=>({}));throw new Error(t.error||`HTTP ${a.status}`)}l(e=>e.map(e=>e.id===t?{...e,title:n}:e))},[e]);return o(()=>{i&&y()},[i,y]),{chats:c,isLoading:u,error:p,refetch:y,deleteChat:w,renameChat:m}}let $=null,O="disconnected",N=null,I=null,P=0,C="",x="";const D=[1e3,2e3,5e3,1e4,3e4],J=new Set,L=[],R=[];function k(t,e){if($&&($.readyState===WebSocket.OPEN||$.readyState===WebSocket.CONNECTING))return $;C=t,x=e;const n=t.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(e)}`,r=new WebSocket(n);return $=r,O="connecting",r.onopen=()=>{O="connected",P=0;for(const t of L)r.send(JSON.stringify({type:"subscribe",...t}));for(const t of R)r.send(JSON.stringify({type:"join",...t}));I&&clearInterval(I),I=setInterval(()=>{r.readyState===WebSocket.OPEN&&r.send(JSON.stringify({type:"heartbeat"}))},25e3)},r.onmessage=t=>{try{const e=JSON.parse("string"==typeof t.data?t.data:"");for(const t of J)t(e)}catch{}},r.onclose=()=>{if(O="disconnected",$=null,I&&clearInterval(I),J.size>0&&!N){const t=D[Math.min(P,D.length-1)];P++,N=setTimeout(()=>{N=null,J.size>0&&C&&x&&k(C,x)},t)}},r.onerror=()=>{O="error"},r}function j(t){return J.add(t),()=>{J.delete(t),0===J.size&&$&&($.close(),$=null)}}function z(t){return!(!$||$.readyState!==WebSocket.OPEN||($.send(JSON.stringify(t)),0))}function W(t,e={}){const n=d(),{event:a="*",filter:s,enabled:c=!0}=e,[l,u]=r([]),[f,p]=r("disconnected"),[h,y]=r(null),w=i(e);return w.current=e,o(()=>{if(!c)return;const e=n.baseUrl,r=n.getAuthToken()||"";if(!r)return void y(new Error("Authentication required for realtime"));const o=k(e,r);p(O),function(t){const e=L.findIndex(e=>e.table===t.table);e>=0&&L.splice(e,1),L.push(t)}({table:t,event:a,filter:s});const i=()=>{o.readyState===WebSocket.OPEN&&(o.send(JSON.stringify({type:"subscribe",table:t,event:a,filter:s})),p("connected"))};o.readyState===WebSocket.OPEN?i():o.addEventListener("open",i,{once:!0});const l=j(e=>{if("connected"!==e.type&&"subscribe"!==e.type||p("connected"),"change"!==e.type||e.table!==t)return;if("*"!==a&&e.event!==a)return;const n={table:e.table,event:e.event,record:e.record??null,old_record:e.old_record??null,timestamp:e.timestamp,truncated:e.truncated};u(t=>[...t.slice(-99),n]),w.current.onChange?.(n),e.truncated?("INSERT"===e.event&&w.current.onInsert?.(null),"UPDATE"===e.event&&w.current.onUpdate?.(null,null),"DELETE"===e.event&&w.current.onDelete?.(null)):("INSERT"===e.event&&e.record&&w.current.onInsert?.(e.record),"UPDATE"===e.event&&e.record&&w.current.onUpdate?.(e.record,e.old_record),"DELETE"===e.event&&e.old_record&&w.current.onDelete?.(e.old_record))});return()=>{l(),function(t){const e=L.findIndex(e=>e.table===t);e>=0&&L.splice(e,1)}(t),z({type:"unsubscribe",table:t})}},[t,a,c,n]),{changes:l,isConnected:"connected"===f,status:f,error:h}}function _(t,e={}){const n=d(),{presence:a,enabled:c=!0}=e,[l,u]=r({}),[f,p]=r(!1),h=i(e);return h.current=e,o(()=>{if(!c||!t)return;const e=n.baseUrl,r=n.getAuthToken()||"";if(!r)return;const o=k(e,r);!function(t){const e=R.findIndex(e=>e.channel===t.channel);e>=0&&R.splice(e,1),R.push(t)}({channel:t,presence:a||void 0});const s=()=>{o.readyState===WebSocket.OPEN&&o.send(JSON.stringify({type:"join",channel:t,presence:a||void 0}))};o.readyState===WebSocket.OPEN?s():o.addEventListener("open",s,{once:!0});const i=j(e=>{"connected"!==e.type&&"join"!==e.type||p(!0),"broadcast"===e.type&&e.channel===t&&h.current.onBroadcast?.({event:e.event,payload:e.payload,sender:e.sender,timestamp:e.timestamp}),"presence_state"===e.type&&e.channel===t&&u(e.presences||{}),"presence_diff"===e.type&&e.channel===t&&u(t=>{const n={...t};if(e.joins)for(const[t,r]of Object.entries(e.joins))n[t]=r,h.current.onJoin?.(t,r);if(e.leaves)for(const[t,r]of Object.entries(e.leaves))delete n[t],h.current.onLeave?.(t,r);return n})});return()=>{i(),function(t){const e=R.findIndex(e=>e.channel===t);e>=0&&R.splice(e,1)}(t),z({type:"leave",channel:t}),p(!1)}},[t,c,n]),{broadcast:s((e,n)=>{z({type:"broadcast",channel:t,event:e,payload:n})},[t]),presences:l,isConnected:f,memberCount:Object.keys(l).length}}function H(t,e={}){const n=d(),{loadHistory:a=!0,limit:c=50}=e,[l,u]=r([]),[f,p]=r(!1),[h,y]=r(null),w=i(new Set);o(()=>{if(!a||!t)return;let e=!1;return(async()=>{p(!0);try{const r=n.baseUrl,o=n.getAuthToken()||"",a=await fetch(`${r}/api/v0/channels/${t}/messages?limit=${c}`,{headers:{Authorization:`Bearer ${o}`}});if(!a.ok)throw new Error(`HTTP ${a.status}`);const s=await a.json();if(!e&&s.messages){const t=s.messages;t.forEach(t=>w.current.add(t.id)),u(t)}}catch(t){e||y(t)}finally{e||p(!1)}})(),()=>{e=!0}},[t,a,c,n]);const{isConnected:m}=W("channel_messages",{event:"INSERT",filter:{channel_id:t},onInsert:t=>{t&&(w.current.has(t.id)||(w.current.add(t.id),u(e=>[...e,t])))}});return{messages:l,send:s(async(e,r)=>{try{const o=n.baseUrl,a=n.getAuthToken()||"",s=await fetch(`${o}/api/v0/channels/${t}/messages`,{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify({content:e,metadata:r})});if(!s.ok){const t=await s.json().catch(()=>({}));throw new Error(t.error||`HTTP ${s.status}`)}const i=await s.json();i.message&&(w.current.add(i.message.id),u(t=>[...t,i.message]))}catch(t){throw y(t),t}},[t,n]),isLoading:f,isConnected:m,error:h}}function U(){const t=d(),[e,n]=r([]),[a,i]=r(!1),[c,l]=r(null),u=s(async()=>{i(!0);try{const e=t.baseUrl,r=t.getAuthToken()||"",o=await fetch(`${e}/api/v0/channels`,{headers:{Authorization:`Bearer ${r}`}});if(!o.ok)throw new Error(`HTTP ${o.status}`);const a=await o.json();n(a.channels||[])}catch(t){l(t)}finally{i(!1)}},[t]);return o(()=>{u()},[u]),{channels:e,isLoading:a,error:c,createChannel:s(async(e,r=[],o="group")=>{const a=t.baseUrl,s=t.getAuthToken()||"",i=await fetch(`${a}/api/v0/channels`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({name:e,type:o,members:r})});if(!i.ok){const t=await i.json().catch(()=>({}));throw new Error(t.error||`HTTP ${i.status}`)}const c=await i.json();return n(t=>[c,...t]),c},[t]),refetch:u}}function B({children:n,redirectTo:r,roles:o,loadingComponent:a,unauthorizedComponent:s,unauthenticatedComponent:i}){const{user:c,isLoading:l,isAuthenticated:d}=u();if(l)return t(e,{children:a||null});if(!d)return r&&"undefined"!=typeof window?(window.location.replace(r),null):t(e,{children:i||null});if(o&&o.length>0){const n=c?.role;if(!n||!o.includes(n))return t(e,{children:s||null})}return t(e,{children:n})}export{l as DypaiProvider,B as ProtectedRoute,w as generateChatId,m as getMessageText,g as getToolCalls,h as useAction,f as useAuth,_ as useChannel,H as useChannelMessages,U as useChannels,T as useChat,A as useChatList,u as useDypai,d as useDypaiClient,p as useEndpoint,W as useRealtime,y as useUpload};
|
package/dist/react/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var jsxRuntime=require("react/jsx-runtime"),react=require("react");const DypaiContext=react.createContext(null);function useDypai(){const e=react.useContext(DypaiContext);if(!e)throw new Error('useDypai must be used within a <DypaiProvider>. Wrap your app with <DypaiProvider url="...">.');return e}function useDypaiClient(){return useDypai().client}function generateChatId(){return crypto.randomUUID?.()||`${Date.now()}-${Math.random().toString(36).slice(2)}`}function generateId(){return generateChatId()}let sharedWs=null,sharedWsStatus="disconnected",reconnectTimer=null,heartbeatTimer=null,reconnectAttempt=0,currentBaseUrl="",currentToken="";const RECONNECT_INTERVALS=[1e3,2e3,5e3,1e4,3e4],messageHandlers=new Set,pendingSubscriptions=[],pendingChannelJoins=[];function getOrCreateSharedWs(e,t){if(sharedWs&&(sharedWs.readyState===WebSocket.OPEN||sharedWs.readyState===WebSocket.CONNECTING))return sharedWs;currentBaseUrl=e,currentToken=t;const r=e.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(t)}`,n=new WebSocket(r);return sharedWs=n,sharedWsStatus="connecting",n.onopen=()=>{sharedWsStatus="connected",reconnectAttempt=0;for(const e of pendingSubscriptions)n.send(JSON.stringify({type:"subscribe",...e}));for(const e of pendingChannelJoins)n.send(JSON.stringify({type:"join",...e}));heartbeatTimer&&clearInterval(heartbeatTimer),heartbeatTimer=setInterval(()=>{n.readyState===WebSocket.OPEN&&n.send(JSON.stringify({type:"heartbeat"}))},25e3)},n.onmessage=e=>{try{const t=JSON.parse("string"==typeof e.data?e.data:"");for(const e of messageHandlers)e(t)}catch{}},n.onclose=()=>{if(sharedWsStatus="disconnected",sharedWs=null,heartbeatTimer&&clearInterval(heartbeatTimer),messageHandlers.size>0&&!reconnectTimer){const e=RECONNECT_INTERVALS[Math.min(reconnectAttempt,RECONNECT_INTERVALS.length-1)];reconnectAttempt++,reconnectTimer=setTimeout(()=>{reconnectTimer=null,messageHandlers.size>0&¤tBaseUrl&¤tToken&&getOrCreateSharedWs(currentBaseUrl,currentToken)},e)}},n.onerror=()=>{sharedWsStatus="error"},n}function addMessageHandler(e){return messageHandlers.add(e),()=>{messageHandlers.delete(e),0===messageHandlers.size&&sharedWs&&(sharedWs.close(),sharedWs=null)}}function sendMessage(e){return!(!sharedWs||sharedWs.readyState!==WebSocket.OPEN||(sharedWs.send(JSON.stringify(e)),0))}function useRealtime(e,t={}){const r=useDypaiClient(),{event:n="*",filter:a,enabled:s=!0}=t,[o,c]=react.useState([]),[i,u]=react.useState("disconnected"),[l,d]=react.useState(null),p=react.useRef(t);return p.current=t,react.useEffect(()=>{if(!s)return;const t=r.baseUrl,o=r.getAuthToken()||"";if(!o)return void d(new Error("Authentication required for realtime"));const i=getOrCreateSharedWs(t,o);u(sharedWsStatus),function(e){const t=pendingSubscriptions.findIndex(t=>t.table===e.table);t>=0&&pendingSubscriptions.splice(t,1),pendingSubscriptions.push(e)}({table:e,event:n,filter:a});const l=()=>{i.readyState===WebSocket.OPEN&&(i.send(JSON.stringify({type:"subscribe",table:e,event:n,filter:a})),u("connected"))};i.readyState===WebSocket.OPEN?l():i.addEventListener("open",l,{once:!0});const h=addMessageHandler(t=>{if("connected"!==t.type&&"subscribe"!==t.type||u("connected"),"change"!==t.type||t.table!==e)return;if("*"!==n&&t.event!==n)return;const r={table:t.table,event:t.event,record:t.record??null,old_record:t.old_record??null,timestamp:t.timestamp,truncated:t.truncated};c(e=>[...e.slice(-99),r]),p.current.onChange?.(r),t.truncated?("INSERT"===t.event&&p.current.onInsert?.(null),"UPDATE"===t.event&&p.current.onUpdate?.(null,null),"DELETE"===t.event&&p.current.onDelete?.(null)):("INSERT"===t.event&&t.record&&p.current.onInsert?.(t.record),"UPDATE"===t.event&&t.record&&p.current.onUpdate?.(t.record,t.old_record),"DELETE"===t.event&&t.old_record&&p.current.onDelete?.(t.old_record))});return()=>{h(),function(e){const t=pendingSubscriptions.findIndex(t=>t.table===e);t>=0&&pendingSubscriptions.splice(t,1)}(e),sendMessage({type:"unsubscribe",table:e})}},[e,n,s,r]),{changes:o,isConnected:"connected"===i,status:i,error:l}}exports.DypaiProvider=function({client:e,children:t}){const[r,n]=react.useState(null),[a,s]=react.useState(null),[o,c]=react.useState(!0),[i,u]=react.useState(null);react.useEffect(()=>{const{data:{subscription:t}}=e.auth.onAuthStateChange((e,t)=>{u(e),"SIGNED_IN"===e||"TOKEN_REFRESHED"===e||"INITIAL_SESSION"===e?(s(t??null),n(t?.user??null),c(!1)):"SIGNED_OUT"===e?(s(null),n(null),c(!1)):"PASSWORD_RECOVERY"===e?(s(t??null),n(t?.user??null),c(!1)):"AUTH_ERROR"===e?c(!1):"USER_UPDATED"===e&&t&&(s(t),n(t.user??null))});return()=>{t?.unsubscribe()}},[e]);const l={client:e,user:r,session:a,isLoading:o,isAuthenticated:!!r,authEvent:i};return jsxRuntime.jsx(DypaiContext.Provider,{value:l,children:t})},exports.ProtectedRoute=function({children:e,redirectTo:t,roles:r,loadingComponent:n,unauthorizedComponent:a,unauthenticatedComponent:s}){const{user:o,isLoading:c,isAuthenticated:i}=useDypai();if(c)return jsxRuntime.jsx(jsxRuntime.Fragment,{children:n||null});if(!i)return t&&"undefined"!=typeof window?(window.location.replace(t),null):jsxRuntime.jsx(jsxRuntime.Fragment,{children:s||null});if(r&&r.length>0){const e=o?.role;if(!e||!r.includes(e))return jsxRuntime.jsx(jsxRuntime.Fragment,{children:a||null})}return jsxRuntime.jsx(jsxRuntime.Fragment,{children:e})},exports.generateChatId=generateChatId,exports.getMessageText=function(e){return e.parts?.length?e.parts.filter(e=>"text"===e.type).map(e=>e.text||"").join(""):e.content||""},exports.getToolCalls=function(e){return e.parts?.length?e.parts.filter(e=>"tool-call"===e.type).map(e=>({toolName:e.toolName||"unknown",args:e.args})):[]},exports.useAction=function(e,t={}){const r=useDypaiClient(),{method:n="POST",onSuccess:a,onError:s}=t,[o,c]=react.useState(null),[i,u]=react.useState(null),[l,d]=react.useState(!1);return{mutate:react.useCallback(async t=>{let o;switch(d(!0),u(null),n){case"PUT":o=await r.api.put(e,t);break;case"PATCH":o=await r.api.patch(e,t);break;case"DELETE":o=await r.api.delete(e,{params:t});break;default:o=await r.api.post(e,t)}return d(!1),o.error?(u(o.error),s?.(o.error),{data:null,error:o.error}):(c(o.data),a?.(o.data),{data:o.data,error:null})},[r,e,n,a,s]),data:o,error:i,isLoading:l,reset:react.useCallback(()=>{c(null),u(null),d(!1)},[])}},exports.useAuth=function(){const{client:e,user:t,isLoading:r,isAuthenticated:n,authEvent:a}=useDypai(),s=react.useCallback(async(t,r)=>{const{error:n}=await e.auth.signInWithPassword({email:t,password:r});return{error:n}},[e]),o=react.useCallback(async(t,r,n)=>{const{data:a,error:s}=await e.auth.signUp({email:t,password:r,...n});return{error:s,confirmationRequired:a?.confirmationRequired}},[e]),c=react.useCallback(async()=>{await e.auth.signOut()},[e]),i=react.useCallback(async(t,r)=>{const{error:n}=await e.auth.resetPasswordForEmail(t,r);return{error:n}},[e]),u=react.useCallback(async t=>{const{error:r}=await e.auth.setPassword(t);return{error:r}},[e]),l=react.useCallback(async t=>{await e.auth.signInWithOAuth(t)},[e]),d=react.useCallback(async t=>{const{error:r}=await e.auth.signInWithOtp({email:t});return{error:r}},[e]),p=react.useCallback(async t=>{const{error:r}=await e.auth.signInWithMagicLink(t);return{error:r}},[e]),h=react.useCallback(async()=>{const{data:t,error:r}=await e.auth.enableTwoFactor();return{data:t,error:r}},[e]),f=react.useCallback(async t=>{const{error:r}=await e.auth.verifyTwoFactor(t);return{error:r}},[e]),y=react.useCallback(async()=>{const{error:t}=await e.auth.disableTwoFactor();return{error:t}},[e]),w=react.useCallback(async(t,r)=>{const{data:n,error:a}=await e.auth.createOrganization(t,r);return{data:n,error:a}},[e]),m=react.useCallback(async()=>{const{data:t,error:r}=await e.auth.listOrganizations();return{data:t,error:r}},[e]),g=react.useCallback(async(t,r,n)=>{const{error:a}=await e.auth.inviteMember(t,r,n);return{error:a}},[e]);return{user:t,isLoading:r,isAuthenticated:n,authEvent:a,lastError:e.auth.lastError,isPasswordRecovery:e.auth.isPasswordRecoveryCallback||e.auth.hasPasswordRecoveryToken,signIn:s,signUp:o,signOut:c,resetPassword:i,setPassword:u,signInWithOAuth:l,signInWithOtp:d,signInWithMagicLink:p,enableTwoFactor:h,verifyTwoFactor:f,disableTwoFactor:y,createOrganization:w,listOrganizations:m,inviteMember:g}},exports.useChannel=function(e,t={}){const r=useDypaiClient(),{presence:n,enabled:a=!0}=t,[s,o]=react.useState({}),[c,i]=react.useState(!1),u=react.useRef(t);return u.current=t,react.useEffect(()=>{if(!a||!e)return;const t=r.baseUrl,s=r.getAuthToken()||"";if(!s)return;const c=getOrCreateSharedWs(t,s);!function(e){const t=pendingChannelJoins.findIndex(t=>t.channel===e.channel);t>=0&&pendingChannelJoins.splice(t,1),pendingChannelJoins.push(e)}({channel:e,presence:n||void 0});const l=()=>{c.readyState===WebSocket.OPEN&&c.send(JSON.stringify({type:"join",channel:e,presence:n||void 0}))};c.readyState===WebSocket.OPEN?l():c.addEventListener("open",l,{once:!0});const d=addMessageHandler(t=>{"connected"!==t.type&&"join"!==t.type||i(!0),"broadcast"===t.type&&t.channel===e&&u.current.onBroadcast?.({event:t.event,payload:t.payload,sender:t.sender,timestamp:t.timestamp}),"presence_state"===t.type&&t.channel===e&&o(t.presences||{}),"presence_diff"===t.type&&t.channel===e&&o(e=>{const r={...e};if(t.joins)for(const[e,n]of Object.entries(t.joins))r[e]=n,u.current.onJoin?.(e,n);if(t.leaves)for(const[e,n]of Object.entries(t.leaves))delete r[e],u.current.onLeave?.(e,n);return r})});return()=>{d(),function(e){const t=pendingChannelJoins.findIndex(t=>t.channel===e);t>=0&&pendingChannelJoins.splice(t,1)}(e),sendMessage({type:"leave",channel:e}),i(!1)}},[e,a,r]),{broadcast:react.useCallback((t,r)=>{sendMessage({type:"broadcast",channel:e,event:t,payload:r})},[e]),presences:s,isConnected:c,memberCount:Object.keys(s).length}},exports.useChannelMessages=function(e,t={}){const r=useDypaiClient(),{loadHistory:n=!0,limit:a=50}=t,[s,o]=react.useState([]),[c,i]=react.useState(!1),[u,l]=react.useState(null),d=react.useRef(new Set);react.useEffect(()=>{if(!n||!e)return;let t=!1;return(async()=>{i(!0);try{const n=r.baseUrl,s=r.getAuthToken()||"",c=await fetch(`${n}/api/v0/channels/${e}/messages?limit=${a}`,{headers:{Authorization:`Bearer ${s}`}});if(!c.ok)throw new Error(`HTTP ${c.status}`);const i=await c.json();if(!t&&i.messages){const e=i.messages;e.forEach(e=>d.current.add(e.id)),o(e)}}catch(e){t||l(e)}finally{t||i(!1)}})(),()=>{t=!0}},[e,n,a,r]);const{isConnected:p}=useRealtime("channel_messages",{event:"INSERT",filter:{channel_id:e},onInsert:e=>{e&&(d.current.has(e.id)||(d.current.add(e.id),o(t=>[...t,e])))}});return{messages:s,send:react.useCallback(async(t,n)=>{try{const a=r.baseUrl,s=r.getAuthToken()||"",c=await fetch(`${a}/api/v0/channels/${e}/messages`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({content:t,metadata:n})});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error||`HTTP ${c.status}`)}const i=await c.json();i.message&&(d.current.add(i.message.id),o(e=>[...e,i.message]))}catch(e){throw l(e),e}},[e,r]),isLoading:c,isConnected:p,error:u}},exports.useChannels=function(){const e=useDypaiClient(),[t,r]=react.useState([]),[n,a]=react.useState(!1),[s,o]=react.useState(null),c=react.useCallback(async()=>{a(!0);try{const t=e.baseUrl,n=e.getAuthToken()||"",a=await fetch(`${t}/api/v0/channels`,{headers:{Authorization:`Bearer ${n}`}});if(!a.ok)throw new Error(`HTTP ${a.status}`);const s=await a.json();r(s.channels||[])}catch(e){o(e)}finally{a(!1)}},[e]);return react.useEffect(()=>{c()},[c]),{channels:t,isLoading:n,error:s,createChannel:react.useCallback(async(t,n=[],a="group")=>{const s=e.baseUrl,o=e.getAuthToken()||"",c=await fetch(`${s}/api/v0/channels`,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify({name:t,type:a,members:n})});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error||`HTTP ${c.status}`)}const i=await c.json();return r(e=>[i,...e]),i},[e]),refetch:c}},exports.useChat=function(e,t={}){const r=useDypaiClient(),[n,a]=react.useState(t.id||generateId),[s,o]=react.useState(t.initialMessages||[]),[c,i]=react.useState("ready"),[u,l]=react.useState(""),[d,p]=react.useState(null),h=react.useRef(null);react.useEffect(()=>{if(!1===t.loadHistory)return;if(!t.id)return;let e=!1;return(async()=>{try{const t=r.baseUrl,a=r.getAuthToken()||"",s=await fetch(`${t}/api/v0/chats/${n}/messages?limit=50`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(!s.ok||e)return;const c=await s.json();if(c.messages&&!e){const e=c.messages.map(e=>({id:e.id,role:e.role,content:(e.parts||[]).filter(e=>"text"===e.type).map(e=>e.text).join(""),parts:e.parts,metadata:e.metadata,createdAt:new Date(e.created_at)}));o(e)}}catch{}})(),()=>{e=!0}},[n,t.id,t.loadHistory]);const f=react.useCallback(async a=>{const c=a||u;if(!c.trim())return;a||l(""),p(null);const d={id:generateId(),role:"user",content:c,parts:[{type:"text",text:c}],createdAt:new Date},f=[...s,d];o(f),i("submitted");const y={id:generateId(),role:"assistant",content:"",parts:[],createdAt:new Date};try{const a=r.baseUrl,s=r.getAuthToken()||"",u=new AbortController;h.current=u;const l={session_id:n,messages:[{role:"user",content:c}],...t.body||{}},d=await fetch(`${a}/api/v0/${e}`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream",...t.headers||{}},body:JSON.stringify(l),signal:u.signal});if(!d.ok){const e=await d.json().catch(()=>({error:d.statusText}));throw new Error(e.error||`HTTP ${d.status}`)}if((d.headers.get("content-type")||"").includes("application/json")){const e=await d.json(),r=e.content||e.text||JSON.stringify(e);y.content=r,y.parts=[{type:"text",text:r}];const n=[...f,{...y}];return o(n),i("ready"),void t.onFinish?.(y,n)}i("streaming"),o([...f,y]);const p=d.body?.getReader();if(!p)throw new Error("No response body");const w=new TextDecoder;let m="";for(;;){const{done:e,value:t}=await p.read();if(e)break;const r=w.decode(t,{stream:!0}).split("\n").filter(e=>!/^[A-Z][a-zA-Z-]+:\s/.test(e)).join("\n");r&&(m+=r,y.content=m,y.parts=[{type:"text",text:m}],o([...f,{...y}]))}const g={...y,content:m,parts:[{type:"text",text:m}]},T=[...f,g];o(T),i("ready"),t.onFinish?.(g,T)}catch(e){if("AbortError"===e.name)return void i("ready");p(e),i("error"),t.onError?.(e)}finally{h.current=null}},[u,s,n,e,r,t]),y=react.useCallback(()=>{h.current?.abort(),h.current=null,i("ready")},[]),w=react.useCallback(()=>{y(),o([]),p(null),i("ready"),l(""),a(generateId())},[y]);return{messages:s,status:c,input:u,setInput:l,sendMessage:f,setMessages:o,stop:y,newChat:w,isLoading:"submitted"===c||"streaming"===c,error:d,chatId:n}},exports.useChatList=function(e={}){const t=useDypaiClient(),{endpointId:r,limit:n=20,enabled:a=!0}=e,[s,o]=react.useState([]),[c,i]=react.useState(!1),[u,l]=react.useState(null),d=react.useCallback(async()=>{i(!0),l(null);try{const e=t.baseUrl,a=t.getAuthToken()||"",s=new URLSearchParams({limit:String(n)});r&&s.set("endpoint_id",r);const c=await fetch(`${e}/api/v0/chats?${s}`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error||`HTTP ${c.status}`)}const i=((await c.json()).chats||[]).map(e=>({id:e.id,title:e.title,visibility:e.visibility,messageCount:parseInt(e.message_count||"0"),createdAt:e.created_at,updatedAt:e.updated_at}));o(i)}catch(e){l(e)}finally{i(!1)}},[t,r,n]),p=react.useCallback(async e=>{const r=t.baseUrl,n=t.getAuthToken()||"",a=await fetch(`${r}/api/v0/chats/${e}`,{method:"DELETE",headers:{Authorization:`Bearer ${n}`}});if(!a.ok){const e=await a.json().catch(()=>({}));throw new Error(e.error||`HTTP ${a.status}`)}o(t=>t.filter(t=>t.id!==e))},[t]),h=react.useCallback(async(e,r)=>{const n=t.baseUrl,a=t.getAuthToken()||"",s=await fetch(`${n}/api/v0/chats/${e}`,{method:"PATCH",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify({title:r})});if(!s.ok){const e=await s.json().catch(()=>({}));throw new Error(e.error||`HTTP ${s.status}`)}o(t=>t.map(t=>t.id===e?{...t,title:r}:t))},[t]);return react.useEffect(()=>{a&&d()},[a,d]),{chats:s,isLoading:c,error:u,refetch:d,deleteChat:p,renameChat:h}},exports.useDypai=useDypai,exports.useDypaiClient=useDypaiClient,exports.useEndpoint=function(e,t={}){const r=useDypaiClient(),{params:n,enabled:a=!0,refetchInterval:s=0}=t,[o,c]=react.useState(null),[i,u]=react.useState(null),[l,d]=react.useState(!0),[p,h]=react.useState(!1),f=react.useRef(!0),y=JSON.stringify(n||{}),w=react.useCallback(async(t=!1)=>{if(!a)return void d(!1);t?h(!0):d(!0),u(null);const{data:s,error:o}=await r.api.get(e,{params:n});f.current&&(o?(u(o),c(null)):c(s),d(!1),h(!1))},[r,e,y,a]);return react.useEffect(()=>(f.current=!0,w(),()=>{f.current=!1}),[w]),react.useEffect(()=>{if(!s||!a)return;const e=setInterval(()=>w(!0),s);return()=>clearInterval(e)},[w,s,a]),{data:o,error:i,isLoading:l,isRefetching:p,refetch:react.useCallback(async()=>{await w(!0)},[w])}},exports.useRealtime=useRealtime,exports.useUpload=function(e,t={}){const r=useDypaiClient(),{onSuccess:n,onError:a}=t,[s,o]=react.useState(0),[c,i]=react.useState(!1),[u,l]=react.useState(null),[d,p]=react.useState(null);return{upload:react.useCallback(async(t,s)=>{i(!0),o(0),l(null);const{data:c,error:u}=await r.api.upload(e,t,{params:s,onProgress:o});return i(!1),u?(l(u),a?.(u),{data:null,error:u}):(p(c),n?.(c),{data:c,error:null})},[r,e,n,a]),progress:s,isUploading:c,error:u,data:d,reset:react.useCallback(()=>{o(0),i(!1),l(null),p(null)},[])}};
|
|
1
|
+
"use strict";var jsxRuntime=require("react/jsx-runtime"),react=require("react");const DypaiContext=react.createContext(null);function useDypai(){const e=react.useContext(DypaiContext);if(!e)throw new Error('useDypai must be used within a <DypaiProvider>. Wrap your app with <DypaiProvider url="...">.');return e}function useDypaiClient(){return useDypai().client}function generateChatId(){return crypto.randomUUID?.()||`${Date.now()}-${Math.random().toString(36).slice(2)}`}function generateId(){return generateChatId()}function parseAIStreamEvent(e){const t=e.trim();if(!t||"[DONE]"===t)return null;if(/^[a-z0-9]:/.test(t)){const e=t[0],r=t.slice(2);if("0"===e){const e=JSON.parse(r);return"string"==typeof e?{type:"text-delta",delta:e}:null}if("9"===e){const e=JSON.parse(r);return{type:"tool-call",toolName:String(e?.toolName||e?.function?.name||"tool"),args:e?.args??e?.arguments??e?.input??{}}}if("e"===e){const e=JSON.parse(r);throw new Error(e?.errorText||e?.message||"AI stream error")}return null}const r=JSON.parse(t);switch(r?.type){case"text-delta":return"string"==typeof r.delta?{type:"text-delta",delta:r.delta}:null;case"text":return"string"==typeof r.text?{type:"text-delta",delta:r.text}:null;case"tool-input-available":return{type:"tool-call",toolName:String(r.toolName||"tool"),args:r.input??{}};case"tool-output-available":return{type:"tool-result",toolName:String(r.toolName||r.toolCallId||"tool"),result:r.output};case"error":throw new Error(r.errorText||r.message||"AI stream error");default:return null}}function readSSEDataPayloads(e){const t=e.split(/\r?\n/).filter(e=>e.startsWith("data:")).map(e=>e.slice(5).trimStart());return t.length>0?[t.join("\n")]:[]}function readLegacyDataPayloads(e){const t=e.split(/\r?\n/),r=t.pop()||"";return{payloads:t.map(e=>e.trim()).filter(Boolean),rest:r}}let sharedWs=null,sharedWsStatus="disconnected",reconnectTimer=null,heartbeatTimer=null,reconnectAttempt=0,currentBaseUrl="",currentToken="";const RECONNECT_INTERVALS=[1e3,2e3,5e3,1e4,3e4],messageHandlers=new Set,pendingSubscriptions=[],pendingChannelJoins=[];function getOrCreateSharedWs(e,t){if(sharedWs&&(sharedWs.readyState===WebSocket.OPEN||sharedWs.readyState===WebSocket.CONNECTING))return sharedWs;currentBaseUrl=e,currentToken=t;const r=e.replace(/^http/,"ws")+`/realtime?token=${encodeURIComponent(t)}`,n=new WebSocket(r);return sharedWs=n,sharedWsStatus="connecting",n.onopen=()=>{sharedWsStatus="connected",reconnectAttempt=0;for(const e of pendingSubscriptions)n.send(JSON.stringify({type:"subscribe",...e}));for(const e of pendingChannelJoins)n.send(JSON.stringify({type:"join",...e}));heartbeatTimer&&clearInterval(heartbeatTimer),heartbeatTimer=setInterval(()=>{n.readyState===WebSocket.OPEN&&n.send(JSON.stringify({type:"heartbeat"}))},25e3)},n.onmessage=e=>{try{const t=JSON.parse("string"==typeof e.data?e.data:"");for(const e of messageHandlers)e(t)}catch{}},n.onclose=()=>{if(sharedWsStatus="disconnected",sharedWs=null,heartbeatTimer&&clearInterval(heartbeatTimer),messageHandlers.size>0&&!reconnectTimer){const e=RECONNECT_INTERVALS[Math.min(reconnectAttempt,RECONNECT_INTERVALS.length-1)];reconnectAttempt++,reconnectTimer=setTimeout(()=>{reconnectTimer=null,messageHandlers.size>0&¤tBaseUrl&¤tToken&&getOrCreateSharedWs(currentBaseUrl,currentToken)},e)}},n.onerror=()=>{sharedWsStatus="error"},n}function addMessageHandler(e){return messageHandlers.add(e),()=>{messageHandlers.delete(e),0===messageHandlers.size&&sharedWs&&(sharedWs.close(),sharedWs=null)}}function sendMessage(e){return!(!sharedWs||sharedWs.readyState!==WebSocket.OPEN||(sharedWs.send(JSON.stringify(e)),0))}function useRealtime(e,t={}){const r=useDypaiClient(),{event:n="*",filter:a,enabled:s=!0}=t,[o,c]=react.useState([]),[i,l]=react.useState("disconnected"),[u,d]=react.useState(null),p=react.useRef(t);return p.current=t,react.useEffect(()=>{if(!s)return;const t=r.baseUrl,o=r.getAuthToken()||"";if(!o)return void d(new Error("Authentication required for realtime"));const i=getOrCreateSharedWs(t,o);l(sharedWsStatus),function(e){const t=pendingSubscriptions.findIndex(t=>t.table===e.table);t>=0&&pendingSubscriptions.splice(t,1),pendingSubscriptions.push(e)}({table:e,event:n,filter:a});const u=()=>{i.readyState===WebSocket.OPEN&&(i.send(JSON.stringify({type:"subscribe",table:e,event:n,filter:a})),l("connected"))};i.readyState===WebSocket.OPEN?u():i.addEventListener("open",u,{once:!0});const h=addMessageHandler(t=>{if("connected"!==t.type&&"subscribe"!==t.type||l("connected"),"change"!==t.type||t.table!==e)return;if("*"!==n&&t.event!==n)return;const r={table:t.table,event:t.event,record:t.record??null,old_record:t.old_record??null,timestamp:t.timestamp,truncated:t.truncated};c(e=>[...e.slice(-99),r]),p.current.onChange?.(r),t.truncated?("INSERT"===t.event&&p.current.onInsert?.(null),"UPDATE"===t.event&&p.current.onUpdate?.(null,null),"DELETE"===t.event&&p.current.onDelete?.(null)):("INSERT"===t.event&&t.record&&p.current.onInsert?.(t.record),"UPDATE"===t.event&&t.record&&p.current.onUpdate?.(t.record,t.old_record),"DELETE"===t.event&&t.old_record&&p.current.onDelete?.(t.old_record))});return()=>{h(),function(e){const t=pendingSubscriptions.findIndex(t=>t.table===e);t>=0&&pendingSubscriptions.splice(t,1)}(e),sendMessage({type:"unsubscribe",table:e})}},[e,n,s,r]),{changes:o,isConnected:"connected"===i,status:i,error:u}}exports.DypaiProvider=function({client:e,children:t}){const[r,n]=react.useState(null),[a,s]=react.useState(null),[o,c]=react.useState(!0),[i,l]=react.useState(null);react.useEffect(()=>{const{data:{subscription:t}}=e.auth.onAuthStateChange((e,t)=>{l(e),"SIGNED_IN"===e||"TOKEN_REFRESHED"===e||"INITIAL_SESSION"===e?(s(t??null),n(t?.user??null),c(!1)):"SIGNED_OUT"===e?(s(null),n(null),c(!1)):"PASSWORD_RECOVERY"===e?(s(t??null),n(t?.user??null),c(!1)):"AUTH_ERROR"===e?c(!1):"USER_UPDATED"===e&&t&&(s(t),n(t.user??null))});return()=>{t?.unsubscribe()}},[e]);const u={client:e,user:r,session:a,isLoading:o,isAuthenticated:!!r,authEvent:i};return jsxRuntime.jsx(DypaiContext.Provider,{value:u,children:t})},exports.ProtectedRoute=function({children:e,redirectTo:t,roles:r,loadingComponent:n,unauthorizedComponent:a,unauthenticatedComponent:s}){const{user:o,isLoading:c,isAuthenticated:i}=useDypai();if(c)return jsxRuntime.jsx(jsxRuntime.Fragment,{children:n||null});if(!i)return t&&"undefined"!=typeof window?(window.location.replace(t),null):jsxRuntime.jsx(jsxRuntime.Fragment,{children:s||null});if(r&&r.length>0){const e=o?.role;if(!e||!r.includes(e))return jsxRuntime.jsx(jsxRuntime.Fragment,{children:a||null})}return jsxRuntime.jsx(jsxRuntime.Fragment,{children:e})},exports.generateChatId=generateChatId,exports.getMessageText=function(e){return e.parts?.length?e.parts.filter(e=>"text"===e.type).map(e=>e.text||"").join(""):e.content||""},exports.getToolCalls=function(e){return e.parts?.length?e.parts.filter(e=>"tool-call"===e.type).map(e=>({toolName:e.toolName||"unknown",args:e.args})):[]},exports.useAction=function(e,t={}){const r=useDypaiClient(),{method:n="POST",onSuccess:a,onError:s}=t,[o,c]=react.useState(null),[i,l]=react.useState(null),[u,d]=react.useState(!1);return{mutate:react.useCallback(async t=>{let o;switch(d(!0),l(null),n){case"PUT":o=await r.api.put(e,t);break;case"PATCH":o=await r.api.patch(e,t);break;case"DELETE":o=await r.api.delete(e,{params:t});break;default:o=await r.api.post(e,t)}return d(!1),o.error?(l(o.error),s?.(o.error),{data:null,error:o.error}):(c(o.data),a?.(o.data),{data:o.data,error:null})},[r,e,n,a,s]),data:o,error:i,isLoading:u,reset:react.useCallback(()=>{c(null),l(null),d(!1)},[])}},exports.useAuth=function(){const{client:e,user:t,isLoading:r,isAuthenticated:n,authEvent:a}=useDypai(),s=react.useCallback(async(t,r)=>{const{error:n}=await e.auth.signInWithPassword({email:t,password:r});return{error:n}},[e]),o=react.useCallback(async(t,r,n)=>{const{data:a,error:s}=await e.auth.signUp({email:t,password:r,...n});return{error:s,confirmationRequired:a?.confirmationRequired}},[e]),c=react.useCallback(async()=>{await e.auth.signOut()},[e]),i=react.useCallback(async(t,r)=>{const{error:n}=await e.auth.resetPasswordForEmail(t,r);return{error:n}},[e]),l=react.useCallback(async t=>{const{error:r}=await e.auth.setPassword(t);return{error:r}},[e]),u=react.useCallback(async t=>{await e.auth.signInWithOAuth(t)},[e]),d=react.useCallback(async t=>{const{error:r}=await e.auth.signInWithOtp({email:t});return{error:r}},[e]),p=react.useCallback(async t=>{const{error:r}=await e.auth.signInWithMagicLink(t);return{error:r}},[e]),h=react.useCallback(async()=>{const{data:t,error:r}=await e.auth.enableTwoFactor();return{data:t,error:r}},[e]),f=react.useCallback(async t=>{const{error:r}=await e.auth.verifyTwoFactor(t);return{error:r}},[e]),y=react.useCallback(async()=>{const{error:t}=await e.auth.disableTwoFactor();return{error:t}},[e]),m=react.useCallback(async(t,r)=>{const{data:n,error:a}=await e.auth.createOrganization(t,r);return{data:n,error:a}},[e]),g=react.useCallback(async()=>{const{data:t,error:r}=await e.auth.listOrganizations();return{data:t,error:r}},[e]),w=react.useCallback(async(t,r,n)=>{const{error:a}=await e.auth.inviteMember(t,r,n);return{error:a}},[e]);return{user:t,isLoading:r,isAuthenticated:n,authEvent:a,lastError:e.auth.lastError,isPasswordRecovery:e.auth.isPasswordRecoveryCallback||e.auth.hasPasswordRecoveryToken,signIn:s,signUp:o,signOut:c,resetPassword:i,setPassword:l,signInWithOAuth:u,signInWithOtp:d,signInWithMagicLink:p,enableTwoFactor:h,verifyTwoFactor:f,disableTwoFactor:y,createOrganization:m,listOrganizations:g,inviteMember:w}},exports.useChannel=function(e,t={}){const r=useDypaiClient(),{presence:n,enabled:a=!0}=t,[s,o]=react.useState({}),[c,i]=react.useState(!1),l=react.useRef(t);return l.current=t,react.useEffect(()=>{if(!a||!e)return;const t=r.baseUrl,s=r.getAuthToken()||"";if(!s)return;const c=getOrCreateSharedWs(t,s);!function(e){const t=pendingChannelJoins.findIndex(t=>t.channel===e.channel);t>=0&&pendingChannelJoins.splice(t,1),pendingChannelJoins.push(e)}({channel:e,presence:n||void 0});const u=()=>{c.readyState===WebSocket.OPEN&&c.send(JSON.stringify({type:"join",channel:e,presence:n||void 0}))};c.readyState===WebSocket.OPEN?u():c.addEventListener("open",u,{once:!0});const d=addMessageHandler(t=>{"connected"!==t.type&&"join"!==t.type||i(!0),"broadcast"===t.type&&t.channel===e&&l.current.onBroadcast?.({event:t.event,payload:t.payload,sender:t.sender,timestamp:t.timestamp}),"presence_state"===t.type&&t.channel===e&&o(t.presences||{}),"presence_diff"===t.type&&t.channel===e&&o(e=>{const r={...e};if(t.joins)for(const[e,n]of Object.entries(t.joins))r[e]=n,l.current.onJoin?.(e,n);if(t.leaves)for(const[e,n]of Object.entries(t.leaves))delete r[e],l.current.onLeave?.(e,n);return r})});return()=>{d(),function(e){const t=pendingChannelJoins.findIndex(t=>t.channel===e);t>=0&&pendingChannelJoins.splice(t,1)}(e),sendMessage({type:"leave",channel:e}),i(!1)}},[e,a,r]),{broadcast:react.useCallback((t,r)=>{sendMessage({type:"broadcast",channel:e,event:t,payload:r})},[e]),presences:s,isConnected:c,memberCount:Object.keys(s).length}},exports.useChannelMessages=function(e,t={}){const r=useDypaiClient(),{loadHistory:n=!0,limit:a=50}=t,[s,o]=react.useState([]),[c,i]=react.useState(!1),[l,u]=react.useState(null),d=react.useRef(new Set);react.useEffect(()=>{if(!n||!e)return;let t=!1;return(async()=>{i(!0);try{const n=r.baseUrl,s=r.getAuthToken()||"",c=await fetch(`${n}/api/v0/channels/${e}/messages?limit=${a}`,{headers:{Authorization:`Bearer ${s}`}});if(!c.ok)throw new Error(`HTTP ${c.status}`);const i=await c.json();if(!t&&i.messages){const e=i.messages;e.forEach(e=>d.current.add(e.id)),o(e)}}catch(e){t||u(e)}finally{t||i(!1)}})(),()=>{t=!0}},[e,n,a,r]);const{isConnected:p}=useRealtime("channel_messages",{event:"INSERT",filter:{channel_id:e},onInsert:e=>{e&&(d.current.has(e.id)||(d.current.add(e.id),o(t=>[...t,e])))}});return{messages:s,send:react.useCallback(async(t,n)=>{try{const a=r.baseUrl,s=r.getAuthToken()||"",c=await fetch(`${a}/api/v0/channels/${e}/messages`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({content:t,metadata:n})});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error||`HTTP ${c.status}`)}const i=await c.json();i.message&&(d.current.add(i.message.id),o(e=>[...e,i.message]))}catch(e){throw u(e),e}},[e,r]),isLoading:c,isConnected:p,error:l}},exports.useChannels=function(){const e=useDypaiClient(),[t,r]=react.useState([]),[n,a]=react.useState(!1),[s,o]=react.useState(null),c=react.useCallback(async()=>{a(!0);try{const t=e.baseUrl,n=e.getAuthToken()||"",a=await fetch(`${t}/api/v0/channels`,{headers:{Authorization:`Bearer ${n}`}});if(!a.ok)throw new Error(`HTTP ${a.status}`);const s=await a.json();r(s.channels||[])}catch(e){o(e)}finally{a(!1)}},[e]);return react.useEffect(()=>{c()},[c]),{channels:t,isLoading:n,error:s,createChannel:react.useCallback(async(t,n=[],a="group")=>{const s=e.baseUrl,o=e.getAuthToken()||"",c=await fetch(`${s}/api/v0/channels`,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify({name:t,type:a,members:n})});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error||`HTTP ${c.status}`)}const i=await c.json();return r(e=>[i,...e]),i},[e]),refetch:c}},exports.useChat=function(e,t={}){const r=useDypaiClient(),[n,a]=react.useState(t.id||generateId),[s,o]=react.useState(t.initialMessages||[]),[c,i]=react.useState("ready"),[l,u]=react.useState(""),[d,p]=react.useState(null),h=react.useRef(null);react.useEffect(()=>{if(!1===t.loadHistory)return;if(!t.id)return;let e=!1;return(async()=>{try{const t=r.baseUrl,a=r.getAuthToken()||"",s=await fetch(`${t}/api/v0/chats/${n}/messages?limit=50`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(!s.ok||e)return;const c=await s.json();if(c.messages&&!e){const e=c.messages.map(e=>({id:e.id,role:e.role,content:(e.parts||[]).filter(e=>"text"===e.type).map(e=>e.text).join(""),parts:e.parts,metadata:e.metadata,createdAt:new Date(e.created_at)}));o(e)}}catch{}})(),()=>{e=!0}},[n,t.id,t.loadHistory]);const f=react.useCallback(async a=>{const c=a||l;if(!c.trim())return;a||u(""),p(null);const d={id:generateId(),role:"user",content:c,parts:[{type:"text",text:c}],createdAt:new Date},f=[...s,d];o(f),i("submitted");const y={id:generateId(),role:"assistant",content:"",parts:[],createdAt:new Date};try{const a=r.baseUrl,s=r.getAuthToken()||"",l=new AbortController;h.current=l;const u={session_id:n,messages:[{role:"user",content:c}],...t.body||{}},d=await fetch(`${a}/api/v0/${e}`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream",...t.headers||{}},body:JSON.stringify(u),signal:l.signal});if(!d.ok){const e=await d.json().catch(()=>({error:d.statusText}));throw new Error(e.error||`HTTP ${d.status}`)}if((d.headers.get("content-type")||"").includes("application/json")){const e=function(e){if("string"==typeof e?.content)return e.content;if("string"==typeof e?.text)return e.text;const t=Array.isArray(e?.messages)?e.messages:[],r=t[t.length-1],n=r?.content;if("string"==typeof n)return n;if(Array.isArray(n)){const e=n.filter(e=>"text"===e?.type).map(e=>e.text||"").join("");if(e)return e}return JSON.stringify(e)}(await d.json());y.content=e,y.parts=[{type:"text",text:e}];const r=[...f,{...y}];return o(r),i("ready"),void t.onFinish?.(y,r)}i("streaming"),o([...f,y]);const p=d.body?.getReader();if(!p)throw new Error("No response body");const g=new TextDecoder;let w="",S="";const T=[],b="v1"===(m=d.headers).get("x-vercel-ai-ui-message-stream")?"ui-message":"v1"===m.get("x-vercel-ai-data-stream")?"legacy-data":null,E=()=>{y.content=w,y.parts=[...w?[{type:"text",text:w}]:[],...T],o([...f,{...y}])},C=e=>{if(e)return"text-delta"===e.type?(w+=e.delta,void E()):"tool-call"===e.type?(T.push({type:"tool-call",toolName:e.toolName,args:e.args}),t.onToolCall?.({toolName:e.toolName,args:e.args}),void E()):void("tool-result"===e.type&&(T.push({type:"tool-result",toolName:e.toolName,result:e.result}),E()))};for(;;){const{done:e,value:t}=await p.read();if(e)break;const r=g.decode(t,{stream:!0});if("ui-message"===b){S+=r;const e=S.split(/\r?\n\r?\n/);S=e.pop()||"";for(const t of e)for(const e of readSSEDataPayloads(t))C(parseAIStreamEvent(e));continue}if("legacy-data"===b){S+=r;const{payloads:e,rest:t}=readLegacyDataPayloads(S);S=t;for(const t of e)C(parseAIStreamEvent(t));continue}const n=r.split("\n").filter(e=>!/^[A-Z][a-zA-Z-]+:\s/.test(e)).join("\n");n&&(w+=n,E())}if("ui-message"===b&&S.trim())for(const e of readSSEDataPayloads(S))C(parseAIStreamEvent(e));else if("legacy-data"===b&&S.trim())for(const e of[S.trim()])C(parseAIStreamEvent(e));const v={...y,content:w,parts:y.parts?.length?y.parts:[{type:"text",text:w}]},x=[...f,v];o(x),i("ready"),t.onFinish?.(v,x)}catch(e){if("AbortError"===e.name)return void i("ready");p(e),i("error"),t.onError?.(e)}finally{h.current=null}var m},[l,s,n,e,r,t]),y=react.useCallback(()=>{h.current?.abort(),h.current=null,i("ready")},[]),m=react.useCallback(()=>{y(),o([]),p(null),i("ready"),u(""),a(generateId())},[y]);return{messages:s,status:c,input:l,setInput:u,sendMessage:f,setMessages:o,stop:y,newChat:m,isLoading:"submitted"===c||"streaming"===c,error:d,chatId:n}},exports.useChatList=function(e={}){const t=useDypaiClient(),{endpointId:r,limit:n=20,enabled:a=!0}=e,[s,o]=react.useState([]),[c,i]=react.useState(!1),[l,u]=react.useState(null),d=react.useCallback(async()=>{i(!0),u(null);try{const e=t.baseUrl,a=t.getAuthToken()||"",s=new URLSearchParams({limit:String(n)});r&&s.set("endpoint_id",r);const c=await fetch(`${e}/api/v0/chats?${s}`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error||`HTTP ${c.status}`)}const i=((await c.json()).chats||[]).map(e=>({id:e.id,title:e.title,visibility:e.visibility,messageCount:parseInt(e.message_count||"0"),createdAt:e.created_at,updatedAt:e.updated_at}));o(i)}catch(e){u(e)}finally{i(!1)}},[t,r,n]),p=react.useCallback(async e=>{const r=t.baseUrl,n=t.getAuthToken()||"",a=await fetch(`${r}/api/v0/chats/${e}`,{method:"DELETE",headers:{Authorization:`Bearer ${n}`}});if(!a.ok){const e=await a.json().catch(()=>({}));throw new Error(e.error||`HTTP ${a.status}`)}o(t=>t.filter(t=>t.id!==e))},[t]),h=react.useCallback(async(e,r)=>{const n=t.baseUrl,a=t.getAuthToken()||"",s=await fetch(`${n}/api/v0/chats/${e}`,{method:"PATCH",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify({title:r})});if(!s.ok){const e=await s.json().catch(()=>({}));throw new Error(e.error||`HTTP ${s.status}`)}o(t=>t.map(t=>t.id===e?{...t,title:r}:t))},[t]);return react.useEffect(()=>{a&&d()},[a,d]),{chats:s,isLoading:c,error:l,refetch:d,deleteChat:p,renameChat:h}},exports.useDypai=useDypai,exports.useDypaiClient=useDypaiClient,exports.useEndpoint=function(e,t={}){const r=useDypaiClient(),{params:n,enabled:a=!0,refetchInterval:s=0}=t,[o,c]=react.useState(null),[i,l]=react.useState(null),[u,d]=react.useState(!0),[p,h]=react.useState(!1),f=react.useRef(!0),y=JSON.stringify(n||{}),m=react.useCallback(async(t=!1)=>{if(!a)return void d(!1);t?h(!0):d(!0),l(null);const{data:s,error:o}=await r.api.get(e,{params:n});f.current&&(o?(l(o),c(null)):c(s),d(!1),h(!1))},[r,e,y,a]);return react.useEffect(()=>(f.current=!0,m(),()=>{f.current=!1}),[m]),react.useEffect(()=>{if(!s||!a)return;const e=setInterval(()=>m(!0),s);return()=>clearInterval(e)},[m,s,a]),{data:o,error:i,isLoading:u,isRefetching:p,refetch:react.useCallback(async()=>{await m(!0)},[m])}},exports.useRealtime=useRealtime,exports.useUpload=function(e,t={}){const r=useDypaiClient(),{onSuccess:n,onError:a}=t,[s,o]=react.useState(0),[c,i]=react.useState(!1),[l,u]=react.useState(null),[d,p]=react.useState(null);return{upload:react.useCallback(async(t,s)=>{i(!0),o(0),u(null);const{data:c,error:l}=await r.api.upload(e,t,{params:s,onProgress:o});return i(!1),l?(u(l),a?.(l),{data:null,error:l}):(p(c),n?.(c),{data:c,error:null})},[r,e,n,a]),progress:s,isUploading:c,error:l,data:d,reset:react.useCallback(()=>{o(0),i(!1),u(null),p(null)},[])}};
|
package/package.json
CHANGED