@holaboss/client 0.2.0-beta.6 → 0.2.0-beta.8

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 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(`
2
+ var z=Object.defineProperty;var we=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var Pe=(e,t)=>{for(var o in t)z(e,o,{get:t[o],enumerable:!0})},Se=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of xe(t))!Te.call(e,n)&&n!==o&&z(e,n,{get:()=>t[n],enumerable:!(s=we(t,n))||s.enumerable});return e};var ke=e=>Se(z({},"__esModule",{value:!0}),e);var Ve={};Pe(Ve,{Chat:()=>ie,GRID_MAX_COLS:()=>L,GRID_MAX_ROWS:()=>N,HolaProvider:()=>se,PortalConflict:()=>v,bandOfKind:()=>B,bandOfType:()=>q,createApiTransport:()=>re,createHolaClient:()=>le,createPortalData:()=>ye,createWidgetTransport:()=>F,deriveFields:()=>J,footprintOf:()=>X,footprintWith:()=>j,inferSpec:()=>V,layoutSegments:()=>Y,mergeMessage:()=>Z,parseDashboardSpec:()=>Q,renderModel:()=>K,textDeltaOf:()=>I,thinkingDeltaOf:()=>W,toolEventOf:()=>M,useArtifactUrl:()=>$,useChat:()=>U,useHolaClient:()=>O,useThread:()=>he});module.exports=ke(Ve);var ee=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},I=e=>{let t=ee(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},W=e=>{let t=ee(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},M=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 Re=/^ /,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"}},Ce=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(Re,"")));return o.length>0?{event:t,data:o.join(`
4
+ `)}:null},He=(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 l=W(s);l&&t.onThinking?.(l);let r=M(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?.(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()},te=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()},ne=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 l=n.body.getReader(),r=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 c=ye(b);c&&be(c,t,o),u=a.indexOf(`
6
+ `);for(;u!==-1;){let b=a.slice(0,u);a=a.slice(u+2);let c=Ce(b);c&&He(c,t,o),u=a.indexOf(`
7
7
 
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});
8
+ `)}},d=async()=>{let u=await l.read();if(!u.done)return a+=r.decode(u.value,{stream:!0}),i(),d()};await d()};var ve=/\/+$/,E="/api/v1/end-user",re=e=>{let t=e.baseUrl.replace(ve,""),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()}`}),l=async r=>{let a=await fetch(`${t}${E}/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()=>h(`${t}${E}/chat/info${s("?")}`,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?.(k(i))}},subscribe:(r,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 n());for(let g of m.messages)r.onMessage(g);i=m.cursor,r.onHealthy?.()}catch(m){r.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 n(),body:"{}"})}}};var A=require("react"),$=(e,t)=>{let[o,s]=(0,A.useState)("");return(0,A.useEffect)(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),l=URL.createObjectURL(new Blob([n],{type:t}));return s(l),()=>URL.revokeObjectURL(l)},[e,t]),o};var _=require("react");var D=require("react"),ae=require("react/jsx-runtime"),oe=(0,D.createContext)(null),se=({client:e,children:t})=>(0,ae.jsx)(oe.Provider,{value:e,children:t}),O=()=>{let e=(0,D.useContext)(oe);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var w=require("react"),U=e=>{let[t,o]=(0,w.useState)([]),[s,n]=(0,w.useState)(""),[l,r]=(0,w.useState)(!1),[a,i]=(0,w.useState)(null),d=(0,w.useRef)(0),u=(0,w.useCallback)(()=>{let c=d.current;return d.current+=1,c},[]);(0,w.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,w.useCallback)(()=>{let c=s.trim();if(!c||l)return;n(""),i(null),r(!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(G=>G.id===m?f(G):G))};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})),r(!1)},onError:f=>{g(T=>({...T,streaming:!1})),i(f.error),r(!1)}}).catch(f=>{g(T=>({...T,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,l,e,u]);return{messages:t,input:s,setInput:n,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"}},Ie=({artifact:e})=>{let t=$(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},ie=()=>{let e=O(),{messages:t,input:o,setInput:s,send:n,sending:l,error:r}=U(e),a=(0,_.useRef)(null);(0,_.useEffect)(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),n()};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)(Ie,{artifact:u},u.filename))}):null]},d.id))}),r?(0,y.jsx)("div",{style:P.error,children:r}):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=r=>`${o}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},l=(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:()=>h(s("/config"),{}),sendMessage:async r=>{let a=await te(l("/message",r));return t=a.sessionId,a},streamMessage:(r,a,i)=>ne(l("/message/stream",r),a,d=>{t=d},i),subscribe:(r,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)r.onMessage(f);i=g.cursor,r.onHealthy?.()}catch(g){r.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:n,body:JSON.stringify({sessionId:t})})}}};var le=e=>"transport"in e?e.transport:F(e);var Me=/^https?:\/\//i,Ee=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,Ae=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function $e(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(Me.test(e))return"url";if(Ee.test(e))return"date"}return"text"}function J(e){let t=[],o=new Map;for(let s of e)for(let[n,l]of Object.entries(s.data??{})){if(l==null)continue;let r=o.get(n);r||(r=new Map,o.set(n,r),t.push(n));let a=$e(l);r.set(a,(r.get(a)??0)+1)}return t.map(s=>{let n=o.get(s)??new Map,l="text",r=-1;for(let[a,i]of n)i>r&&(r=i,l=a);return{key:s,label:Ae(s),type:l}})}var De=e=>typeof e=="number"?e:Number(e)||0,de=e=>/rate|position|rank|score|percent|avg/i.test(e),Oe=/posted|published|sent|completed|shipped|launched|_at|date/i,Ue=new Set(["service","order","end_user_id"]);function V(e,t){let o=e.filter(i=>!Ue.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"),l=o.find(i=>i.type==="date"&&Oe.test(i.key))??o.find(i=>i.type==="date"),r=o.filter(i=>i.type==="number"&&!de(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+De(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=de(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:o.map(i=>i.key)}),{version:1,widgets:a}}var ue={stat:{span:1,h:1},chart:{span:2,h:2},block:{span:0,h:0}},L=6,N=4,_e=e=>Math.max(1,Math.min(L,Math.round(e))),Fe=e=>Math.max(1,Math.min(N,Math.round(e)));function B(e){return e==="stat"?"stat":e==="series"||e==="donut"?"chart":"block"}function q(e){return e==="stat"?"stat":e==="line"||e==="bar"||e==="donut"?"chart":"block"}function X(e){return ue[e]}function j(e,t,o){let s=ue[e];return{span:t===void 0?s.span:_e(t),h:o===void 0?s.h:Fe(o)}}function Y(e){let t=[],o=null;for(let s of e){let n=B(s.kind);if(n==="block"){t.push({kind:"block",widget:s}),o=null;continue}let l=j(n,s.w,s.h),r={widget:s,span:l.span,h:l.h,band:n};o?o.cells.push(r):(o={kind:"grid",cells:[r]},t.push(o))}return t}var pe=new Set(["service","order","end_user_id"]),ce=e=>typeof e=="number"?e:Number(e)||0,ge=e=>e==null?"":String(e);function me(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 Le(e,t,o,s){let n=new Map;for(let l of e){let r=new Date(ge(l.data[t]));if(Number.isNaN(r.getTime()))continue;let a=r.toISOString().slice(0,10),i=n.get(a)??[];i.push(o?ce(l.data[o]):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:o?me(s,r):r.length}))}function Ne(e,t){let o=new Map;for(let s of e){let n=ge(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 Be(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 l=s.map(r=>ce(r.data[n.key]));return{kind:"stat",label:e.label??n.label,value:me(e.agg??"sum",l)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let n=Le(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=Ne(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(l=>t.get(l)).filter(l=>l!==void 0&&!pe.has(l.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 K(e,t,o){let s=t.filter(r=>!pe.has(r.key)),n=new Map(t.map(r=>[r.key,r])),l=[];for(let r of e.widgets){let a=Be(r,n,s,o);a&&l.push({...a,w:r.w,h:r.h})}return l}var je=new Set(["sum","avg","count","min","max","last"]),fe=e=>typeof e=="string"&&je.has(e)?e:void 0,S=e=>typeof e=="string"&&e!==""?e:void 0,C=e=>typeof e=="number"&&e>=1?Math.min(6,Math.round(e)):void 0,H=e=>typeof e=="number"&&e>=1?Math.min(4,Math.round(e)):void 0;function Ge(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:S(t.label),metric:S(t.metric),agg:fe(t.agg),w:C(t.w),h:H(t.h)};case"line":case"bar":{let o=S(t.x);return o?{type:t.type,title:S(t.title),x:o,y:S(t.y),agg:fe(t.agg),w:C(t.w),h:H(t.h)}:null}case"donut":{let o=S(t.groupBy);return o?{type:"donut",title:S(t.title),groupBy:o,w:C(t.w),h:H(t.h)}:null}case"table":{let o=Array.isArray(t.columns)?t.columns.filter(s=>typeof s=="string"):void 0;return{type:"table",title:S(t.title),columns:o,w:C(t.w),h:H(t.h)}}case"text":{let o=S(t.body);return o?{type:"text",title:S(t.title),body:o,w:C(t.w),h:H(t.h)}: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(Ge).filter(l=>l!==null);if(s.length===0)return null;let n=t.version;return{version:typeof n=="number"?n:1,title:S(t.title),widgets:s}}var ze=/\/+$/,R="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function Je(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var ye=e=>{let t=e.baseUrl.replace(ze,""),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}${R}${r}`,{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 v(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 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=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await h(`${t}${R}/tables`,await s())).tables,listRows:async(r,a)=>await h(`${t}${R}${l(r)}/rows?limit=${String(a??100)}`,await s()),insertRow:async(r,a)=>{let{rowIds:i}=await n(`${l(r)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw k(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 h(`${t}${R}/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:Je(i),...a===void 0?{}:{message:a}}})},fileLink:async r=>await h(`${t}${R}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${R}/documents`,await s())).documents,readDocument:async r=>(await h(`${t}${R}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,a)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:a})}};var x=require("react"),be="greeting";function Z(e,t){if(e.some(n=>n.id===t.id))return e;let o=e.filter(n=>n.id!==be);return[...t.author==="client"?o.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):o,t]}var he=(e,t)=>{let[o,s]=(0,x.useState)([]),[n,l]=(0,x.useState)(""),[r,a]=(0,x.useState)(!1),[i,d]=(0,x.useState)(null),u=(0,x.useRef)(0),b=t?.pollMs??2e3;(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&s(g=>g.length===0?[{id:be,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),(0,x.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,x.useCallback)(()=>{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};s(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:o,input:n,setInput:l,send:c,sending:r,error:i}};0&&(module.exports={Chat,GRID_MAX_COLS,GRID_MAX_ROWS,HolaProvider,PortalConflict,bandOfKind,bandOfType,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,deriveFields,footprintOf,footprintWith,inferSpec,layoutSegments,mergeMessage,parseDashboardSpec,renderModel,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
package/dist/index.d.cts CHANGED
@@ -306,6 +306,8 @@ interface StatWidget {
306
306
  label?: string | undefined;
307
307
  metric?: string | undefined;
308
308
  agg?: Agg | undefined;
309
+ w?: number | undefined;
310
+ h?: number | undefined;
309
311
  }
