@holaboss/client 0.2.0-beta.4 → 0.2.0-beta.5

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/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client'
2
- var F=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var ee=Object.prototype.hasOwnProperty;var te=(e,t)=>{for(var a in t)F(e,a,{get:t[a],enumerable:!0})},re=(e,t,a,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Z(t))!ee.call(e,n)&&n!==a&&F(e,n,{get:()=>t[n],enumerable:!(s=X(t,n))||s.enumerable});return e};var oe=e=>re(F({},"__esModule",{value:!0}),e);var ue={};te(ue,{Chat:()=>B,HolaProvider:()=>q,PortalConflict:()=>v,createApiTransport:()=>z,createHolaClient:()=>K,createPortalData:()=>V,createWidgetTransport:()=>O,mergeMessage:()=>L,textDeltaOf:()=>I,thinkingDeltaOf:()=>R,toolEventOf:()=>E,useArtifactUrl:()=>k,useChat:()=>U,useHolaClient:()=>A,useThread:()=>Q});module.exports=oe(ue);var W=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},I=e=>{let t=W(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},R=e=>{let t=W(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var se=/^ /,C=e=>{if(e&&typeof e=="object"){let t=e,a=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:a,retryable:t.retryable}:{error:a}}return{error:"request_failed"}},ne=e=>{let t="message",a=[];for(let s of e.split(`
3
- `))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&a.push(s.slice(5).replace(se,"")));return a.length>0?{event:t,data:a.join(`
4
- `)}:null},ae=(e,t,a)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=s.sessionId;n&&(a(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(s);let n=I(s);n&&t.onText?.(n);let d=R(s);d&&t.onThinking?.(d);let r=E(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=s;a(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(C(s))},h=async(e,t)=>{let a=await fetch(e,{headers:t});if(!a.ok)throw new Error(`hola: request failed (${a.status})`);return await a.json()},j=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},N=async(e,t,a,s)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let d=n.body.getReader(),r=new TextDecoder,o="",l=()=>{let c=o.indexOf(`
2
+ var _=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var de=Object.getOwnPropertyNames;var ue=Object.prototype.hasOwnProperty;var ce=(e,t)=>{for(var o in t)_(e,o,{get:t[o],enumerable:!0})},pe=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of de(t))!ue.call(e,r)&&r!==o&&_(e,r,{get:()=>t[r],enumerable:!(s=le(t,r))||s.enumerable});return e};var ge=e=>pe(_({},"__esModule",{value:!0}),e);var Ee={};ce(Ee,{Chat:()=>X,HolaProvider:()=>Y,PortalConflict:()=>S,createApiTransport:()=>q,createHolaClient:()=>Z,createPortalData:()=>se,createWidgetTransport:()=>O,inferSpec:()=>N,mergeMessage:()=>B,parseDashboardSpec:()=>j,renderModel:()=>L,textDeltaOf:()=>v,thinkingDeltaOf:()=>R,toolEventOf:()=>I,useArtifactUrl:()=>A,useChat:()=>D,useHolaClient:()=>M,useThread:()=>ie});module.exports=ge(Ee);var G=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},v=e=>{let t=G(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},R=e=>{let t=G(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},I=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var me=/^ /,k=e=>{if(e&&typeof e=="object"){let t=e,o=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:o,retryable:t.retryable}:{error:o}}return{error:"request_failed"}},fe=e=>{let t="message",o=[];for(let s of e.split(`
3
+ `))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&o.push(s.slice(5).replace(me,"")));return o.length>0?{event:t,data:o.join(`
4
+ `)}:null},ye=(e,t,o)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let r=s.sessionId;r&&(o(r),t.onSession?.(r));return}if(e.event==="turn_event"){t.onEvent?.(s);let r=v(s);r&&t.onText?.(r);let d=R(s);d&&t.onThinking?.(d);let n=I(s);n&&t.onTool?.(n);return}if(e.event==="chat_completed"){let r=s;o(r.sessionId),t.onComplete?.(r);return}e.event==="chat_error"&&t.onError?.(k(s))},h=async(e,t)=>{let o=await fetch(e,{headers:t});if(!o.ok)throw new Error(`hola: request failed (${o.status})`);return await o.json()},J=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},z=async(e,t,o,s)=>{let r=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(r.ok&&r.body)){t.onError?.({error:`stream_failed_${r.status}`});return}let d=r.body.getReader(),n=new TextDecoder,a="",i=()=>{let u=a.indexOf(`
5
5
 
6
- `);for(;c!==-1;){let b=o.slice(0,c);o=o.slice(c+2);let u=ne(b);u&&ae(u,t,a),c=o.indexOf(`
6
+ `);for(;u!==-1;){let b=a.slice(0,u);a=a.slice(u+2);let p=fe(b);p&&ye(p,t,o),u=a.indexOf(`
7
7
 
8
- `)}},i=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),i()};await i()};var ie=/\/+$/,S="/api/v1/end-user",z=e=>{let t=e.baseUrl.replace(ie,""),a=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),d=async r=>{let o=await fetch(`${t}${S}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:r})});if(!o.ok)throw new Error(`hola: message failed (${o.status})`);return{reply:(await o.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>h(`${t}${S}/chat/info${s("?")}`,await n()),sendMessage:d,streamMessage:async(r,o)=>{try{let l=await d(r);o.onText?.(l.reply),o.onComplete?.(l)}catch(l){o.onError?.(C(l))}},subscribe:(r,o)=>{let l=o?.since??"0",i=!1,c,b=o?.intervalMs??3e3,u=async()=>{try{let g=await h(`${t}${S}/chat/thread?since=${encodeURIComponent(l)}${s("&")}`,await n());for(let m of g.messages)r.onMessage(m);l=g.cursor}catch(g){r.onError?.(C(g))}i||(c=setTimeout(u,b))},p=()=>{i=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{await fetch(`${t}${S}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};var $=require("react"),k=(e,t)=>{let[a,s]=(0,$.useState)("");return(0,$.useEffect)(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([n],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),a};var D=require("react");var M=require("react"),G=require("react/jsx-runtime"),J=(0,M.createContext)(null),q=({client:e,children:t})=>(0,G.jsx)(J.Provider,{value:e,children:t}),A=()=>{let e=(0,M.useContext)(J);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var w=require("react"),U=e=>{let[t,a]=(0,w.useState)([]),[s,n]=(0,w.useState)(""),[d,r]=(0,w.useState)(!1),[o,l]=(0,w.useState)(null),i=(0,w.useRef)(0),c=(0,w.useCallback)(()=>{let u=i.current;return i.current+=1,u},[]);(0,w.useEffect)(()=>{let u=!0;return e.getInfo().then(p=>{u&&p.greeting&&a([{id:c(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,c]);let b=(0,w.useCallback)(()=>{let u=s.trim();if(!u||d)return;n(""),l(null),r(!0);let p=c(),g=c();a(f=>[...f,{id:p,role:"user",text:u},{id:g,role:"assistant",text:"",streaming:!0}]);let m=f=>{a(H=>H.map(_=>_.id===g?f(_):_))};e.streamMessage(u,{onText:f=>m(H=>({...H,text:H.text+f})),onComplete:f=>{m(H=>({...H,text:f.reply||H.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{m(H=>({...H,streaming:!1})),l(f.error),r(!1)}}).catch(f=>{m(H=>({...H,streaming:!1})),l(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,d,e,c]);return{messages:t,input:s,setInput:n,send:b,sending:d,error:o}};var y=require("react/jsx-runtime"),T={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},le=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),a=e.url??t;return a?e.mimeType.startsWith("image/")?(0,y.jsx)("a",{href:a,rel:"noopener noreferrer",target:"_blank",children:(0,y.jsx)("img",{alt:e.filename,src:a,style:T.image})}):(0,y.jsx)("a",{download:e.filename,href:a,style:T.link,children:e.filename}):null},B=()=>{let e=A(),{messages:t,input:a,setInput:s,send:n,sending:d,error:r}=U(e),o=(0,D.useRef)(null);(0,D.useEffect)(()=>{let i=o.current;i&&(i.scrollTop=i.scrollHeight)},[t]);let l=i=>{i.preventDefault(),n()};return(0,y.jsxs)("div",{style:T.root,children:[(0,y.jsx)("div",{ref:o,style:T.log,children:t.map(i=>(0,y.jsxs)("div",{style:i.role==="user"?T.user:T.assistant,children:[i.text||(i.streaming?"\u2026":""),i.artifacts&&i.artifacts.length>0?(0,y.jsx)("div",{style:T.artifacts,children:i.artifacts.map(c=>(0,y.jsx)(le,{artifact:c},c.filename))}):null]},i.id))}),r?(0,y.jsx)("div",{style:T.error,children:r}):null,(0,y.jsxs)("form",{onSubmit:l,style:T.form,children:[(0,y.jsx)("input",{"aria-label":"Message",onChange:i=>s(i.target.value),placeholder:"Type a message\u2026",style:T.input,value:a}),(0,y.jsx)("button",{disabled:d||a.trim().length===0,style:T.button,type:"submit",children:"Send"})]})]})};var ce=/\/+$/,O=e=>{let t=e.sessionId,a=e.baseUrl.replace(ce,""),s=r=>`${a}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},d=(r,o)=>({url:s(r),headers:n,body:JSON.stringify({message:o,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>h(s("/config"),{}),sendMessage:async r=>{let o=await j(d("/message",r));return t=o.sessionId,o},streamMessage:(r,o,l)=>N(d("/message/stream",r),o,i=>{t=i},l),subscribe:(r,o)=>{let l=o?.since??"0",i=!1,c,b=o?.intervalMs??3e3,u=async()=>{let g=t;if(g)try{let m=await h(s(`/messages?session=${encodeURIComponent(g)}&since=${encodeURIComponent(l)}`),{});for(let f of m.messages)r.onMessage(f);l=m.cursor}catch(m){r.onError?.(C(m))}i||(c=setTimeout(u,b))},p=()=>{i=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var K=e=>"transport"in e?e.transport:O(e);var de=/\/+$/,P="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function pe(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var V=e=>{let t=e.baseUrl.replace(de,""),a=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),n=async(r,o)=>{try{let l=await fetch(`${t}${P}${r}`,{method:o.method,headers:await s(),...o.body===void 0?{}:{body:JSON.stringify(o.body)}});if(l.status===409){let i=await l.json().catch(()=>({}));throw new v(i.message??"Someone else saved this first.")}if(!l.ok){let i=await l.text().catch(()=>""),c=`${o.method} ${r} \u2192 ${String(l.status)}`;try{let b=JSON.parse(i);b.message&&(c=b.message)}catch{c=i?`${c}: ${i}`:c}throw C(new Error(c))}return await l.json()}catch(l){throw C(l)}},d=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await h(`${t}${P}/tables`,await s())).tables,listRows:async(r,o)=>await h(`${t}${P}${d(r)}/rows?limit=${String(o??100)}`,await s()),insertRow:async(r,o)=>{let{rowIds:l}=await n(`${d(r)}/rows`,{method:"POST",body:{rows:[o]}}),i=l[0];if(!i)throw C(new Error("The row was not created."));return i},patchRow:async(r,o,l)=>(await n(`${d(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,o)=>(await n(`/${encodeURIComponent(r)}/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await h(`${t}${P}/tables/files/rows?limit=200`,await s())).rows.map(o=>({id:o.rowId,filename:String(o.data.filename??""),source:String(o.data.source??"client"),campaign:String(o.data.campaign??""),size:typeof o.data.size=="number"?o.data.size:0,createdAt:o.createdAt})),uploadFile:async(r,o)=>{let l=new Uint8Array(await r.arrayBuffer());return await n("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:pe(l),...o===void 0?{}:{message:o}}})},fileLink:async r=>await h(`${t}${P}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${P}/documents`,await s())).documents,readDocument:async r=>(await h(`${t}${P}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,o)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:o})}};var x=require("react"),Y="greeting";function L(e,t){if(e.some(n=>n.id===t.id))return e;let a=e.filter(n=>n.id!==Y);return[...t.author==="client"?a.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):a,t]}var Q=(e,t)=>{let[a,s]=(0,x.useState)([]),[n,d]=(0,x.useState)(""),[r,o]=(0,x.useState)(!1),[l,i]=(0,x.useState)(null),c=(0,x.useRef)(0),b=t?.pollMs??2e3;(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(g=>{p&&g.greeting&&s(m=>m.length===0?[{id:Y,cursor:"",author:"agent",text:g.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),(0,x.useEffect)(()=>e.subscribe?.({onMessage:p=>s(g=>L(g,p)),onError:p=>i(p.error)},{intervalMs:b}),[e,b]);let u=(0,x.useCallback)(()=>{let p=n.trim();if(!p||r)return;d(""),i(null),o(!0),c.current+=1;let g={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};s(m=>[...m,g]),e.sendMessage(p).then(()=>o(!1)).catch(m=>{i(m instanceof Error?m.message:"send_failed"),o(!1)})},[n,r,e]);return{messages:a,input:n,setInput:d,send:u,sending:r,error:l}};0&&(module.exports={Chat,HolaProvider,PortalConflict,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,mergeMessage,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
8
+ `)}},l=async()=>{let u=await d.read();if(!u.done)return a+=n.decode(u.value,{stream:!0}),i(),l()};await l()};var be=/\/+$/,E="/api/v1/end-user",q=e=>{let t=e.baseUrl.replace(be,""),o=()=>typeof e.token=="function"?e.token():e.token,s=n=>e.project?`${n}project=${encodeURIComponent(e.project)}`:"",r=async()=>({"content-type":"application/json",authorization:`Bearer ${await o()}`}),d=async n=>{let a=await fetch(`${t}${E}/chat${s("?")}`,{method:"POST",headers:await r(),body:JSON.stringify({message:n})});if(!a.ok)throw new Error(`hola: message failed (${a.status})`);return{reply:(await a.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>h(`${t}${E}/chat/info${s("?")}`,await r()),sendMessage:d,streamMessage:async(n,a)=>{try{let i=await d(n);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(k(i))}},subscribe:(n,a)=>{let i=a?.since??"0",l=!1,u,b=a?.intervalMs??3e3,p=async()=>{try{let m=await h(`${t}${E}/chat/thread?since=${encodeURIComponent(i)}${s("&")}`,await r());for(let g of m.messages)n.onMessage(g);i=m.cursor,n.onHealthy?.()}catch(m){n.onError?.(k(m))}l||(u=setTimeout(p,b))},c=()=>{l=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",c),u=setTimeout(p,0),c},requestHuman:async()=>{await fetch(`${t}${E}/chat/request-human${s("?")}`,{method:"POST",headers:await r(),body:"{}"})}}};var $=require("react"),A=(e,t)=>{let[o,s]=(0,$.useState)("");return(0,$.useEffect)(()=>{if(!e){s("");return}let r=Uint8Array.from(atob(e),n=>n.charCodeAt(0)),d=URL.createObjectURL(new Blob([r],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),o};var U=require("react");var W=require("react"),Q=require("react/jsx-runtime"),V=(0,W.createContext)(null),Y=({client:e,children:t})=>(0,Q.jsx)(V.Provider,{value:e,children:t}),M=()=>{let e=(0,W.useContext)(V);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var x=require("react"),D=e=>{let[t,o]=(0,x.useState)([]),[s,r]=(0,x.useState)(""),[d,n]=(0,x.useState)(!1),[a,i]=(0,x.useState)(null),l=(0,x.useRef)(0),u=(0,x.useCallback)(()=>{let p=l.current;return l.current+=1,p},[]);(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(c=>{p&&c.greeting&&o([{id:u(),role:"assistant",text:c.greeting}])}).catch(()=>{}),()=>{p=!1}},[e,u]);let b=(0,x.useCallback)(()=>{let p=s.trim();if(!p||d)return;r(""),i(null),n(!0);let c=u(),m=u();o(f=>[...f,{id:c,role:"user",text:p},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{o(T=>T.map(F=>F.id===m?f(F):F))};e.streamMessage(p,{onText:f=>g(T=>({...T,text:T.text+f})),onComplete:f=>{g(T=>({...T,text:f.reply||T.text,artifacts:f.artifacts,streaming:!1})),n(!1)},onError:f=>{g(T=>({...T,streaming:!1})),i(f.error),n(!1)}}).catch(f=>{g(T=>({...T,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),n(!1)})},[s,d,e,u]);return{messages:t,input:s,setInput:r,send:b,sending:d,error:a}};var y=require("react/jsx-runtime"),P={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},he=({artifact:e})=>{let t=A(e.dataBase64??"",e.mimeType),o=e.url??t;return o?e.mimeType.startsWith("image/")?(0,y.jsx)("a",{href:o,rel:"noopener noreferrer",target:"_blank",children:(0,y.jsx)("img",{alt:e.filename,src:o,style:P.image})}):(0,y.jsx)("a",{download:e.filename,href:o,style:P.link,children:e.filename}):null},X=()=>{let e=M(),{messages:t,input:o,setInput:s,send:r,sending:d,error:n}=D(e),a=(0,U.useRef)(null);(0,U.useEffect)(()=>{let l=a.current;l&&(l.scrollTop=l.scrollHeight)},[t]);let i=l=>{l.preventDefault(),r()};return(0,y.jsxs)("div",{style:P.root,children:[(0,y.jsx)("div",{ref:a,style:P.log,children:t.map(l=>(0,y.jsxs)("div",{style:l.role==="user"?P.user:P.assistant,children:[l.text||(l.streaming?"\u2026":""),l.artifacts&&l.artifacts.length>0?(0,y.jsx)("div",{style:P.artifacts,children:l.artifacts.map(u=>(0,y.jsx)(he,{artifact:u},u.filename))}):null]},l.id))}),n?(0,y.jsx)("div",{style:P.error,children:n}):null,(0,y.jsxs)("form",{onSubmit:i,style:P.form,children:[(0,y.jsx)("input",{"aria-label":"Message",onChange:l=>s(l.target.value),placeholder:"Type a message\u2026",style:P.input,value:o}),(0,y.jsx)("button",{disabled:d||o.trim().length===0,style:P.button,type:"submit",children:"Send"})]})]})};var xe=/\/+$/,O=e=>{let t=e.sessionId,o=e.baseUrl.replace(xe,""),s=n=>`${o}/endpoint/${encodeURIComponent(e.token)}${n}`,r={"content-type":"application/json"},d=(n,a)=>({url:s(n),headers:r,body:JSON.stringify({message:a,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>h(s("/config"),{}),sendMessage:async n=>{let a=await J(d("/message",n));return t=a.sessionId,a},streamMessage:(n,a,i)=>z(d("/message/stream",n),a,l=>{t=l},i),subscribe:(n,a)=>{let i=a?.since??"0",l=!1,u,b=a?.intervalMs??3e3,p=async()=>{let m=t;if(m)try{let g=await h(s(`/messages?session=${encodeURIComponent(m)}&since=${encodeURIComponent(i)}`),{});for(let f of g.messages)n.onMessage(f);i=g.cursor,n.onHealthy?.()}catch(g){n.onError?.(k(g))}l||(u=setTimeout(p,b))},c=()=>{l=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",c),u=setTimeout(p,0),c},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:r,body:JSON.stringify({sessionId:t})})}}};var Z=e=>"transport"in e?e.transport:O(e);var we=e=>typeof e=="number"?e:Number(e)||0,K=e=>/rate|position|rank|score|percent|avg/i.test(e),Te=/posted|published|sent|completed|shipped|launched|_at|date/i,Pe=new Set(["service","order","end_user_id"]);function N(e,t){let o=e.filter(i=>!Pe.has(i.key)),s=o.filter(i=>i.type==="number").slice(0,3),r=o.find(i=>i.key==="status")??o.find(i=>i.type==="select"),d=o.find(i=>i.type==="date"&&Te.test(i.key))??o.find(i=>i.type==="date"),n=o.filter(i=>i.type==="number"&&!K(i.key)).map(i=>({field:i,total:t.reduce((l,u)=>l+we(u.data[i.key]),0)})).sort((i,l)=>l.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of s){let l=K(i.key);a.push({type:"stat",metric:i.key,agg:l?"avg":"sum",label:l?`Avg ${i.label.toLowerCase()}`:i.label})}return d&&a.push({type:"line",x:d.key,y:n?.key,agg:"sum",title:n?`${n.label} by day`:"Items by day"}),r&&a.push({type:"donut",groupBy:r.key,title:r.label}),a.push({type:"table",columns:o.map(i=>i.key)}),{version:1,widgets:a}}var ee=new Set(["service","order","end_user_id"]),te=e=>typeof e=="number"?e:Number(e)||0,re=e=>e==null?"":String(e);function ne(e,t){if(e==="count")return t.length;if(t.length===0)return 0;if(e==="last")return t[t.length-1]??0;if(e==="min")return Math.min(...t);if(e==="max")return Math.max(...t);let o=t.reduce((s,r)=>s+r,0);return e==="avg"?Math.round(o/t.length):o}function He(e,t,o,s){let r=new Map;for(let d of e){let n=new Date(re(d.data[t]));if(Number.isNaN(n.getTime()))continue;let a=n.toISOString().slice(0,10),i=r.get(a)??[];i.push(o?te(d.data[o]):1),r.set(a,i)}return[...r.entries()].sort((d,n)=>d[0]<n[0]?-1:1).map(([d,n])=>({label:new Date(`${d}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:o?ne(s,n):n.length}))}function ke(e,t){let o=new Map;for(let s of e){let r=re(s.data[t]).trim();r&&o.set(r,(o.get(r)??0)+1)}return[...o.entries()].sort((s,r)=>r[1]-s[1]).map(([s,r])=>({label:s,value:r}))}function Ce(e,t,o,s){switch(e.type){case"stat":{let r=e.metric?t.get(e.metric):void 0;if(e.metric&&!r)return null;if(!r)return{kind:"stat",label:e.label??"Items",value:s.length};let d=s.map(n=>te(n.data[r.key]));return{kind:"stat",label:e.label??r.label,value:ne(e.agg??"sum",d)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let r=He(s,e.x,e.y,e.agg??"sum");return r.length<2?null:{kind:"series",draw:e.type,title:e.title??"By day",points:r}}case"donut":{if(!t.has(e.groupBy))return null;let r=ke(s,e.groupBy);return r.length===0?null:{kind:"donut",title:e.title??t.get(e.groupBy)?.label??"Breakdown",points:r}}case"table":{let r=e.columns?e.columns.map(d=>t.get(d)).filter(d=>d!==void 0&&!ee.has(d.key)):o;return r.length===0?null:{kind:"table",title:e.title,columns:r,rows:s}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function L(e,t,o){let s=t.filter(n=>!ee.has(n.key)),r=new Map(t.map(n=>[n.key,n])),d=[];for(let n of e.widgets){let a=Ce(n,r,s,o);a&&d.push(a)}return d}var Se=new Set(["sum","avg","count","min","max","last"]),oe=e=>typeof e=="string"&&Se.has(e)?e:void 0,H=e=>typeof e=="string"&&e!==""?e:void 0;function ve(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:H(t.label),metric:H(t.metric),agg:oe(t.agg)};case"line":case"bar":{let o=H(t.x);return o?{type:t.type,title:H(t.title),x:o,y:H(t.y),agg:oe(t.agg)}:null}case"donut":{let o=H(t.groupBy);return o?{type:"donut",title:H(t.title),groupBy:o}:null}case"table":{let o=Array.isArray(t.columns)?t.columns.filter(s=>typeof s=="string"):void 0;return{type:"table",title:H(t.title),columns:o}}case"text":{let o=H(t.body);return o?{type:"text",title:H(t.title),body:o}:null}default:return null}}function j(e){let t=e;if(typeof e=="string"){if(e.trim()==="")return null;try{t=JSON.parse(e)}catch{return null}}if(typeof t!="object"||t===null)return null;let o=t.widgets;if(!Array.isArray(o))return null;let s=o.map(ve).filter(d=>d!==null);if(s.length===0)return null;let r=t.version;return{version:typeof r=="number"?r:1,title:H(t.title),widgets:s}}var Re=/\/+$/,C="/api/v1/end-user",S=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function Ie(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var se=e=>{let t=e.baseUrl.replace(Re,""),o=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await o()}`}),r=async(n,a)=>{try{let i=await fetch(`${t}${C}${n}`,{method:a.method,headers:await s(),...a.body===void 0?{}:{body:JSON.stringify(a.body)}});if(i.status===409){let l=await i.json().catch(()=>({}));throw new S(l.message??"Someone else saved this first.")}if(!i.ok){let l=await i.text().catch(()=>""),u=`${a.method} ${n} \u2192 ${String(i.status)}`;try{let b=JSON.parse(l);b.message&&(u=b.message)}catch{u=l?`${u}: ${l}`:u}throw k(new Error(u))}return await i.json()}catch(i){throw k(i)}},d=n=>`/tables/${encodeURIComponent(n)}`;return{listTables:async()=>(await h(`${t}${C}/tables`,await s())).tables,listRows:async(n,a)=>await h(`${t}${C}${d(n)}/rows?limit=${String(a??100)}`,await s()),insertRow:async(n,a)=>{let{rowIds:i}=await r(`${d(n)}/rows`,{method:"POST",body:{rows:[a]}}),l=i[0];if(!l)throw k(new Error("The row was not created."));return l},patchRow:async(n,a,i)=>(await r(`${d(n)}/rows/${encodeURIComponent(a)}`,{method:"PATCH",body:i})).updated,deleteRow:async(n,a)=>(await r(`/${encodeURIComponent(n)}/${encodeURIComponent(a)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await h(`${t}${C}/tables/files/rows?limit=200`,await s())).rows.map(a=>({id:a.rowId,filename:String(a.data.filename??""),source:String(a.data.source??"client"),campaign:String(a.data.campaign??""),size:typeof a.data.size=="number"?a.data.size:0,createdAt:a.createdAt})),uploadFile:async(n,a)=>{let i=new Uint8Array(await n.arrayBuffer());return await r("/files",{method:"POST",body:{filename:n.name,mime:n.type||"application/octet-stream",data:Ie(i),...a===void 0?{}:{message:a}}})},fileLink:async n=>await h(`${t}${C}/files/${encodeURIComponent(n)}/url`,await s()),deleteFile:async n=>(await r(`/files/${encodeURIComponent(n)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${C}/documents`,await s())).documents,readDocument:async n=>(await h(`${t}${C}/documents/${encodeURIComponent(n)}`,await s())).document,saveDocument:async(n,a)=>await r(`/documents/${encodeURIComponent(n)}`,{method:"PATCH",body:a})}};var w=require("react"),ae="greeting";function B(e,t){if(e.some(r=>r.id===t.id))return e;let o=e.filter(r=>r.id!==ae);return[...t.author==="client"?o.filter(r=>!(r.pending&&r.author==="client"&&r.text===t.text)):o,t]}var ie=(e,t)=>{let[o,s]=(0,w.useState)([]),[r,d]=(0,w.useState)(""),[n,a]=(0,w.useState)(!1),[i,l]=(0,w.useState)(null),u=(0,w.useRef)(0),b=t?.pollMs??2e3;(0,w.useEffect)(()=>{let c=!0;return e.getInfo().then(m=>{c&&m.greeting&&s(g=>g.length===0?[{id:ae,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{c=!1}},[e]),(0,w.useEffect)(()=>e.subscribe?.({onMessage:c=>s(m=>B(m,c)),onError:c=>l(c.error),onHealthy:()=>l(null)},{intervalMs:b}),[e,b]);let p=(0,w.useCallback)(()=>{let c=r.trim();if(!c||n)return;d(""),l(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:c,pending:!0};s(g=>[...g,m]),e.sendMessage(c).then(()=>a(!1)).catch(g=>{l(g instanceof Error?g.message:"send_failed"),a(!1)})},[r,n,e]);return{messages:o,input:r,setInput:d,send:p,sending:n,error:i}};0&&(module.exports={Chat,HolaProvider,PortalConflict,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,inferSpec,mergeMessage,parseDashboardSpec,renderModel,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
package/dist/index.d.cts CHANGED
@@ -61,6 +61,9 @@ type HolaMessage = {
61
61
  type HolaSubscribeHandlers = {
62
62
  onMessage: (message: HolaMessage) => void;
63
63
  onError?: (error: HolaError) => void;
64
+ /** A poll completed cleanly. Fires every successful cycle (even with no new messages) so a
65
+ * consumer can clear a stale error — the receive channel retries, so one blip is not an outage. */
66
+ onHealthy?: () => void;
64
67
  };
65
68
  type HolaSubscribeOptions = {
66
69
  /** Resume from this cursor; omit to read the thread from the start. */
@@ -158,13 +161,6 @@ type CreateHolaClientOptions = WidgetTransportConfig | {
158
161
  * agnostic — the current transport is an internal detail, not the SDK's identity. */
159
162
  declare const createHolaClient: (options: CreateHolaClientOptions) => HolaClient;
160
163
 
161
- /** Extract a streamed text delta from a raw pi turn event, or null. */
162
- declare const textDeltaOf: (event: unknown) => string | null;
163
- /** Extract a streamed reasoning delta from a raw pi turn event, or null. */
164
- declare const thinkingDeltaOf: (event: unknown) => string | null;
165
- /** Extract a tool-execution start/end from a raw pi turn event, or null. */
166
- declare const toolEventOf: (event: unknown) => HolaToolEvent | null;
167
-
168
164
  /** A column, as the company defined it. */
169
165
  type PortalField = {
170
166
  key: string;
@@ -294,6 +290,96 @@ type PortalData = {
294
290
  };
295
291
  declare const createPortalData: (config: ApiTransportConfig) => PortalData;
296
292
 
293
+ /** How a metric column is reduced to a single number. */
294
+ type Agg = "sum" | "avg" | "count" | "min" | "max" | "last";
295
+ type WidgetType = "stat" | "line" | "bar" | "donut" | "table" | "text";
296
+ /** A headline number: one metric reduced by `agg` (omit `metric` to count the rows). */
297
+ interface StatWidget {
298
+ type: "stat";
299
+ label?: string | undefined;
300
+ metric?: string | undefined;
301
+ agg?: Agg | undefined;
302
+ }
303
+ /** A time series: `y` (a numeric column) aggregated per day of `x` (a date column); omit `y` to count
304
+ * rows per day. `line` and `bar` differ only in how the renderer draws the same points. */
305
+ interface SeriesWidget {
306
+ type: "line" | "bar";
307
+ title?: string | undefined;
308
+ x: string;
309
+ y?: string | undefined;
310
+ agg?: Agg | undefined;
311
+ }
312
+ /** A breakdown: the count of rows per distinct value of `groupBy`. */
313
+ interface DonutWidget {
314
+ type: "donut";
315
+ title?: string | undefined;
316
+ groupBy: string;
317
+ }
318
+ /** A row table: `columns` (by key, in order) or every non-plumbing column when omitted. */
319
+ interface TableWidget {
320
+ type: "table";
321
+ title?: string | undefined;
322
+ columns?: string[] | undefined;
323
+ }
324
+ /** Static prose — a note above or between the data widgets. */
325
+ interface TextWidget {
326
+ type: "text";
327
+ title?: string | undefined;
328
+ body: string;
329
+ }
330
+ type Widget = StatWidget | SeriesWidget | DonutWidget | TableWidget | TextWidget;
331
+ interface DashboardSpec {
332
+ version: number;
333
+ title?: string | undefined;
334
+ widgets: Widget[];
335
+ }
336
+ /** Parse a service's stored dashboard spec (a JSON string or an already-parsed object). Returns null
337
+ * for anything that isn't a usable spec — empty, absent, garbled, or with no valid widgets — so the
338
+ * caller falls back to inference instead of showing a blank dashboard. */
339
+ declare function parseDashboardSpec(raw: unknown): DashboardSpec | null;
340
+
341
+ declare function inferSpec(fields: PortalField[], rows: PortalRow[]): DashboardSpec;
342
+
343
+ /** A point on a line/bar chart or a slice of a donut. */
344
+ interface Point {
345
+ label: string;
346
+ value: number;
347
+ }
348
+ /** A widget resolved against the data — everything a renderer needs, already computed. */
349
+ type ResolvedWidget = {
350
+ kind: "stat";
351
+ label: string;
352
+ value: number;
353
+ } | {
354
+ kind: "series";
355
+ draw: "line" | "bar";
356
+ title: string;
357
+ points: Point[];
358
+ } | {
359
+ kind: "donut";
360
+ title: string;
361
+ points: Point[];
362
+ } | {
363
+ kind: "table";
364
+ title?: string | undefined;
365
+ columns: PortalField[];
366
+ rows: PortalRow[];
367
+ } | {
368
+ kind: "text";
369
+ title?: string | undefined;
370
+ body: string;
371
+ };
372
+ /** Resolve a whole spec against the data. The result is render-ready and free of widgets that would
373
+ * draw nothing. */
374
+ declare function renderModel(spec: DashboardSpec, fields: PortalField[], rows: PortalRow[]): ResolvedWidget[];
375
+
376
+ /** Extract a streamed text delta from a raw pi turn event, or null. */
377
+ declare const textDeltaOf: (event: unknown) => string | null;
378
+ /** Extract a streamed reasoning delta from a raw pi turn event, or null. */
379
+ declare const thinkingDeltaOf: (event: unknown) => string | null;
380
+ /** Extract a tool-execution start/end from a raw pi turn event, or null. */
381
+ declare const toolEventOf: (event: unknown) => HolaToolEvent | null;
382
+
297
383
  /** Provides a `HolaClient` (from `createHolaClient`) to `useChat` / `<Chat/>`.
298
384
  * Create the client once (e.g. `useMemo`) so its session persists across renders. */
299
385
  declare const HolaProvider: ({ client, children, }: {
@@ -343,4 +429,4 @@ declare const useThread: (client: HolaClient, options?: {
343
429
  pollMs?: number;
344
430
  }) => UseThread;
345
431
 
346
- export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
432
+ export { type Agg, type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type Point, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ResolvedWidget, type SeriesWidget, type StatWidget, type TableWidget, type TextWidget, type ThreadMessage, type UseChat, type UseThread, type Widget, type WidgetTransportConfig, type WidgetType, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, inferSpec, mergeMessage, parseDashboardSpec, renderModel, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
package/dist/index.d.ts CHANGED
@@ -61,6 +61,9 @@ type HolaMessage = {
61
61
  type HolaSubscribeHandlers = {
62
62
  onMessage: (message: HolaMessage) => void;
63
63
  onError?: (error: HolaError) => void;
64
+ /** A poll completed cleanly. Fires every successful cycle (even with no new messages) so a
65
+ * consumer can clear a stale error — the receive channel retries, so one blip is not an outage. */
66
+ onHealthy?: () => void;
64
67
  };
65
68
  type HolaSubscribeOptions = {
66
69
  /** Resume from this cursor; omit to read the thread from the start. */
@@ -158,13 +161,6 @@ type CreateHolaClientOptions = WidgetTransportConfig | {
158
161
  * agnostic — the current transport is an internal detail, not the SDK's identity. */
159
162
  declare const createHolaClient: (options: CreateHolaClientOptions) => HolaClient;
160
163
 
161
- /** Extract a streamed text delta from a raw pi turn event, or null. */
162
- declare const textDeltaOf: (event: unknown) => string | null;
163
- /** Extract a streamed reasoning delta from a raw pi turn event, or null. */
164
- declare const thinkingDeltaOf: (event: unknown) => string | null;
165
- /** Extract a tool-execution start/end from a raw pi turn event, or null. */
166
- declare const toolEventOf: (event: unknown) => HolaToolEvent | null;
167
-
168
164
  /** A column, as the company defined it. */
169
165
  type PortalField = {
170
166
  key: string;
@@ -294,6 +290,96 @@ type PortalData = {
294
290
  };
295
291
  declare const createPortalData: (config: ApiTransportConfig) => PortalData;
296
292
 
293
+ /** How a metric column is reduced to a single number. */
294
+ type Agg = "sum" | "avg" | "count" | "min" | "max" | "last";
295
+ type WidgetType = "stat" | "line" | "bar" | "donut" | "table" | "text";
296
+ /** A headline number: one metric reduced by `agg` (omit `metric` to count the rows). */
297
+ interface StatWidget {
298
+ type: "stat";
299
+ label?: string | undefined;
300
+ metric?: string | undefined;
301
+ agg?: Agg | undefined;
302
+ }
303
+ /** A time series: `y` (a numeric column) aggregated per day of `x` (a date column); omit `y` to count
304
+ * rows per day. `line` and `bar` differ only in how the renderer draws the same points. */
305
+ interface SeriesWidget {
306
+ type: "line" | "bar";
307
+ title?: string | undefined;
308
+ x: string;
309
+ y?: string | undefined;
310
+ agg?: Agg | undefined;
311
+ }
312
+ /** A breakdown: the count of rows per distinct value of `groupBy`. */
313
+ interface DonutWidget {
314
+ type: "donut";
315
+ title?: string | undefined;
316
+ groupBy: string;
317
+ }
318
+ /** A row table: `columns` (by key, in order) or every non-plumbing column when omitted. */
319
+ interface TableWidget {
320
+ type: "table";
321
+ title?: string | undefined;
322
+ columns?: string[] | undefined;
323
+ }
324
+ /** Static prose — a note above or between the data widgets. */
325
+ interface TextWidget {
326
+ type: "text";
327
+ title?: string | undefined;
328
+ body: string;
329
+ }
330
+ type Widget = StatWidget | SeriesWidget | DonutWidget | TableWidget | TextWidget;
331
+ interface DashboardSpec {
332
+ version: number;
333
+ title?: string | undefined;
334
+ widgets: Widget[];
335
+ }
336
+ /** Parse a service's stored dashboard spec (a JSON string or an already-parsed object). Returns null
337
+ * for anything that isn't a usable spec — empty, absent, garbled, or with no valid widgets — so the
338
+ * caller falls back to inference instead of showing a blank dashboard. */
339
+ declare function parseDashboardSpec(raw: unknown): DashboardSpec | null;
340
+
341
+ declare function inferSpec(fields: PortalField[], rows: PortalRow[]): DashboardSpec;
342
+
343
+ /** A point on a line/bar chart or a slice of a donut. */
344
+ interface Point {
345
+ label: string;
346
+ value: number;
347
+ }
348
+ /** A widget resolved against the data — everything a renderer needs, already computed. */
349
+ type ResolvedWidget = {
350
+ kind: "stat";
351
+ label: string;
352
+ value: number;
353
+ } | {
354
+ kind: "series";
355
+ draw: "line" | "bar";
356
+ title: string;
357
+ points: Point[];
358
+ } | {
359
+ kind: "donut";
360
+ title: string;
361
+ points: Point[];
362
+ } | {
363
+ kind: "table";
364
+ title?: string | undefined;
365
+ columns: PortalField[];
366
+ rows: PortalRow[];
367
+ } | {
368
+ kind: "text";
369
+ title?: string | undefined;
370
+ body: string;
371
+ };
372
+ /** Resolve a whole spec against the data. The result is render-ready and free of widgets that would
373
+ * draw nothing. */
374
+ declare function renderModel(spec: DashboardSpec, fields: PortalField[], rows: PortalRow[]): ResolvedWidget[];
375
+
376
+ /** Extract a streamed text delta from a raw pi turn event, or null. */
377
+ declare const textDeltaOf: (event: unknown) => string | null;
378
+ /** Extract a streamed reasoning delta from a raw pi turn event, or null. */
379
+ declare const thinkingDeltaOf: (event: unknown) => string | null;
380
+ /** Extract a tool-execution start/end from a raw pi turn event, or null. */
381
+ declare const toolEventOf: (event: unknown) => HolaToolEvent | null;
382
+
297
383
  /** Provides a `HolaClient` (from `createHolaClient`) to `useChat` / `<Chat/>`.
298
384
  * Create the client once (e.g. `useMemo`) so its session persists across renders. */
299
385
  declare const HolaProvider: ({ client, children, }: {
@@ -343,4 +429,4 @@ declare const useThread: (client: HolaClient, options?: {
343
429
  pollMs?: number;
344
430
  }) => UseThread;
345
431
 
346
- export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
432
+ export { type Agg, type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type Point, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ResolvedWidget, type SeriesWidget, type StatWidget, type TableWidget, type TextWidget, type ThreadMessage, type UseChat, type UseThread, type Widget, type WidgetTransportConfig, type WidgetType, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, inferSpec, mergeMessage, parseDashboardSpec, renderModel, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client'
2
- var O=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},E=e=>{let t=O(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},S=e=>{let t=O(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},$=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var J=/^ /,x=e=>{if(e&&typeof e=="object"){let t=e,i=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:i,retryable:t.retryable}:{error:i}}return{error:"request_failed"}},q=e=>{let t="message",i=[];for(let s of e.split(`
3
- `))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&i.push(s.slice(5).replace(J,"")));return i.length>0?{event:t,data:i.join(`
4
- `)}:null},G=(e,t,i)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=s.sessionId;n&&(i(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(s);let n=E(s);n&&t.onText?.(n);let d=S(s);d&&t.onThinking?.(d);let r=$(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=s;i(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(x(s))},b=async(e,t)=>{let i=await fetch(e,{headers:t});if(!i.ok)throw new Error(`hola: request failed (${i.status})`);return await i.json()},_=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},F=async(e,t,i,s)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let d=n.body.getReader(),r=new TextDecoder,o="",l=()=>{let c=o.indexOf(`
2
+ var O=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},I=e=>{let t=O(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{let t=O(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},$=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var ee=/^ /,T=e=>{if(e&&typeof e=="object"){let t=e,o=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:o,retryable:t.retryable}:{error:o}}return{error:"request_failed"}},te=e=>{let t="message",o=[];for(let s of e.split(`
3
+ `))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&o.push(s.slice(5).replace(ee,"")));return o.length>0?{event:t,data:o.join(`
4
+ `)}:null},re=(e,t,o)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=s.sessionId;n&&(o(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(s);let n=I(s);n&&t.onText?.(n);let d=E(s);d&&t.onThinking?.(d);let r=$(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=s;o(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(T(s))},b=async(e,t)=>{let o=await fetch(e,{headers:t});if(!o.ok)throw new Error(`hola: request failed (${o.status})`);return await o.json()},F=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},_=async(e,t,o,s)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let d=n.body.getReader(),r=new TextDecoder,a="",i=()=>{let u=a.indexOf(`
5
5
 
6
- `);for(;c!==-1;){let y=o.slice(0,c);o=o.slice(c+2);let u=q(y);u&&G(u,t,i),c=o.indexOf(`
6
+ `);for(;u!==-1;){let y=a.slice(0,u);a=a.slice(u+2);let p=te(y);p&&re(p,t,o),u=a.indexOf(`
7
7
 
8
- `)}},a=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),a()};await a()};var B=/\/+$/,C="/api/v1/end-user",K=e=>{let t=e.baseUrl.replace(B,""),i=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await i()}`}),d=async r=>{let o=await fetch(`${t}${C}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:r})});if(!o.ok)throw new Error(`hola: message failed (${o.status})`);return{reply:(await o.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${C}/chat/info${s("?")}`,await n()),sendMessage:d,streamMessage:async(r,o)=>{try{let l=await d(r);o.onText?.(l.reply),o.onComplete?.(l)}catch(l){o.onError?.(x(l))}},subscribe:(r,o)=>{let l=o?.since??"0",a=!1,c,y=o?.intervalMs??3e3,u=async()=>{try{let g=await b(`${t}${C}/chat/thread?since=${encodeURIComponent(l)}${s("&")}`,await n());for(let m of g.messages)r.onMessage(m);l=g.cursor}catch(g){r.onError?.(x(g))}a||(c=setTimeout(u,y))},p=()=>{a=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{await fetch(`${t}${C}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};import{useEffect as V,useState as Y}from"react";var k=(e,t)=>{let[i,s]=Y("");return V(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([n],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),i};import{useEffect as oe,useRef as se}from"react";import{createContext as Q,useContext as X}from"react";import{jsx as ee}from"react/jsx-runtime";var L=Q(null),Z=({client:e,children:t})=>ee(L.Provider,{value:e,children:t}),M=()=>{let e=X(L);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as W,useEffect as te,useRef as re,useState as P}from"react";var A=e=>{let[t,i]=P([]),[s,n]=P(""),[d,r]=P(!1),[o,l]=P(null),a=re(0),c=W(()=>{let u=a.current;return a.current+=1,u},[]);te(()=>{let u=!0;return e.getInfo().then(p=>{u&&p.greeting&&i([{id:c(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,c]);let y=W(()=>{let u=s.trim();if(!u||d)return;n(""),l(null),r(!0);let p=c(),g=c();i(f=>[...f,{id:p,role:"user",text:u},{id:g,role:"assistant",text:"",streaming:!0}]);let m=f=>{i(h=>h.map(R=>R.id===g?f(R):R))};e.streamMessage(u,{onText:f=>m(h=>({...h,text:h.text+f})),onComplete:f=>{m(h=>({...h,text:f.reply||h.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{m(h=>({...h,streaming:!1})),l(f.error),r(!1)}}).catch(f=>{m(h=>({...h,streaming:!1})),l(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,d,e,c]);return{messages:t,input:s,setInput:n,send:y,sending:d,error:o}};import{jsx as H,jsxs as U}from"react/jsx-runtime";var w={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},ne=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),i=e.url??t;return i?e.mimeType.startsWith("image/")?H("a",{href:i,rel:"noopener noreferrer",target:"_blank",children:H("img",{alt:e.filename,src:i,style:w.image})}):H("a",{download:e.filename,href:i,style:w.link,children:e.filename}):null},ae=()=>{let e=M(),{messages:t,input:i,setInput:s,send:n,sending:d,error:r}=A(e),o=se(null);oe(()=>{let a=o.current;a&&(a.scrollTop=a.scrollHeight)},[t]);let l=a=>{a.preventDefault(),n()};return U("div",{style:w.root,children:[H("div",{ref:o,style:w.log,children:t.map(a=>U("div",{style:a.role==="user"?w.user:w.assistant,children:[a.text||(a.streaming?"\u2026":""),a.artifacts&&a.artifacts.length>0?H("div",{style:w.artifacts,children:a.artifacts.map(c=>H(ne,{artifact:c},c.filename))}):null]},a.id))}),r?H("div",{style:w.error,children:r}):null,U("form",{onSubmit:l,style:w.form,children:[H("input",{"aria-label":"Message",onChange:a=>s(a.target.value),placeholder:"Type a message\u2026",style:w.input,value:i}),H("button",{disabled:d||i.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var ie=/\/+$/,D=e=>{let t=e.sessionId,i=e.baseUrl.replace(ie,""),s=r=>`${i}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},d=(r,o)=>({url:s(r),headers:n,body:JSON.stringify({message:o,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>b(s("/config"),{}),sendMessage:async r=>{let o=await _(d("/message",r));return t=o.sessionId,o},streamMessage:(r,o,l)=>F(d("/message/stream",r),o,a=>{t=a},l),subscribe:(r,o)=>{let l=o?.since??"0",a=!1,c,y=o?.intervalMs??3e3,u=async()=>{let g=t;if(g)try{let m=await b(s(`/messages?session=${encodeURIComponent(g)}&since=${encodeURIComponent(l)}`),{});for(let f of m.messages)r.onMessage(f);l=m.cursor}catch(m){r.onError?.(x(m))}a||(c=setTimeout(u,y))},p=()=>{a=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var le=e=>"transport"in e?e.transport:D(e);var ce=/\/+$/,T="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function de(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var pe=e=>{let t=e.baseUrl.replace(ce,""),i=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await i()}`}),n=async(r,o)=>{try{let l=await fetch(`${t}${T}${r}`,{method:o.method,headers:await s(),...o.body===void 0?{}:{body:JSON.stringify(o.body)}});if(l.status===409){let a=await l.json().catch(()=>({}));throw new v(a.message??"Someone else saved this first.")}if(!l.ok){let a=await l.text().catch(()=>""),c=`${o.method} ${r} \u2192 ${String(l.status)}`;try{let y=JSON.parse(a);y.message&&(c=y.message)}catch{c=a?`${c}: ${a}`:c}throw x(new Error(c))}return await l.json()}catch(l){throw x(l)}},d=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await b(`${t}${T}/tables`,await s())).tables,listRows:async(r,o)=>await b(`${t}${T}${d(r)}/rows?limit=${String(o??100)}`,await s()),insertRow:async(r,o)=>{let{rowIds:l}=await n(`${d(r)}/rows`,{method:"POST",body:{rows:[o]}}),a=l[0];if(!a)throw x(new Error("The row was not created."));return a},patchRow:async(r,o,l)=>(await n(`${d(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,o)=>(await n(`/${encodeURIComponent(r)}/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await b(`${t}${T}/tables/files/rows?limit=200`,await s())).rows.map(o=>({id:o.rowId,filename:String(o.data.filename??""),source:String(o.data.source??"client"),campaign:String(o.data.campaign??""),size:typeof o.data.size=="number"?o.data.size:0,createdAt:o.createdAt})),uploadFile:async(r,o)=>{let l=new Uint8Array(await r.arrayBuffer());return await n("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:de(l),...o===void 0?{}:{message:o}}})},fileLink:async r=>await b(`${t}${T}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${T}/documents`,await s())).documents,readDocument:async r=>(await b(`${t}${T}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,o)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:o})}};import{useCallback as ue,useEffect as j,useRef as me,useState as I}from"react";var N="greeting";function z(e,t){if(e.some(n=>n.id===t.id))return e;let i=e.filter(n=>n.id!==N);return[...t.author==="client"?i.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):i,t]}var ge=(e,t)=>{let[i,s]=I([]),[n,d]=I(""),[r,o]=I(!1),[l,a]=I(null),c=me(0),y=t?.pollMs??2e3;j(()=>{let p=!0;return e.getInfo().then(g=>{p&&g.greeting&&s(m=>m.length===0?[{id:N,cursor:"",author:"agent",text:g.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),j(()=>e.subscribe?.({onMessage:p=>s(g=>z(g,p)),onError:p=>a(p.error)},{intervalMs:y}),[e,y]);let u=ue(()=>{let p=n.trim();if(!p||r)return;d(""),a(null),o(!0),c.current+=1;let g={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};s(m=>[...m,g]),e.sendMessage(p).then(()=>o(!1)).catch(m=>{a(m instanceof Error?m.message:"send_failed"),o(!1)})},[n,r,e]);return{messages:i,input:n,setInput:d,send:u,sending:r,error:l}};export{ae as Chat,Z as HolaProvider,v as PortalConflict,K as createApiTransport,le as createHolaClient,pe as createPortalData,D as createWidgetTransport,z as mergeMessage,E as textDeltaOf,S as thinkingDeltaOf,$ as toolEventOf,k as useArtifactUrl,A as useChat,M as useHolaClient,ge as useThread};
8
+ `)}},l=async()=>{let u=await d.read();if(!u.done)return a+=r.decode(u.value,{stream:!0}),i(),l()};await l()};var ne=/\/+$/,k="/api/v1/end-user",oe=e=>{let t=e.baseUrl.replace(ne,""),o=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await o()}`}),d=async r=>{let a=await fetch(`${t}${k}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:r})});if(!a.ok)throw new Error(`hola: message failed (${a.status})`);return{reply:(await a.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${k}/chat/info${s("?")}`,await n()),sendMessage:d,streamMessage:async(r,a)=>{try{let i=await d(r);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(T(i))}},subscribe:(r,a)=>{let i=a?.since??"0",l=!1,u,y=a?.intervalMs??3e3,p=async()=>{try{let m=await b(`${t}${k}/chat/thread?since=${encodeURIComponent(i)}${s("&")}`,await n());for(let g of m.messages)r.onMessage(g);i=m.cursor,r.onHealthy?.()}catch(m){r.onError?.(T(m))}l||(u=setTimeout(p,y))},c=()=>{l=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",c),u=setTimeout(p,0),c},requestHuman:async()=>{await fetch(`${t}${k}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};import{useEffect as se,useState as ae}from"react";var A=(e,t)=>{let[o,s]=ae("");return se(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([n],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),o};import{useEffect as ge,useRef as me}from"react";import{createContext as ie,useContext as le}from"react";import{jsx as ue}from"react/jsx-runtime";var N=ie(null),de=({client:e,children:t})=>ue(N.Provider,{value:e,children:t}),W=()=>{let e=le(N);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as L,useEffect as ce,useRef as pe,useState as C}from"react";var M=e=>{let[t,o]=C([]),[s,n]=C(""),[d,r]=C(!1),[a,i]=C(null),l=pe(0),u=L(()=>{let p=l.current;return l.current+=1,p},[]);ce(()=>{let p=!0;return e.getInfo().then(c=>{p&&c.greeting&&o([{id:u(),role:"assistant",text:c.greeting}])}).catch(()=>{}),()=>{p=!1}},[e,u]);let y=L(()=>{let p=s.trim();if(!p||d)return;n(""),i(null),r(!0);let c=u(),m=u();o(f=>[...f,{id:c,role:"user",text:p},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{o(h=>h.map(R=>R.id===m?f(R):R))};e.streamMessage(p,{onText:f=>g(h=>({...h,text:h.text+f})),onComplete:f=>{g(h=>({...h,text:f.reply||h.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{g(h=>({...h,streaming:!1})),i(f.error),r(!1)}}).catch(f=>{g(h=>({...h,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,d,e,u]);return{messages:t,input:s,setInput:n,send:y,sending:d,error:a}};import{jsx as P,jsxs as D}from"react/jsx-runtime";var x={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},fe=({artifact:e})=>{let t=A(e.dataBase64??"",e.mimeType),o=e.url??t;return o?e.mimeType.startsWith("image/")?P("a",{href:o,rel:"noopener noreferrer",target:"_blank",children:P("img",{alt:e.filename,src:o,style:x.image})}):P("a",{download:e.filename,href:o,style:x.link,children:e.filename}):null},ye=()=>{let e=W(),{messages:t,input:o,setInput:s,send:n,sending:d,error:r}=M(e),a=me(null);ge(()=>{let l=a.current;l&&(l.scrollTop=l.scrollHeight)},[t]);let i=l=>{l.preventDefault(),n()};return D("div",{style:x.root,children:[P("div",{ref:a,style:x.log,children:t.map(l=>D("div",{style:l.role==="user"?x.user:x.assistant,children:[l.text||(l.streaming?"\u2026":""),l.artifacts&&l.artifacts.length>0?P("div",{style:x.artifacts,children:l.artifacts.map(u=>P(fe,{artifact:u},u.filename))}):null]},l.id))}),r?P("div",{style:x.error,children:r}):null,D("form",{onSubmit:i,style:x.form,children:[P("input",{"aria-label":"Message",onChange:l=>s(l.target.value),placeholder:"Type a message\u2026",style:x.input,value:o}),P("button",{disabled:d||o.trim().length===0,style:x.button,type:"submit",children:"Send"})]})]})};var be=/\/+$/,U=e=>{let t=e.sessionId,o=e.baseUrl.replace(be,""),s=r=>`${o}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},d=(r,a)=>({url:s(r),headers:n,body:JSON.stringify({message:a,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>b(s("/config"),{}),sendMessage:async r=>{let a=await F(d("/message",r));return t=a.sessionId,a},streamMessage:(r,a,i)=>_(d("/message/stream",r),a,l=>{t=l},i),subscribe:(r,a)=>{let i=a?.since??"0",l=!1,u,y=a?.intervalMs??3e3,p=async()=>{let m=t;if(m)try{let g=await b(s(`/messages?session=${encodeURIComponent(m)}&since=${encodeURIComponent(i)}`),{});for(let f of g.messages)r.onMessage(f);i=g.cursor,r.onHealthy?.()}catch(g){r.onError?.(T(g))}l||(u=setTimeout(p,y))},c=()=>{l=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",c),u=setTimeout(p,0),c},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var he=e=>"transport"in e?e.transport:U(e);var xe=e=>typeof e=="number"?e:Number(e)||0,j=e=>/rate|position|rank|score|percent|avg/i.test(e),we=/posted|published|sent|completed|shipped|launched|_at|date/i,Te=new Set(["service","order","end_user_id"]);function B(e,t){let o=e.filter(i=>!Te.has(i.key)),s=o.filter(i=>i.type==="number").slice(0,3),n=o.find(i=>i.key==="status")??o.find(i=>i.type==="select"),d=o.find(i=>i.type==="date"&&we.test(i.key))??o.find(i=>i.type==="date"),r=o.filter(i=>i.type==="number"&&!j(i.key)).map(i=>({field:i,total:t.reduce((l,u)=>l+xe(u.data[i.key]),0)})).sort((i,l)=>l.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of s){let l=j(i.key);a.push({type:"stat",metric:i.key,agg:l?"avg":"sum",label:l?`Avg ${i.label.toLowerCase()}`:i.label})}return d&&a.push({type:"line",x:d.key,y:r?.key,agg:"sum",title:r?`${r.label} by day`:"Items by day"}),n&&a.push({type:"donut",groupBy:n.key,title:n.label}),a.push({type:"table",columns:o.map(i=>i.key)}),{version:1,widgets:a}}var G=new Set(["service","order","end_user_id"]),J=e=>typeof e=="number"?e:Number(e)||0,z=e=>e==null?"":String(e);function q(e,t){if(e==="count")return t.length;if(t.length===0)return 0;if(e==="last")return t[t.length-1]??0;if(e==="min")return Math.min(...t);if(e==="max")return Math.max(...t);let o=t.reduce((s,n)=>s+n,0);return e==="avg"?Math.round(o/t.length):o}function Pe(e,t,o,s){let n=new Map;for(let d of e){let r=new Date(z(d.data[t]));if(Number.isNaN(r.getTime()))continue;let a=r.toISOString().slice(0,10),i=n.get(a)??[];i.push(o?J(d.data[o]):1),n.set(a,i)}return[...n.entries()].sort((d,r)=>d[0]<r[0]?-1:1).map(([d,r])=>({label:new Date(`${d}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:o?q(s,r):r.length}))}function He(e,t){let o=new Map;for(let s of e){let n=z(s.data[t]).trim();n&&o.set(n,(o.get(n)??0)+1)}return[...o.entries()].sort((s,n)=>n[1]-s[1]).map(([s,n])=>({label:s,value:n}))}function ke(e,t,o,s){switch(e.type){case"stat":{let n=e.metric?t.get(e.metric):void 0;if(e.metric&&!n)return null;if(!n)return{kind:"stat",label:e.label??"Items",value:s.length};let d=s.map(r=>J(r.data[n.key]));return{kind:"stat",label:e.label??n.label,value:q(e.agg??"sum",d)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let n=Pe(s,e.x,e.y,e.agg??"sum");return n.length<2?null:{kind:"series",draw:e.type,title:e.title??"By day",points:n}}case"donut":{if(!t.has(e.groupBy))return null;let n=He(s,e.groupBy);return n.length===0?null:{kind:"donut",title:e.title??t.get(e.groupBy)?.label??"Breakdown",points:n}}case"table":{let n=e.columns?e.columns.map(d=>t.get(d)).filter(d=>d!==void 0&&!G.has(d.key)):o;return n.length===0?null:{kind:"table",title:e.title,columns:n,rows:s}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function V(e,t,o){let s=t.filter(r=>!G.has(r.key)),n=new Map(t.map(r=>[r.key,r])),d=[];for(let r of e.widgets){let a=ke(r,n,s,o);a&&d.push(a)}return d}var Ce=new Set(["sum","avg","count","min","max","last"]),Y=e=>typeof e=="string"&&Ce.has(e)?e:void 0,w=e=>typeof e=="string"&&e!==""?e:void 0;function Se(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:w(t.label),metric:w(t.metric),agg:Y(t.agg)};case"line":case"bar":{let o=w(t.x);return o?{type:t.type,title:w(t.title),x:o,y:w(t.y),agg:Y(t.agg)}:null}case"donut":{let o=w(t.groupBy);return o?{type:"donut",title:w(t.title),groupBy:o}:null}case"table":{let o=Array.isArray(t.columns)?t.columns.filter(s=>typeof s=="string"):void 0;return{type:"table",title:w(t.title),columns:o}}case"text":{let o=w(t.body);return o?{type:"text",title:w(t.title),body:o}:null}default:return null}}function Q(e){let t=e;if(typeof e=="string"){if(e.trim()==="")return null;try{t=JSON.parse(e)}catch{return null}}if(typeof t!="object"||t===null)return null;let o=t.widgets;if(!Array.isArray(o))return null;let s=o.map(Se).filter(d=>d!==null);if(s.length===0)return null;let n=t.version;return{version:typeof n=="number"?n:1,title:w(t.title),widgets:s}}var ve=/\/+$/,H="/api/v1/end-user",S=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function Re(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var Ie=e=>{let t=e.baseUrl.replace(ve,""),o=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await o()}`}),n=async(r,a)=>{try{let i=await fetch(`${t}${H}${r}`,{method:a.method,headers:await s(),...a.body===void 0?{}:{body:JSON.stringify(a.body)}});if(i.status===409){let l=await i.json().catch(()=>({}));throw new S(l.message??"Someone else saved this first.")}if(!i.ok){let l=await i.text().catch(()=>""),u=`${a.method} ${r} \u2192 ${String(i.status)}`;try{let y=JSON.parse(l);y.message&&(u=y.message)}catch{u=l?`${u}: ${l}`:u}throw T(new Error(u))}return await i.json()}catch(i){throw T(i)}},d=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await b(`${t}${H}/tables`,await s())).tables,listRows:async(r,a)=>await b(`${t}${H}${d(r)}/rows?limit=${String(a??100)}`,await s()),insertRow:async(r,a)=>{let{rowIds:i}=await n(`${d(r)}/rows`,{method:"POST",body:{rows:[a]}}),l=i[0];if(!l)throw T(new Error("The row was not created."));return l},patchRow:async(r,a,i)=>(await n(`${d(r)}/rows/${encodeURIComponent(a)}`,{method:"PATCH",body:i})).updated,deleteRow:async(r,a)=>(await n(`/${encodeURIComponent(r)}/${encodeURIComponent(a)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await b(`${t}${H}/tables/files/rows?limit=200`,await s())).rows.map(a=>({id:a.rowId,filename:String(a.data.filename??""),source:String(a.data.source??"client"),campaign:String(a.data.campaign??""),size:typeof a.data.size=="number"?a.data.size:0,createdAt:a.createdAt})),uploadFile:async(r,a)=>{let i=new Uint8Array(await r.arrayBuffer());return await n("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:Re(i),...a===void 0?{}:{message:a}}})},fileLink:async r=>await b(`${t}${H}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${H}/documents`,await s())).documents,readDocument:async r=>(await b(`${t}${H}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,a)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:a})}};import{useCallback as Ee,useEffect as X,useRef as $e,useState as v}from"react";var Z="greeting";function K(e,t){if(e.some(n=>n.id===t.id))return e;let o=e.filter(n=>n.id!==Z);return[...t.author==="client"?o.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):o,t]}var Ae=(e,t)=>{let[o,s]=v([]),[n,d]=v(""),[r,a]=v(!1),[i,l]=v(null),u=$e(0),y=t?.pollMs??2e3;X(()=>{let c=!0;return e.getInfo().then(m=>{c&&m.greeting&&s(g=>g.length===0?[{id:Z,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{c=!1}},[e]),X(()=>e.subscribe?.({onMessage:c=>s(m=>K(m,c)),onError:c=>l(c.error),onHealthy:()=>l(null)},{intervalMs:y}),[e,y]);let p=Ee(()=>{let c=n.trim();if(!c||r)return;d(""),l(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:c,pending:!0};s(g=>[...g,m]),e.sendMessage(c).then(()=>a(!1)).catch(g=>{l(g instanceof Error?g.message:"send_failed"),a(!1)})},[n,r,e]);return{messages:o,input:n,setInput:d,send:p,sending:r,error:i}};export{ye as Chat,de as HolaProvider,S as PortalConflict,oe as createApiTransport,he as createHolaClient,Ie as createPortalData,U as createWidgetTransport,B as inferSpec,K as mergeMessage,Q as parseDashboardSpec,V as renderModel,I as textDeltaOf,E as thinkingDeltaOf,$ as toolEventOf,A as useArtifactUrl,M as useChat,W as useHolaClient,Ae as useThread};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@holaboss/client",
3
- "version": "0.2.0-beta.4",
4
- "description": "Injectable client SDK for embedding a Holaboss AI employee (chat today; dashboard + artifacts next) into your own web app.",
3
+ "version": "0.2.0-beta.5",
4
+ "description": "Injectable client SDK for embedding a Holaboss AI employee (chat + configurable dashboards today; artifacts next) into your own web app.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",