@holaboss/client 0.2.0-beta.5 → 0.2.0-beta.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/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client'
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(`
2
+ var _=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var ce=(e,t)=>{for(var o in t)_(e,o,{get:t[o],enumerable:!0})},ge=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ue(t))!pe.call(e,r)&&r!==o&&_(e,r,{get:()=>t[r],enumerable:!(s=de(t,r))||s.enumerable});return e};var me=e=>ge(_({},"__esModule",{value:!0}),e);var De={};ce(De,{Chat:()=>Z,HolaProvider:()=>Q,PortalConflict:()=>S,createApiTransport:()=>V,createHolaClient:()=>K,createPortalData:()=>ae,createWidgetTransport:()=>F,deriveFields:()=>N,inferSpec:()=>L,mergeMessage:()=>z,parseDashboardSpec:()=>B,renderModel:()=>j,textDeltaOf:()=>v,thinkingDeltaOf:()=>R,toolEventOf:()=>I,useArtifactUrl:()=>A,useChat:()=>D,useHolaClient:()=>W,useThread:()=>le});module.exports=me(De);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 fe=/^ /,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"}},ye=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(fe,"")));return o.length>0?{event:t,data:o.join(`
4
+ `)}:null},be=(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 l=R(s);l&&t.onThinking?.(l);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()},q=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 l=r.body.getReader(),n=new TextDecoder,a="",i=()=>{let u=a.indexOf(`
5
5
 
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(`
6
+ `);for(;u!==-1;){let b=a.slice(0,u);a=a.slice(u+2);let c=ye(b);c&&be(c,t,o),u=a.indexOf(`
7
7
 
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});
8
+ `)}},d=async()=>{let u=await l.read();if(!u.done)return a+=n.decode(u.value,{stream:!0}),i(),d()};await d()};var he=/\/+$/,E="/api/v1/end-user",V=e=>{let t=e.baseUrl.replace(he,""),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()}`}),l=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:l,streamMessage:async(n,a)=>{try{let i=await l(n);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(k(i))}},subscribe:(n,a)=>{let i=a?.since??"0",d=!1,u,b=a?.intervalMs??3e3,c=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))}d||(u=setTimeout(c,b))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},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)),l=URL.createObjectURL(new Blob([r],{type:t}));return s(l),()=>URL.revokeObjectURL(l)},[e,t]),o};var U=require("react");var M=require("react"),X=require("react/jsx-runtime"),Y=(0,M.createContext)(null),Q=({client:e,children:t})=>(0,X.jsx)(Y.Provider,{value:e,children:t}),W=()=>{let e=(0,M.useContext)(Y);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)(""),[l,n]=(0,x.useState)(!1),[a,i]=(0,x.useState)(null),d=(0,x.useRef)(0),u=(0,x.useCallback)(()=>{let c=d.current;return d.current+=1,c},[]);(0,x.useEffect)(()=>{let c=!0;return e.getInfo().then(p=>{c&&p.greeting&&o([{id:u(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{c=!1}},[e,u]);let b=(0,x.useCallback)(()=>{let c=s.trim();if(!c||l)return;r(""),i(null),n(!0);let p=u(),m=u();o(f=>[...f,{id:p,role:"user",text:c},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{o(T=>T.map(O=>O.id===m?f(O):O))};e.streamMessage(c,{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,l,e,u]);return{messages:t,input:s,setInput:r,send:b,sending:l,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"}},xe=({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},Z=()=>{let e=W(),{messages:t,input:o,setInput:s,send:r,sending:l,error:n}=D(e),a=(0,U.useRef)(null);(0,U.useEffect)(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),r()};return(0,y.jsxs)("div",{style:P.root,children:[(0,y.jsx)("div",{ref:a,style:P.log,children:t.map(d=>(0,y.jsxs)("div",{style:d.role==="user"?P.user:P.assistant,children:[d.text||(d.streaming?"\u2026":""),d.artifacts&&d.artifacts.length>0?(0,y.jsx)("div",{style:P.artifacts,children:d.artifacts.map(u=>(0,y.jsx)(xe,{artifact:u},u.filename))}):null]},d.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:d=>s(d.target.value),placeholder:"Type a message\u2026",style:P.input,value:o}),(0,y.jsx)("button",{disabled:l||o.trim().length===0,style:P.button,type:"submit",children:"Send"})]})]})};var we=/\/+$/,F=e=>{let t=e.sessionId,o=e.baseUrl.replace(we,""),s=n=>`${o}/endpoint/${encodeURIComponent(e.token)}${n}`,r={"content-type":"application/json"},l=(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(l("/message",n));return t=a.sessionId,a},streamMessage:(n,a,i)=>q(l("/message/stream",n),a,d=>{t=d},i),subscribe:(n,a)=>{let i=a?.since??"0",d=!1,u,b=a?.intervalMs??3e3,c=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))}d||(u=setTimeout(c,b))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:r,body:JSON.stringify({sessionId:t})})}}};var K=e=>"transport"in e?e.transport:F(e);var Te=/^https?:\/\//i,Pe=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,He=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function ke(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(Te.test(e))return"url";if(Pe.test(e))return"date"}return"text"}function N(e){let t=[],o=new Map;for(let s of e)for(let[r,l]of Object.entries(s.data??{})){if(l==null)continue;let n=o.get(r);n||(n=new Map,o.set(r,n),t.push(r));let a=ke(l);n.set(a,(n.get(a)??0)+1)}return t.map(s=>{let r=o.get(s)??new Map,l="text",n=-1;for(let[a,i]of r)i>n&&(n=i,l=a);return{key:s,label:He(s),type:l}})}var Ce=e=>typeof e=="number"?e:Number(e)||0,ee=e=>/rate|position|rank|score|percent|avg/i.test(e),Se=/posted|published|sent|completed|shipped|launched|_at|date/i,ve=new Set(["service","order","end_user_id"]);function L(e,t){let o=e.filter(i=>!ve.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"),l=o.find(i=>i.type==="date"&&Se.test(i.key))??o.find(i=>i.type==="date"),n=o.filter(i=>i.type==="number"&&!ee(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+Ce(u.data[i.key]),0)})).sort((i,d)=>d.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of s){let d=ee(i.key);a.push({type:"stat",metric:i.key,agg:d?"avg":"sum",label:d?`Avg ${i.label.toLowerCase()}`:i.label})}return l&&a.push({type:"line",x:l.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 te=new Set(["service","order","end_user_id"]),re=e=>typeof e=="number"?e:Number(e)||0,ne=e=>e==null?"":String(e);function oe(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 Re(e,t,o,s){let r=new Map;for(let l of e){let n=new Date(ne(l.data[t]));if(Number.isNaN(n.getTime()))continue;let a=n.toISOString().slice(0,10),i=r.get(a)??[];i.push(o?re(l.data[o]):1),r.set(a,i)}return[...r.entries()].sort((l,n)=>l[0]<n[0]?-1:1).map(([l,n])=>({label:new Date(`${l}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:o?oe(s,n):n.length}))}function Ie(e,t){let o=new Map;for(let s of e){let r=ne(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 Ee(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 l=s.map(n=>re(n.data[r.key]));return{kind:"stat",label:e.label??r.label,value:oe(e.agg??"sum",l)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let r=Re(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=Ie(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(l=>t.get(l)).filter(l=>l!==void 0&&!te.has(l.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 j(e,t,o){let s=t.filter(n=>!te.has(n.key)),r=new Map(t.map(n=>[n.key,n])),l=[];for(let n of e.widgets){let a=Ee(n,r,s,o);a&&l.push(a)}return l}var $e=new Set(["sum","avg","count","min","max","last"]),se=e=>typeof e=="string"&&$e.has(e)?e:void 0,H=e=>typeof e=="string"&&e!==""?e:void 0;function Ae(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:se(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:se(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 B(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(Ae).filter(l=>l!==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 Me=/\/+$/,C="/api/v1/end-user",S=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function We(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var ae=e=>{let t=e.baseUrl.replace(Me,""),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 d=await i.json().catch(()=>({}));throw new S(d.message??"Someone else saved this first.")}if(!i.ok){let d=await i.text().catch(()=>""),u=`${a.method} ${n} \u2192 ${String(i.status)}`;try{let b=JSON.parse(d);b.message&&(u=b.message)}catch{u=d?`${u}: ${d}`:u}throw k(new Error(u))}return await i.json()}catch(i){throw k(i)}},l=n=>`/tables/${encodeURIComponent(n)}`;return{listTables:async()=>(await h(`${t}${C}/tables`,await s())).tables,listRows:async(n,a)=>await h(`${t}${C}${l(n)}/rows?limit=${String(a??100)}`,await s()),insertRow:async(n,a)=>{let{rowIds:i}=await r(`${l(n)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw k(new Error("The row was not created."));return d},patchRow:async(n,a,i)=>(await r(`${l(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:We(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"),ie="greeting";function z(e,t){if(e.some(r=>r.id===t.id))return e;let o=e.filter(r=>r.id!==ie);return[...t.author==="client"?o.filter(r=>!(r.pending&&r.author==="client"&&r.text===t.text)):o,t]}var le=(e,t)=>{let[o,s]=(0,w.useState)([]),[r,l]=(0,w.useState)(""),[n,a]=(0,w.useState)(!1),[i,d]=(0,w.useState)(null),u=(0,w.useRef)(0),b=t?.pollMs??2e3;(0,w.useEffect)(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&s(g=>g.length===0?[{id:ie,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),(0,w.useEffect)(()=>e.subscribe?.({onMessage:p=>s(m=>z(m,p)),onError:p=>d(p.error),onHealthy:()=>d(null)},{intervalMs:b}),[e,b]);let c=(0,w.useCallback)(()=>{let p=r.trim();if(!p||n)return;l(""),d(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:p,pending:!0};s(g=>[...g,m]),e.sendMessage(p).then(()=>a(!1)).catch(g=>{d(g instanceof Error?g.message:"send_failed"),a(!1)})},[r,n,e]);return{messages:o,input:r,setInput:l,send:c,sending:n,error:i}};0&&(module.exports={Chat,HolaProvider,PortalConflict,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,deriveFields,inferSpec,mergeMessage,parseDashboardSpec,renderModel,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
package/dist/index.d.cts CHANGED
@@ -290,6 +290,13 @@ type PortalData = {
290
290
  };
291
291
  declare const createPortalData: (config: ApiTransportConfig) => PortalData;
292
292
 
293
+ /** Build a `PortalField[]` from a slice's rows, typed by the dominant JSON value type per key.
294
+ * `select` is never inferred (it needs an option set only a template carries) — such columns fall
295
+ * back to `text`. Returns [] for an empty slice. */
296
+ declare function deriveFields(rows: readonly {
297
+ data: Record<string, unknown>;
298
+ }[]): PortalField[];
299
+
293
300
  /** How a metric column is reduced to a single number. */
294
301
  type Agg = "sum" | "avg" | "count" | "min" | "max" | "last";
295
302
  type WidgetType = "stat" | "line" | "bar" | "donut" | "table" | "text";
@@ -429,4 +436,4 @@ declare const useThread: (client: HolaClient, options?: {
429
436
  pollMs?: number;
430
437
  }) => UseThread;
431
438
 
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 };
439
+ 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, deriveFields, inferSpec, mergeMessage, parseDashboardSpec, renderModel, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
package/dist/index.d.ts CHANGED
@@ -290,6 +290,13 @@ type PortalData = {
290
290
  };
291
291
  declare const createPortalData: (config: ApiTransportConfig) => PortalData;
292
292
 
293
+ /** Build a `PortalField[]` from a slice's rows, typed by the dominant JSON value type per key.
294
+ * `select` is never inferred (it needs an option set only a template carries) — such columns fall
295
+ * back to `text`. Returns [] for an empty slice. */
296
+ declare function deriveFields(rows: readonly {
297
+ data: Record<string, unknown>;
298
+ }[]): PortalField[];
299
+
293
300
  /** How a metric column is reduced to a single number. */
294
301
  type Agg = "sum" | "avg" | "count" | "min" | "max" | "last";
295
302
  type WidgetType = "stat" | "line" | "bar" | "donut" | "table" | "text";
@@ -429,4 +436,4 @@ declare const useThread: (client: HolaClient, options?: {
429
436
  pollMs?: number;
430
437
  }) => UseThread;
431
438
 
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 };
439
+ 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, deriveFields, 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},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(`
2
+ var F=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},I=e=>{let t=F(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{let t=F(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 te=/^ /,T=e=>{if(e&&typeof e=="object"){let t=e,s=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:s,retryable:t.retryable}:{error:s}}return{error:"request_failed"}},re=e=>{let t="message",s=[];for(let o of e.split(`
3
+ `))o.startsWith(":")||(o.startsWith("event:")?t=o.slice(6).trim():o.startsWith("data:")&&s.push(o.slice(5).replace(te,"")));return s.length>0?{event:t,data:s.join(`
4
+ `)}:null},ne=(e,t,s)=>{let o;try{o=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=o.sessionId;n&&(s(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(o);let n=I(o);n&&t.onText?.(n);let l=E(o);l&&t.onThinking?.(l);let r=$(o);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=o;s(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(T(o))},b=async(e,t)=>{let s=await fetch(e,{headers:t});if(!s.ok)throw new Error(`hola: request failed (${s.status})`);return await s.json()},O=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,s,o)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:o});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let l=n.body.getReader(),r=new TextDecoder,a="",i=()=>{let u=a.indexOf(`
5
5
 
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(`
6
+ `);for(;u!==-1;){let y=a.slice(0,u);a=a.slice(u+2);let c=re(y);c&&ne(c,t,s),u=a.indexOf(`
7
7
 
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};
8
+ `)}},d=async()=>{let u=await l.read();if(!u.done)return a+=r.decode(u.value,{stream:!0}),i(),d()};await d()};var oe=/\/+$/,k="/api/v1/end-user",se=e=>{let t=e.baseUrl.replace(oe,""),s=()=>typeof e.token=="function"?e.token():e.token,o=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await s()}`}),l=async r=>{let a=await fetch(`${t}${k}/chat${o("?")}`,{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${o("?")}`,await n()),sendMessage:l,streamMessage:async(r,a)=>{try{let i=await l(r);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(T(i))}},subscribe:(r,a)=>{let i=a?.since??"0",d=!1,u,y=a?.intervalMs??3e3,c=async()=>{try{let m=await b(`${t}${k}/chat/thread?since=${encodeURIComponent(i)}${o("&")}`,await n());for(let g of m.messages)r.onMessage(g);i=m.cursor,r.onHealthy?.()}catch(m){r.onError?.(T(m))}d||(u=setTimeout(c,y))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{await fetch(`${t}${k}/chat/request-human${o("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};import{useEffect as ae,useState as ie}from"react";var A=(e,t)=>{let[s,o]=ie("");return ae(()=>{if(!e){o("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),l=URL.createObjectURL(new Blob([n],{type:t}));return o(l),()=>URL.revokeObjectURL(l)},[e,t]),s};import{useEffect as me,useRef as fe}from"react";import{createContext as le,useContext as de}from"react";import{jsx as pe}from"react/jsx-runtime";var N=le(null),ue=({client:e,children:t})=>pe(N.Provider,{value:e,children:t}),M=()=>{let e=de(N);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as L,useEffect as ce,useRef as ge,useState as C}from"react";var W=e=>{let[t,s]=C([]),[o,n]=C(""),[l,r]=C(!1),[a,i]=C(null),d=ge(0),u=L(()=>{let c=d.current;return d.current+=1,c},[]);ce(()=>{let c=!0;return e.getInfo().then(p=>{c&&p.greeting&&s([{id:u(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{c=!1}},[e,u]);let y=L(()=>{let c=o.trim();if(!c||l)return;n(""),i(null),r(!0);let p=u(),m=u();s(f=>[...f,{id:p,role:"user",text:c},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{s(h=>h.map(R=>R.id===m?f(R):R))};e.streamMessage(c,{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)})},[o,l,e,u]);return{messages:t,input:o,setInput:n,send:y,sending:l,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"}},ye=({artifact:e})=>{let t=A(e.dataBase64??"",e.mimeType),s=e.url??t;return s?e.mimeType.startsWith("image/")?P("a",{href:s,rel:"noopener noreferrer",target:"_blank",children:P("img",{alt:e.filename,src:s,style:x.image})}):P("a",{download:e.filename,href:s,style:x.link,children:e.filename}):null},be=()=>{let e=M(),{messages:t,input:s,setInput:o,send:n,sending:l,error:r}=W(e),a=fe(null);me(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),n()};return D("div",{style:x.root,children:[P("div",{ref:a,style:x.log,children:t.map(d=>D("div",{style:d.role==="user"?x.user:x.assistant,children:[d.text||(d.streaming?"\u2026":""),d.artifacts&&d.artifacts.length>0?P("div",{style:x.artifacts,children:d.artifacts.map(u=>P(ye,{artifact:u},u.filename))}):null]},d.id))}),r?P("div",{style:x.error,children:r}):null,D("form",{onSubmit:i,style:x.form,children:[P("input",{"aria-label":"Message",onChange:d=>o(d.target.value),placeholder:"Type a message\u2026",style:x.input,value:s}),P("button",{disabled:l||s.trim().length===0,style:x.button,type:"submit",children:"Send"})]})]})};var he=/\/+$/,U=e=>{let t=e.sessionId,s=e.baseUrl.replace(he,""),o=r=>`${s}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},l=(r,a)=>({url:o(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(o("/config"),{}),sendMessage:async r=>{let a=await O(l("/message",r));return t=a.sessionId,a},streamMessage:(r,a,i)=>_(l("/message/stream",r),a,d=>{t=d},i),subscribe:(r,a)=>{let i=a?.since??"0",d=!1,u,y=a?.intervalMs??3e3,c=async()=>{let m=t;if(m)try{let g=await b(o(`/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))}d||(u=setTimeout(c,y))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{t&&await fetch(o("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var xe=e=>"transport"in e?e.transport:U(e);var we=/^https?:\/\//i,Te=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,Pe=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function He(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(we.test(e))return"url";if(Te.test(e))return"date"}return"text"}function j(e){let t=[],s=new Map;for(let o of e)for(let[n,l]of Object.entries(o.data??{})){if(l==null)continue;let r=s.get(n);r||(r=new Map,s.set(n,r),t.push(n));let a=He(l);r.set(a,(r.get(a)??0)+1)}return t.map(o=>{let n=s.get(o)??new Map,l="text",r=-1;for(let[a,i]of n)i>r&&(r=i,l=a);return{key:o,label:Pe(o),type:l}})}var ke=e=>typeof e=="number"?e:Number(e)||0,B=e=>/rate|position|rank|score|percent|avg/i.test(e),Ce=/posted|published|sent|completed|shipped|launched|_at|date/i,Se=new Set(["service","order","end_user_id"]);function z(e,t){let s=e.filter(i=>!Se.has(i.key)),o=s.filter(i=>i.type==="number").slice(0,3),n=s.find(i=>i.key==="status")??s.find(i=>i.type==="select"),l=s.find(i=>i.type==="date"&&Ce.test(i.key))??s.find(i=>i.type==="date"),r=s.filter(i=>i.type==="number"&&!B(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+ke(u.data[i.key]),0)})).sort((i,d)=>d.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of o){let d=B(i.key);a.push({type:"stat",metric:i.key,agg:d?"avg":"sum",label:d?`Avg ${i.label.toLowerCase()}`:i.label})}return l&&a.push({type:"line",x:l.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:s.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,q=e=>e==null?"":String(e);function V(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 s=t.reduce((o,n)=>o+n,0);return e==="avg"?Math.round(s/t.length):s}function ve(e,t,s,o){let n=new Map;for(let l of e){let r=new Date(q(l.data[t]));if(Number.isNaN(r.getTime()))continue;let a=r.toISOString().slice(0,10),i=n.get(a)??[];i.push(s?J(l.data[s]):1),n.set(a,i)}return[...n.entries()].sort((l,r)=>l[0]<r[0]?-1:1).map(([l,r])=>({label:new Date(`${l}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:s?V(o,r):r.length}))}function Re(e,t){let s=new Map;for(let o of e){let n=q(o.data[t]).trim();n&&s.set(n,(s.get(n)??0)+1)}return[...s.entries()].sort((o,n)=>n[1]-o[1]).map(([o,n])=>({label:o,value:n}))}function Ie(e,t,s,o){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:o.length};let l=o.map(r=>J(r.data[n.key]));return{kind:"stat",label:e.label??n.label,value:V(e.agg??"sum",l)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let n=ve(o,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=Re(o,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(l=>t.get(l)).filter(l=>l!==void 0&&!G.has(l.key)):s;return n.length===0?null:{kind:"table",title:e.title,columns:n,rows:o}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function Y(e,t,s){let o=t.filter(r=>!G.has(r.key)),n=new Map(t.map(r=>[r.key,r])),l=[];for(let r of e.widgets){let a=Ie(r,n,o,s);a&&l.push(a)}return l}var Ee=new Set(["sum","avg","count","min","max","last"]),Q=e=>typeof e=="string"&&Ee.has(e)?e:void 0,w=e=>typeof e=="string"&&e!==""?e:void 0;function $e(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:Q(t.agg)};case"line":case"bar":{let s=w(t.x);return s?{type:t.type,title:w(t.title),x:s,y:w(t.y),agg:Q(t.agg)}:null}case"donut":{let s=w(t.groupBy);return s?{type:"donut",title:w(t.title),groupBy:s}:null}case"table":{let s=Array.isArray(t.columns)?t.columns.filter(o=>typeof o=="string"):void 0;return{type:"table",title:w(t.title),columns:s}}case"text":{let s=w(t.body);return s?{type:"text",title:w(t.title),body:s}:null}default:return null}}function X(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 s=t.widgets;if(!Array.isArray(s))return null;let o=s.map($e).filter(l=>l!==null);if(o.length===0)return null;let n=t.version;return{version:typeof n=="number"?n:1,title:w(t.title),widgets:o}}var Ae=/\/+$/,H="/api/v1/end-user",S=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function Me(e){let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)}var We=e=>{let t=e.baseUrl.replace(Ae,""),s=()=>typeof e.token=="function"?e.token():e.token,o=async()=>({"content-type":"application/json",authorization:`Bearer ${await s()}`}),n=async(r,a)=>{try{let i=await fetch(`${t}${H}${r}`,{method:a.method,headers:await o(),...a.body===void 0?{}:{body:JSON.stringify(a.body)}});if(i.status===409){let d=await i.json().catch(()=>({}));throw new S(d.message??"Someone else saved this first.")}if(!i.ok){let d=await i.text().catch(()=>""),u=`${a.method} ${r} \u2192 ${String(i.status)}`;try{let y=JSON.parse(d);y.message&&(u=y.message)}catch{u=d?`${u}: ${d}`:u}throw T(new Error(u))}return await i.json()}catch(i){throw T(i)}},l=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await b(`${t}${H}/tables`,await o())).tables,listRows:async(r,a)=>await b(`${t}${H}${l(r)}/rows?limit=${String(a??100)}`,await o()),insertRow:async(r,a)=>{let{rowIds:i}=await n(`${l(r)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw T(new Error("The row was not created."));return d},patchRow:async(r,a,i)=>(await n(`${l(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 o())).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:Me(i),...a===void 0?{}:{message:a}}})},fileLink:async r=>await b(`${t}${H}/files/${encodeURIComponent(r)}/url`,await o()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${H}/documents`,await o())).documents,readDocument:async r=>(await b(`${t}${H}/documents/${encodeURIComponent(r)}`,await o())).document,saveDocument:async(r,a)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:a})}};import{useCallback as De,useEffect as Z,useRef as Ue,useState as v}from"react";var K="greeting";function ee(e,t){if(e.some(n=>n.id===t.id))return e;let s=e.filter(n=>n.id!==K);return[...t.author==="client"?s.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):s,t]}var Fe=(e,t)=>{let[s,o]=v([]),[n,l]=v(""),[r,a]=v(!1),[i,d]=v(null),u=Ue(0),y=t?.pollMs??2e3;Z(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&o(g=>g.length===0?[{id:K,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),Z(()=>e.subscribe?.({onMessage:p=>o(m=>ee(m,p)),onError:p=>d(p.error),onHealthy:()=>d(null)},{intervalMs:y}),[e,y]);let c=De(()=>{let p=n.trim();if(!p||r)return;l(""),d(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:p,pending:!0};o(g=>[...g,m]),e.sendMessage(p).then(()=>a(!1)).catch(g=>{d(g instanceof Error?g.message:"send_failed"),a(!1)})},[n,r,e]);return{messages:s,input:n,setInput:l,send:c,sending:r,error:i}};export{be as Chat,ue as HolaProvider,S as PortalConflict,se as createApiTransport,xe as createHolaClient,We as createPortalData,U as createWidgetTransport,j as deriveFields,z as inferSpec,ee as mergeMessage,X as parseDashboardSpec,Y as renderModel,I as textDeltaOf,E as thinkingDeltaOf,$ as toolEventOf,A as useArtifactUrl,W as useChat,M as useHolaClient,Fe as useThread};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holaboss/client",
3
- "version": "0.2.0-beta.5",
3
+ "version": "0.2.0-beta.6",
4
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",