310
312
  /** A time series: `y` (a numeric column) aggregated per day of `x` (a date column); omit `y` to count
311
313
  * rows per day. `line` and `bar` differ only in how the renderer draws the same points. */
@@ -315,24 +317,32 @@ interface SeriesWidget {
315
317
  x: string;
316
318
  y?: string | undefined;
317
319
  agg?: Agg | undefined;
320
+ w?: number | undefined;
321
+ h?: number | undefined;
318
322
  }
319
323
  /** A breakdown: the count of rows per distinct value of `groupBy`. */
320
324
  interface DonutWidget {
321
325
  type: "donut";
322
326
  title?: string | undefined;
323
327
  groupBy: string;
328
+ w?: number | undefined;
329
+ h?: number | undefined;
324
330
  }
325
331
  /** A row table: `columns` (by key, in order) or every non-plumbing column when omitted. */
326
332
  interface TableWidget {
327
333
  type: "table";
328
334
  title?: string | undefined;
329
335
  columns?: string[] | undefined;
336
+ w?: number | undefined;
337
+ h?: number | undefined;
330
338
  }
331
339
  /** Static prose — a note above or between the data widgets. */
332
340
  interface TextWidget {
333
341
  type: "text";
334
342
  title?: string | undefined;
335
343
  body: string;
344
+ w?: number | undefined;
345
+ h?: number | undefined;
336
346
  }
337
347
  type Widget = StatWidget | SeriesWidget | DonutWidget | TableWidget | TextWidget;
338
348
  interface DashboardSpec {
@@ -357,29 +367,91 @@ type ResolvedWidget = {
357
367
  kind: "stat";
358
368
  label: string;
359
369
  value: number;
370
+ w?: number | undefined;
371
+ h?: number | undefined;
360
372
  } | {
361
373
  kind: "series";
362
374
  draw: "line" | "bar";
363
375
  title: string;
364
376
  points: Point[];
377
+ w?: number | undefined;
378
+ h?: number | undefined;
365
379
  } | {
366
380
  kind: "donut";
367
381
  title: string;
368
382
  points: Point[];
383
+ w?: number | undefined;
384
+ h?: number | undefined;
369
385
  } | {
370
386
  kind: "table";
371
387
  title?: string | undefined;
372
388
  columns: PortalField[];
373
389
  rows: PortalRow[];
390
+ w?: number | undefined;
391
+ h?: number | undefined;
374
392
  } | {
375
393
  kind: "text";
376
394
  title?: string | undefined;
377
395
  body: string;
396
+ w?: number | undefined;
397
+ h?: number | undefined;
378
398
  };
379
399
  /** Resolve a whole spec against the data. The result is render-ready and free of widgets that would
380
400
  * draw nothing. */
381
401
  declare function renderModel(spec: DashboardSpec, fields: PortalField[], rows: PortalRow[]): ResolvedWidget[];
382
402
 
403
+ type Band = "stat" | "chart" | "block";
404
+ type StatView = Extract<ResolvedWidget, {
405
+ kind: "stat";
406
+ }>;
407
+ type ChartView = Extract<ResolvedWidget, {
408
+ kind: "series" | "donut";
409
+ }>;
410
+ declare const GRID_MAX_COLS = 6;
411
+ declare const GRID_MAX_ROWS = 4;
412
+ /** The band a resolved widget belongs to. */
413
+ declare function bandOfKind(kind: ResolvedWidget["kind"]): Band;
414
+ /** The band a spec widget TYPE belongs to — for the editor canvas, which lays out widgets before they
415
+ * resolve (a half-configured widget still needs a cell). Mirrors bandOfKind. */
416
+ declare function bandOfType(type: WidgetType): Band;
417
+ /** A band's fixed bento footprint — `span` cells wide × `h` cells tall. The editable canvas lays out
418
+ * spec widgets before they resolve, so it reads this directly to match the rendered dashboard. */
419
+ declare function footprintOf(band: Band): {
420
+ span: number;
421
+ h: number;
422
+ };
423
+ /** A widget's effective footprint: its hand-dragged `w`×`h` override (clamped to the grid), else the
424
+ * band default. The renderers and the editable canvas both size cells through this, so a resized
425
+ * widget looks the same everywhere. */
426
+ declare function footprintWith(band: Band, w: number | undefined, h: number | undefined): {
427
+ span: number;
428
+ h: number;
429
+ };
430
+ /** A resolved widget placed in the grid — the view-model plus its cell footprint (column span `span`,
431
+ * row span `h`) and band, ready to render. */
432
+ interface LaidOutWidget {
433
+ widget: ResolvedWidget;
434
+ span: number;
435
+ h: number;
436
+ band: Band;
437
+ }
438
+ /** A run of the dashboard: either a fixed-cell BENTO grid (stats + charts, whose cells are squares
439
+ * that align to the grid), or a full-width BLOCK (a table/note, whose height is its content). Splitting
440
+ * them keeps the fixed grid strict — a variable-height table never gets crammed into a fixed cell —
441
+ * while everything that snaps to a cell, does. Order is preserved. */
442
+ type LayoutSegment = {
443
+ kind: "grid";
444
+ cells: LaidOutWidget[];
445
+ } | {
446
+ kind: "block";
447
+ widget: ResolvedWidget;
448
+ };
449
+ /** Arrange resolved widgets into bento segments: consecutive stats/charts coalesce into one grid (a
450
+ * flat run of cells — the renderer flows them into the 4-cell rows); each table/note becomes its own
451
+ * full-width block. This is the arrangement every surface renders, so a dashboard reads identically
452
+ * everywhere. */
453
+ declare function layoutSegments(resolved: ResolvedWidget[]): LayoutSegment[];
454
+
383
455
  /** Extract a streamed text delta from a raw pi turn event, or null. */
384
456
  declare const textDeltaOf: (event: unknown) => string | null;
385
457
  /** Extract a streamed reasoning delta from a raw pi turn event, or null. */
@@ -436,4 +508,4 @@ declare const useThread: (client: HolaClient, options?: {
436
508
  pollMs?: number;
437
509
  }) => UseThread;
438
510
 
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 };
511
+ export { type Agg, type ApiTransportConfig, type Band, type ChartView, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, GRID_MAX_COLS, GRID_MAX_ROWS, 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 LaidOutWidget, type LayoutSegment, type Point, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ResolvedWidget, type SeriesWidget, type StatView, type StatWidget, type TableWidget, type TextWidget, type ThreadMessage, type UseChat, type UseThread, type Widget, type WidgetTransportConfig, type WidgetType, bandOfKind, bandOfType, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, deriveFields, footprintOf, footprintWith, inferSpec, layoutSegments, mergeMessage, parseDashboardSpec, renderModel, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
package/dist/index.d.ts CHANGED
@@ -306,6 +306,8 @@ interface StatWidget {
306
306
  label?: string | undefined;
307
307
  metric?: string | undefined;
308
308
  agg?: Agg | undefined;
309
+ w?: number | undefined;
310
+ h?: number | undefined;
309
311
  }
310
312
  /** A time series: `y` (a numeric column) aggregated per day of `x` (a date column); omit `y` to count
311
313
  * rows per day. `line` and `bar` differ only in how the renderer draws the same points. */
@@ -315,24 +317,32 @@ interface SeriesWidget {
315
317
  x: string;
316
318
  y?: string | undefined;
317
319
  agg?: Agg | undefined;
320
+ w?: number | undefined;
321
+ h?: number | undefined;
318
322
  }
319
323
  /** A breakdown: the count of rows per distinct value of `groupBy`. */
320
324
  interface DonutWidget {
321
325
  type: "donut";
322
326
  title?: string | undefined;
323
327
  groupBy: string;
328
+ w?: number | undefined;
329
+ h?: number | undefined;
324
330
  }
325
331
  /** A row table: `columns` (by key, in order) or every non-plumbing column when omitted. */
326
332
  interface TableWidget {
327
333
  type: "table";
328
334
  title?: string | undefined;
329
335
  columns?: string[] | undefined;
336
+ w?: number | undefined;
337
+ h?: number | undefined;
330
338
  }
331
339
  /** Static prose — a note above or between the data widgets. */
332
340
  interface TextWidget {
333
341
  type: "text";
334
342
  title?: string | undefined;
335
343
  body: string;
344
+ w?: number | undefined;
345
+ h?: number | undefined;
336
346
  }
337
347
  type Widget = StatWidget | SeriesWidget | DonutWidget | TableWidget | TextWidget;
338
348
  interface DashboardSpec {
@@ -357,29 +367,91 @@ type ResolvedWidget = {
357
367
  kind: "stat";
358
368
  label: string;
359
369
  value: number;
370
+ w?: number | undefined;
371
+ h?: number | undefined;
360
372
  } | {
361
373
  kind: "series";
362
374
  draw: "line" | "bar";
363
375
  title: string;
364
376
  points: Point[];
377
+ w?: number | undefined;
378
+ h?: number | undefined;
365
379
  } | {
366
380
  kind: "donut";
367
381
  title: string;
368
382
  points: Point[];
383
+ w?: number | undefined;
384
+ h?: number | undefined;
369
385
  } | {
370
386
  kind: "table";
371
387
  title?: string | undefined;
372
388
  columns: PortalField[];
373
389
  rows: PortalRow[];
390
+ w?: number | undefined;
391
+ h?: number | undefined;
374
392
  } | {
375
393
  kind: "text";
376
394
  title?: string | undefined;
377
395
  body: string;
396
+ w?: number | undefined;
397
+ h?: number | undefined;
378
398
  };
379
399
  /** Resolve a whole spec against the data. The result is render-ready and free of widgets that would
380
400
  * draw nothing. */
381
401
  declare function renderModel(spec: DashboardSpec, fields: PortalField[], rows: PortalRow[]): ResolvedWidget[];
382
402
 
403
+ type Band = "stat" | "chart" | "block";
404
+ type StatView = Extract<ResolvedWidget, {
405
+ kind: "stat";
406
+ }>;
407
+ type ChartView = Extract<ResolvedWidget, {
408
+ kind: "series" | "donut";
409
+ }>;
410
+ declare const GRID_MAX_COLS = 6;
411
+ declare const GRID_MAX_ROWS = 4;
412
+ /** The band a resolved widget belongs to. */
413
+ declare function bandOfKind(kind: ResolvedWidget["kind"]): Band;
414
+ /** The band a spec widget TYPE belongs to — for the editor canvas, which lays out widgets before they
415
+ * resolve (a half-configured widget still needs a cell). Mirrors bandOfKind. */
416
+ declare function bandOfType(type: WidgetType): Band;
417
+ /** A band's fixed bento footprint — `span` cells wide × `h` cells tall. The editable canvas lays out
418
+ * spec widgets before they resolve, so it reads this directly to match the rendered dashboard. */
419
+ declare function footprintOf(band: Band): {
420
+ span: number;
421
+ h: number;
422
+ };
423
+ /** A widget's effective footprint: its hand-dragged `w`×`h` override (clamped to the grid), else the
424
+ * band default. The renderers and the editable canvas both size cells through this, so a resized
425
+ * widget looks the same everywhere. */
426
+ declare function footprintWith(band: Band, w: number | undefined, h: number | undefined): {
427
+ span: number;
428
+ h: number;
429
+ };
430
+ /** A resolved widget placed in the grid — the view-model plus its cell footprint (column span `span`,
431
+ * row span `h`) and band, ready to render. */
432
+ interface LaidOutWidget {
433
+ widget: ResolvedWidget;
434
+ span: number;
435
+ h: number;
436
+ band: Band;
437
+ }
438
+ /** A run of the dashboard: either a fixed-cell BENTO grid (stats + charts, whose cells are squares
439
+ * that align to the grid), or a full-width BLOCK (a table/note, whose height is its content). Splitting
440
+ * them keeps the fixed grid strict — a variable-height table never gets crammed into a fixed cell —
441
+ * while everything that snaps to a cell, does. Order is preserved. */
442
+ type LayoutSegment = {
443
+ kind: "grid";
444
+ cells: LaidOutWidget[];
445
+ } | {
446
+ kind: "block";
447
+ widget: ResolvedWidget;
448
+ };
449
+ /** Arrange resolved widgets into bento segments: consecutive stats/charts coalesce into one grid (a
450
+ * flat run of cells — the renderer flows them into the 4-cell rows); each table/note becomes its own
451
+ * full-width block. This is the arrangement every surface renders, so a dashboard reads identically
452
+ * everywhere. */
453
+ declare function layoutSegments(resolved: ResolvedWidget[]): LayoutSegment[];
454
+
383
455
  /** Extract a streamed text delta from a raw pi turn event, or null. */
384
456
  declare const textDeltaOf: (event: unknown) => string | null;
385
457
  /** Extract a streamed reasoning delta from a raw pi turn event, or null. */
@@ -436,4 +508,4 @@ declare const useThread: (client: HolaClient, options?: {
436
508
  pollMs?: number;
437
509
  }) => UseThread;
438
510
 
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 };
511
+ export { type Agg, type ApiTransportConfig, type Band, type ChartView, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, GRID_MAX_COLS, GRID_MAX_ROWS, 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 LaidOutWidget, type LayoutSegment, type Point, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ResolvedWidget, type SeriesWidget, type StatView, type StatWidget, type TableWidget, type TextWidget, type ThreadMessage, type UseChat, type UseThread, type Widget, type WidgetTransportConfig, type WidgetType, bandOfKind, bandOfType, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, deriveFields, footprintOf, footprintWith, inferSpec, layoutSegments, 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 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(`
2
+ var j=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},M=e=>{let t=j(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{let t=j(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},A=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 pe=/^ /,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"}},ce=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(pe,"")));return s.length>0?{event:t,data:s.join(`
4
+ `)}:null},ge=(e,t,s)=>{let o;try{o=JSON.parse(e.data)}catch{return}if(e.event==="session"){let r=o.sessionId;r&&(s(r),t.onSession?.(r));return}if(e.event==="turn_event"){t.onEvent?.(o);let r=M(o);r&&t.onText?.(r);let l=E(o);l&&t.onThinking?.(l);let n=A(o);n&&t.onTool?.(n);return}if(e.event==="chat_completed"){let r=o;s(r.sessionId),t.onComplete?.(r);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()},G=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,s,o)=>{let r=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:o});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 y=a.slice(0,u);a=a.slice(u+2);let c=re(y);c&&ne(c,t,s),u=a.indexOf(`
6
+ `);for(;u!==-1;){let y=a.slice(0,u);a=a.slice(u+2);let c=ce(y);c&&ge(c,t,s),u=a.indexOf(`
7
7
 
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};
8
+ `)}},d=async()=>{let u=await l.read();if(!u.done)return a+=n.decode(u.value,{stream:!0}),i(),d()};await d()};var me=/\/+$/,C="/api/v1/end-user",fe=e=>{let t=e.baseUrl.replace(me,""),s=()=>typeof e.token=="function"?e.token():e.token,o=n=>e.project?`${n}project=${encodeURIComponent(e.project)}`:"",r=async()=>({"content-type":"application/json",authorization:`Bearer ${await s()}`}),l=async n=>{let a=await fetch(`${t}${C}/chat${o("?")}`,{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()=>b(`${t}${C}/chat/info${o("?")}`,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?.(T(i))}},subscribe:(n,a)=>{let i=a?.since??"0",d=!1,u,y=a?.intervalMs??3e3,c=async()=>{try{let m=await b(`${t}${C}/chat/thread?since=${encodeURIComponent(i)}${o("&")}`,await r());for(let g of m.messages)n.onMessage(g);i=m.cursor,n.onHealthy?.()}catch(m){n.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}${C}/chat/request-human${o("?")}`,{method:"POST",headers:await r(),body:"{}"})}}};import{useEffect as ye,useState as be}from"react";var $=(e,t)=>{let[s,o]=be("");return ye(()=>{if(!e){o("");return}let r=Uint8Array.from(atob(e),n=>n.charCodeAt(0)),l=URL.createObjectURL(new Blob([r],{type:t}));return o(l),()=>URL.revokeObjectURL(l)},[e,t]),s};import{useEffect as ke,useRef as Re}from"react";import{createContext as he,useContext as we}from"react";import{jsx as Te}from"react/jsx-runtime";var J=he(null),xe=({client:e,children:t})=>Te(J.Provider,{value:e,children:t}),D=()=>{let e=we(J);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as V,useEffect as Pe,useRef as Se,useState as H}from"react";var O=e=>{let[t,s]=H([]),[o,r]=H(""),[l,n]=H(!1),[a,i]=H(null),d=Se(0),u=V(()=>{let c=d.current;return d.current+=1,c},[]);Pe(()=>{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=V(()=>{let c=o.trim();if(!c||l)return;r(""),i(null),n(!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(W=>W.id===m?f(W):W))};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})),n(!1)},onError:f=>{g(h=>({...h,streaming:!1})),i(f.error),n(!1)}}).catch(f=>{g(h=>({...h,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),n(!1)})},[o,l,e,u]);return{messages:t,input:o,setInput:r,send:y,sending:l,error:a}};import{jsx as P,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"}},Ce=({artifact:e})=>{let t=$(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:w.image})}):P("a",{download:e.filename,href:s,style:w.link,children:e.filename}):null},He=()=>{let e=D(),{messages:t,input:s,setInput:o,send:r,sending:l,error:n}=O(e),a=Re(null);ke(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),r()};return U("div",{style:w.root,children:[P("div",{ref:a,style:w.log,children:t.map(d=>U("div",{style:d.role==="user"?w.user:w.assistant,children:[d.text||(d.streaming?"\u2026":""),d.artifacts&&d.artifacts.length>0?P("div",{style:w.artifacts,children:d.artifacts.map(u=>P(Ce,{artifact:u},u.filename))}):null]},d.id))}),n?P("div",{style:w.error,children:n}):null,U("form",{onSubmit:i,style:w.form,children:[P("input",{"aria-label":"Message",onChange:d=>o(d.target.value),placeholder:"Type a message\u2026",style:w.input,value:s}),P("button",{disabled:l||s.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var ve=/\/+$/,_=e=>{let t=e.sessionId,s=e.baseUrl.replace(ve,""),o=n=>`${s}/endpoint/${encodeURIComponent(e.token)}${n}`,r={"content-type":"application/json"},l=(n,a)=>({url:o(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:()=>b(o("/config"),{}),sendMessage:async n=>{let a=await G(l("/message",n));return t=a.sessionId,a},streamMessage:(n,a,i)=>z(l("/message/stream",n),a,d=>{t=d},i),subscribe:(n,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)n.onMessage(f);i=g.cursor,n.onHealthy?.()}catch(g){n.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:r,body:JSON.stringify({sessionId:t})})}}};var Ie=e=>"transport"in e?e.transport:_(e);var We=/^https?:\/\//i,Me=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,Ee=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function Ae(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(We.test(e))return"url";if(Me.test(e))return"date"}return"text"}function q(e){let t=[],s=new Map;for(let o of e)for(let[r,l]of Object.entries(o.data??{})){if(l==null)continue;let n=s.get(r);n||(n=new Map,s.set(r,n),t.push(r));let a=Ae(l);n.set(a,(n.get(a)??0)+1)}return t.map(o=>{let r=s.get(o)??new Map,l="text",n=-1;for(let[a,i]of r)i>n&&(n=i,l=a);return{key:o,label:Ee(o),type:l}})}var $e=e=>typeof e=="number"?e:Number(e)||0,X=e=>/rate|position|rank|score|percent|avg/i.test(e),De=/posted|published|sent|completed|shipped|launched|_at|date/i,Oe=new Set(["service","order","end_user_id"]);function Y(e,t){let s=e.filter(i=>!Oe.has(i.key)),o=s.filter(i=>i.type==="number").slice(0,3),r=s.find(i=>i.key==="status")??s.find(i=>i.type==="select"),l=s.find(i=>i.type==="date"&&De.test(i.key))??s.find(i=>i.type==="date"),n=s.filter(i=>i.type==="number"&&!X(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+$e(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=X(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:s.map(i=>i.key)}),{version:1,widgets:a}}var K={stat:{span:1,h:1},chart:{span:2,h:2},block:{span:0,h:0}},F=6,L=4,Ue=e=>Math.max(1,Math.min(F,Math.round(e))),_e=e=>Math.max(1,Math.min(L,Math.round(e)));function N(e){return e==="stat"?"stat":e==="series"||e==="donut"?"chart":"block"}function Q(e){return e==="stat"?"stat":e==="line"||e==="bar"||e==="donut"?"chart":"block"}function Z(e){return K[e]}function B(e,t,s){let o=K[e];return{span:t===void 0?o.span:Ue(t),h:s===void 0?o.h:_e(s)}}function ee(e){let t=[],s=null;for(let o of e){let r=N(o.kind);if(r==="block"){t.push({kind:"block",widget:o}),s=null;continue}let l=B(r,o.w,o.h),n={widget:o,span:l.span,h:l.h,band:r};s?s.cells.push(n):(s={kind:"grid",cells:[n]},t.push(s))}return t}var te=new Set(["service","order","end_user_id"]),ne=e=>typeof e=="number"?e:Number(e)||0,re=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 s=t.reduce((o,r)=>o+r,0);return e==="avg"?Math.round(s/t.length):s}function Fe(e,t,s,o){let r=new Map;for(let l of e){let n=new Date(re(l.data[t]));if(Number.isNaN(n.getTime()))continue;let a=n.toISOString().slice(0,10),i=r.get(a)??[];i.push(s?ne(l.data[s]):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:s?oe(o,n):n.length}))}function Le(e,t){let s=new Map;for(let o of e){let r=re(o.data[t]).trim();r&&s.set(r,(s.get(r)??0)+1)}return[...s.entries()].sort((o,r)=>r[1]-o[1]).map(([o,r])=>({label:o,value:r}))}function Ne(e,t,s,o){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:o.length};let l=o.map(n=>ne(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=Fe(o,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=Le(o,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)):s;return r.length===0?null:{kind:"table",title:e.title,columns:r,rows:o}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function se(e,t,s){let o=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=Ne(n,r,o,s);a&&l.push({...a,w:n.w,h:n.h})}return l}var Be=new Set(["sum","avg","count","min","max","last"]),ae=e=>typeof e=="string"&&Be.has(e)?e:void 0,x=e=>typeof e=="string"&&e!==""?e:void 0,k=e=>typeof e=="number"&&e>=1?Math.min(6,Math.round(e)):void 0,R=e=>typeof e=="number"&&e>=1?Math.min(4,Math.round(e)):void 0;function je(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:x(t.label),metric:x(t.metric),agg:ae(t.agg),w:k(t.w),h:R(t.h)};case"line":case"bar":{let s=x(t.x);return s?{type:t.type,title:x(t.title),x:s,y:x(t.y),agg:ae(t.agg),w:k(t.w),h:R(t.h)}:null}case"donut":{let s=x(t.groupBy);return s?{type:"donut",title:x(t.title),groupBy:s,w:k(t.w),h:R(t.h)}:null}case"table":{let s=Array.isArray(t.columns)?t.columns.filter(o=>typeof o=="string"):void 0;return{type:"table",title:x(t.title),columns:s,w:k(t.w),h:R(t.h)}}case"text":{let s=x(t.body);return s?{type:"text",title:x(t.title),body:s,w:k(t.w),h:R(t.h)}:null}default:return null}}function ie(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(je).filter(l=>l!==null);if(o.length===0)return null;let r=t.version;return{version:typeof r=="number"?r:1,title:x(t.title),widgets:o}}var Ge=/\/+$/,S="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function ze(e){let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)}var Je=e=>{let t=e.baseUrl.replace(Ge,""),s=()=>typeof e.token=="function"?e.token():e.token,o=async()=>({"content-type":"application/json",authorization:`Bearer ${await s()}`}),r=async(n,a)=>{try{let i=await fetch(`${t}${S}${n}`,{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 v(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 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=n=>`/tables/${encodeURIComponent(n)}`;return{listTables:async()=>(await b(`${t}${S}/tables`,await o())).tables,listRows:async(n,a)=>await b(`${t}${S}${l(n)}/rows?limit=${String(a??100)}`,await o()),insertRow:async(n,a)=>{let{rowIds:i}=await r(`${l(n)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw T(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 b(`${t}${S}/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(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:ze(i),...a===void 0?{}:{message:a}}})},fileLink:async n=>await b(`${t}${S}/files/${encodeURIComponent(n)}/url`,await o()),deleteFile:async n=>(await r(`/files/${encodeURIComponent(n)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${S}/documents`,await o())).documents,readDocument:async n=>(await b(`${t}${S}/documents/${encodeURIComponent(n)}`,await o())).document,saveDocument:async(n,a)=>await r(`/documents/${encodeURIComponent(n)}`,{method:"PATCH",body:a})}};import{useCallback as Ve,useEffect as le,useRef as qe,useState as I}from"react";var de="greeting";function ue(e,t){if(e.some(r=>r.id===t.id))return e;let s=e.filter(r=>r.id!==de);return[...t.author==="client"?s.filter(r=>!(r.pending&&r.author==="client"&&r.text===t.text)):s,t]}var Xe=(e,t)=>{let[s,o]=I([]),[r,l]=I(""),[n,a]=I(!1),[i,d]=I(null),u=qe(0),y=t?.pollMs??2e3;le(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&o(g=>g.length===0?[{id:de,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),le(()=>e.subscribe?.({onMessage:p=>o(m=>ue(m,p)),onError:p=>d(p.error),onHealthy:()=>d(null)},{intervalMs:y}),[e,y]);let c=Ve(()=>{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};o(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:s,input:r,setInput:l,send:c,sending:n,error:i}};export{He as Chat,F as GRID_MAX_COLS,L as GRID_MAX_ROWS,xe as HolaProvider,v as PortalConflict,N as bandOfKind,Q as bandOfType,fe as createApiTransport,Ie as createHolaClient,Je as createPortalData,_ as createWidgetTransport,q as deriveFields,Z as footprintOf,B as footprintWith,Y as inferSpec,ee as layoutSegments,ue as mergeMessage,ie as parseDashboardSpec,se as renderModel,M as textDeltaOf,E as thinkingDeltaOf,A as toolEventOf,$ as useArtifactUrl,O as useChat,D as useHolaClient,Xe as useThread};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holaboss/client",
3
- "version": "0.2.0-beta.6",
3
+ "version": "0.2.0-beta.8",
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",