@gamention/pulse-core 0.1.9 → 0.1.11

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.d.ts CHANGED
@@ -92,6 +92,8 @@ export declare class PulseClient extends Emitter {
92
92
  }
93
93
 
94
94
  export declare class StateManager extends Emitter {
95
+ /** HTTP base URL for resolving relative attachment paths (e.g. "http://localhost:4000"). */
96
+ baseUrl: string;
95
97
  private _user;
96
98
  private _users;
97
99
  private _presence;
@@ -114,6 +116,11 @@ export declare class StateManager extends Emitter {
114
116
  get viewports(): Map<string, ViewportInfo>;
115
117
  getViewport(userId: string): ViewportInfo | undefined;
116
118
  get selections(): Map<string, SelectionRange | null>;
119
+ /** Resolve a relative URL to absolute using baseUrl. */
120
+ private resolveUrl;
121
+ private resolveAttachments;
122
+ private resolveComment;
123
+ private resolveThread;
117
124
  handleMessage(msg: ServerMessage): void;
118
125
  reset(): void;
119
126
  }
@@ -1 +1 @@
1
- "use strict";var u=Object.defineProperty;var _=(a,n,e)=>n in a?u(a,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[n]=e;var i=(a,n,e)=>_(a,typeof n!="symbol"?n+"":n,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f="ws://localhost:4567",h=50,w=3e4,y=1e3,m=3e4;class c{constructor(){i(this,"handlers",new Map)}on(n,e){this.handlers.has(n)||this.handlers.set(n,new Set);const t=this.handlers.get(n);return t.add(e),()=>t.delete(e)}off(n,e){var t;(t=this.handlers.get(n))==null||t.delete(e)}emit(n,e){var t;(t=this.handlers.get(n))==null||t.forEach(s=>s(e))}removeAll(){this.handlers.clear()}}class d extends c{constructor(e){super();i(this,"ws",null);i(this,"endpoint");i(this,"reconnectAttempt",0);i(this,"reconnectTimer",null);i(this,"_state","disconnected");this.endpoint=e??f}get state(){return this._state}connect(){this.ws||(this._state="connecting",this.emit("state",this._state),this.ws=new WebSocket(this.endpoint),this.ws.addEventListener("open",()=>{this._state="connected",this.reconnectAttempt=0,this.emit("state",this._state)}),this.ws.addEventListener("message",e=>{const t=JSON.parse(e.data);this.emit("message",t)}),this.ws.addEventListener("close",()=>{this.ws=null,this._state="disconnected",this.emit("state",this._state),this.scheduleReconnect()}),this.ws.addEventListener("error",()=>{var e;(e=this.ws)==null||e.close()}))}disconnect(){var e;this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.reconnectAttempt=0,(e=this.ws)==null||e.close(),this.ws=null,this._state="disconnected",this.emit("state",this._state)}send(e){var t;((t=this.ws)==null?void 0:t.readyState)===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}scheduleReconnect(){const e=Math.min(y*2**this.reconnectAttempt,m);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},e)}}class p extends c{constructor(){super(...arguments);i(this,"_user",null);i(this,"_users",new Map);i(this,"_presence",new Map);i(this,"_threads",new Map);i(this,"_reactions",new Map);i(this,"_notifications",[]);i(this,"_activityLogs",[]);i(this,"_typing",new Map);i(this,"_viewports",new Map);i(this,"_selections",new Map)}get user(){return this._user}get presence(){return[...this._presence.values()]}get threads(){return[...this._threads.values()]}get notifications(){return this._notifications}get unreadCount(){return this._notifications.filter(e=>!e.read).length}get activityLogs(){return this._activityLogs}getUser(e){return this._users.get(e)}getReactions(e){return this._reactions.get(e)??[]}getTypingUsers(e){const t=this._typing.get(e);if(!t)return[];const s=Date.now(),r=[];for(const[o,l]of t)s-l<3e3&&r.push(o);return r}get viewports(){return this._viewports}getViewport(e){return this._viewports.get(e)}get selections(){return this._selections}handleMessage(e){switch(e.type){case"auth:ok":this._user=e.user,this._users.clear();for(const t of e.users)this._users.set(t.id,t);this._presence.clear();for(const t of e.presence)this._presence.set(t.user.id,t),this._users.set(t.user.id,t.user);this._users.set(e.user.id,e.user),this._threads.clear();for(const t of e.threads)this._threads.set(t.id,t);this._notifications=e.notifications,this._reactions.clear();for(const t of e.reactions){const s=this._reactions.get(t.targetId)??[];s.push(t),this._reactions.set(t.targetId,s)}this._activityLogs=[...e.activityLogs],this.emit("auth",e.user),this.emit("presence",this.presence),this.emit("threads",this.threads),this.emit("notifications",this._notifications),this.emit("reactions",null),this.emit("activity-logs",this._activityLogs);break;case"presence:join":this._presence.set(e.user.user.id,e.user),this._users.set(e.user.user.id,e.user.user),this.emit("presence",this.presence);break;case"presence:leave":this._presence.delete(e.userId),this._viewports.delete(e.userId),this._selections.delete(e.userId);for(const t of this._typing.values())t.delete(e.userId);this.emit("presence",this.presence);break;case"presence:update":{const t=this._presence.get(e.userId);t&&(t.status=e.status,this.emit("presence",this.presence));break}case"cursor:move":this.emit("cursor",{userId:e.userId,position:e.position});break;case"click:perform":this.emit("click",{userId:e.userId,position:e.position});break;case"thread:created":this._threads.set(e.thread.id,e.thread),this.emit("threads",this.threads);break;case"comment:created":{const t=this._threads.get(e.threadId);t&&(t.comments.push(e.comment),t.updatedAt=e.comment.createdAt,this.emit("threads",this.threads));break}case"comment:edited":{const t=this._threads.get(e.threadId);if(t){const s=t.comments.findIndex(r=>r.id===e.comment.id);s!==-1&&(t.comments[s]=e.comment),this.emit("threads",this.threads)}break}case"comment:deleted":{const t=this._threads.get(e.threadId);t&&(t.comments=t.comments.filter(s=>s.id!==e.commentId),t.comments.length===0&&this._threads.delete(e.threadId),this.emit("threads",this.threads));break}case"thread:resolved":{const t=this._threads.get(e.threadId);t&&(t.resolved=e.resolved,this.emit("threads",this.threads));break}case"thread:deleted":this._threads.delete(e.threadId),this.emit("threads",this.threads);break;case"reaction:added":{const t=this._reactions.get(e.reaction.targetId)??[];t.push(e.reaction),this._reactions.set(e.reaction.targetId,t),this.emit("reactions",{targetId:e.reaction.targetId,reactions:t});break}case"reaction:removed":{const t=this._reactions.get(e.targetId);if(t){const s=t.filter(r=>r.id!==e.reactionId);this._reactions.set(e.targetId,s),this.emit("reactions",{targetId:e.targetId,reactions:s})}break}case"notification":this._notifications.unshift(e.notification),this.emit("notifications",this._notifications);break;case"typing:indicator":{this._typing.has(e.threadId)||this._typing.set(e.threadId,new Map),this._typing.get(e.threadId).set(e.userId,Date.now()),this.emit("typing",{threadId:e.threadId,userId:e.userId});break}case"viewport:update":{this._viewports.set(e.userId,{scrollX:e.scrollX,scrollY:e.scrollY,viewportWidth:e.viewportWidth,viewportHeight:e.viewportHeight,pageWidth:e.pageWidth,pageHeight:e.pageHeight}),this.emit("viewport",{userId:e.userId});break}case"selection:update":this._selections.set(e.userId,e.selection),this.emit("selection",{userId:e.userId,selection:e.selection});break;case"emoji:drop":this.emit("emoji-drop",{userId:e.userId,emoji:e.emoji,position:e.position});break;case"draw:stroke":this.emit("draw-stroke",{userId:e.userId,points:e.points,color:e.color,width:e.width});break;case"draw:clear":this.emit("draw-clear",{userId:e.userId});break;case"activity:logged":this._activityLogs.unshift(e.activityLog),this._activityLogs.length>100&&(this._activityLogs=this._activityLogs.slice(0,100)),this.emit("activity-logs",this._activityLogs);break;case"error":this.emit("error",e);break}}reset(){this._user=null,this._users.clear(),this._presence.clear(),this._threads.clear(),this._reactions.clear(),this._notifications=[],this._activityLogs=[],this._typing.clear(),this._viewports.clear(),this._selections.clear()}}class I extends c{constructor(e){var s;super();i(this,"state");i(this,"connection");i(this,"config");i(this,"heartbeatTimer",null);i(this,"lastCursorSend",0);i(this,"pendingCursor",null);i(this,"cursorTimer",null);this.config=e,this.state=new p;const t=((s=e.endpoint)==null?void 0:s.replace(/^http/,"ws"))??void 0;this.connection=new d(t),this.connection.on("message",r=>{this.state.handleMessage(r),this.emit(r.type,r)}),this.connection.on("state",r=>{this.emit("connection",r),r==="connected"?(this.authenticate(),this.startHeartbeat()):r==="disconnected"&&this.stopHeartbeat()})}get connectionState(){return this.connection.state}connect(){this.connection.connect()}disconnect(){this.stopHeartbeat(),this.connection.disconnect(),this.state.reset()}authenticate(){this.send({type:"auth",apiKey:this.config.apiKey,token:this.config.token,room:this.config.room})}send(e){this.connection.send(e)}moveCursor(e){const t=Date.now();this.pendingCursor=e,t-this.lastCursorSend>=h?this.flushCursor():this.cursorTimer||(this.cursorTimer=setTimeout(()=>{this.cursorTimer=null,this.flushCursor()},h))}flushCursor(){this.pendingCursor&&(this.send({type:"cursor:move",position:this.pendingCursor}),this.lastCursorSend=Date.now(),this.pendingCursor=null)}updatePresence(e){this.send({type:"presence:update",status:e})}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.send({type:"presence:update",status:"online"})},w)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}createThread(e,t={}){const s=crypto.randomUUID();return this.send({type:"thread:create",id:s,body:e,mentions:t.mentions??[],position:t.position??null,attachmentIds:t.attachmentIds}),s}reply(e,t,s=[],r){const o=crypto.randomUUID();return this.send({type:"comment:create",threadId:e,id:o,body:t,mentions:s,attachmentIds:r}),o}editComment(e,t,s=[]){this.send({type:"comment:edit",commentId:e,body:t,mentions:s})}deleteComment(e){this.send({type:"comment:delete",commentId:e})}resolveThread(e,t=!0){this.send({type:"thread:resolve",threadId:e,resolved:t})}addReaction(e,t,s){this.send({type:"reaction:add",targetId:e,targetType:t,emoji:s})}removeReaction(e){this.send({type:"reaction:remove",reactionId:e})}markRead(e){this.send({type:"notification:read",notificationId:e})}markAllRead(){this.send({type:"notification:read-all"})}performClick(e){this.send({type:"click:perform",position:e})}sendTyping(e){this.send({type:"typing:start",threadId:e})}updateViewport(e){this.send({type:"viewport:update",...e})}updateSelection(e){this.send({type:"selection:update",selection:e})}dropEmoji(e,t){this.send({type:"emoji:drop",emoji:e,position:t})}drawStroke(e,t,s){this.send({type:"draw:stroke",points:e,color:t,width:s})}clearDrawing(){this.send({type:"draw:clear"})}async uploadFile(e){const t=(this.config.endpoint??window.location.origin).replace(/^ws(s?):/,"http$1:"),s=new FormData;s.append("file",e);const r=await fetch(`${t}/api/v1/upload`,{method:"POST",headers:{"X-Pulse-Key":this.config.apiKey,"X-Pulse-Token":this.config.token},body:s});if(!r.ok){const o=await r.json().catch(()=>({error:"Upload failed"}));throw new Error(o.error??"Upload failed")}return r.json()}setAppearOffline(e){e?(this.stopHeartbeat(),this.send({type:"presence:update",status:"idle"})):(this.startHeartbeat(),this.send({type:"presence:update",status:"online"}))}}exports.Connection=d;exports.Emitter=c;exports.PulseClient=I;exports.StateManager=p;
1
+ "use strict";var u=Object.defineProperty;var _=(o,n,t)=>n in o?u(o,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[n]=t;var i=(o,n,t)=>_(o,typeof n!="symbol"?n+"":n,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f="ws://localhost:4567",d=50,m=3e4,w=1e3,v=3e4;class h{constructor(){i(this,"handlers",new Map)}on(n,t){this.handlers.has(n)||this.handlers.set(n,new Set);const e=this.handlers.get(n);return e.add(t),()=>e.delete(t)}off(n,t){var e;(e=this.handlers.get(n))==null||e.delete(t)}emit(n,t){var e;(e=this.handlers.get(n))==null||e.forEach(s=>s(t))}removeAll(){this.handlers.clear()}}class l extends h{constructor(t){super();i(this,"ws",null);i(this,"endpoint");i(this,"reconnectAttempt",0);i(this,"reconnectTimer",null);i(this,"_state","disconnected");this.endpoint=t??f}get state(){return this._state}connect(){this.ws||(this._state="connecting",this.emit("state",this._state),this.ws=new WebSocket(this.endpoint),this.ws.addEventListener("open",()=>{this._state="connected",this.reconnectAttempt=0,this.emit("state",this._state)}),this.ws.addEventListener("message",t=>{const e=JSON.parse(t.data);this.emit("message",e)}),this.ws.addEventListener("close",()=>{this.ws=null,this._state="disconnected",this.emit("state",this._state),this.scheduleReconnect()}),this.ws.addEventListener("error",()=>{var t;(t=this.ws)==null||t.close()}))}disconnect(){var t;this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.reconnectAttempt=0,(t=this.ws)==null||t.close(),this.ws=null,this._state="disconnected",this.emit("state",this._state)}send(t){var e;((e=this.ws)==null?void 0:e.readyState)===WebSocket.OPEN&&this.ws.send(JSON.stringify(t))}scheduleReconnect(){const t=Math.min(w*2**this.reconnectAttempt,v);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},t)}}class p extends h{constructor(){super(...arguments);i(this,"baseUrl","");i(this,"_user",null);i(this,"_users",new Map);i(this,"_presence",new Map);i(this,"_threads",new Map);i(this,"_reactions",new Map);i(this,"_notifications",[]);i(this,"_activityLogs",[]);i(this,"_typing",new Map);i(this,"_viewports",new Map);i(this,"_selections",new Map)}get user(){return this._user}get presence(){return[...this._presence.values()]}get threads(){return[...this._threads.values()]}get notifications(){return this._notifications}get unreadCount(){return this._notifications.filter(t=>!t.read).length}get activityLogs(){return this._activityLogs}getUser(t){return this._users.get(t)}getReactions(t){return this._reactions.get(t)??[]}getTypingUsers(t){const e=this._typing.get(t);if(!e)return[];const s=Date.now(),r=[];for(const[a,c]of e)s-c<3e3&&r.push(a);return r}get viewports(){return this._viewports}getViewport(t){return this._viewports.get(t)}get selections(){return this._selections}resolveUrl(t){return!this.baseUrl||!t||t.startsWith("http://")||t.startsWith("https://")?t:`${this.baseUrl}${t}`}resolveAttachments(t){return t.map(e=>({...e,url:this.resolveUrl(e.url),thumbnailUrl:e.thumbnailUrl?this.resolveUrl(e.thumbnailUrl):void 0}))}resolveComment(t){return!t.attachments||t.attachments.length===0?t:{...t,attachments:this.resolveAttachments(t.attachments)}}resolveThread(t){return{...t,comments:t.comments.map(e=>this.resolveComment(e))}}handleMessage(t){switch(t.type){case"auth:ok":this._user=t.user,this._users.clear();for(const e of t.users)this._users.set(e.id,e);this._presence.clear();for(const e of t.presence)this._presence.set(e.user.id,e),this._users.set(e.user.id,e.user);this._users.set(t.user.id,t.user),this._threads.clear();for(const e of t.threads)this._threads.set(e.id,this.resolveThread(e));this._notifications=t.notifications,this._reactions.clear();for(const e of t.reactions){const s=this._reactions.get(e.targetId)??[];s.push(e),this._reactions.set(e.targetId,s)}this._activityLogs=[...t.activityLogs],this.emit("auth",t.user),this.emit("presence",this.presence),this.emit("threads",this.threads),this.emit("notifications",this._notifications),this.emit("reactions",null),this.emit("activity-logs",this._activityLogs);break;case"presence:join":this._presence.set(t.user.user.id,t.user),this._users.set(t.user.user.id,t.user.user),this.emit("presence",this.presence);break;case"presence:leave":this._presence.delete(t.userId),this._viewports.delete(t.userId),this._selections.delete(t.userId);for(const e of this._typing.values())e.delete(t.userId);this.emit("presence",this.presence);break;case"presence:update":{const e=this._presence.get(t.userId);e&&(e.status=t.status,this.emit("presence",this.presence));break}case"cursor:move":this.emit("cursor",{userId:t.userId,position:t.position});break;case"click:perform":this.emit("click",{userId:t.userId,position:t.position});break;case"thread:created":this._threads.set(t.thread.id,this.resolveThread(t.thread)),this.emit("threads",this.threads);break;case"comment:created":{const e=this._threads.get(t.threadId);e&&(e.comments.push(this.resolveComment(t.comment)),e.updatedAt=t.comment.createdAt,this.emit("threads",this.threads));break}case"comment:edited":{const e=this._threads.get(t.threadId);if(e){const s=e.comments.findIndex(r=>r.id===t.comment.id);s!==-1&&(e.comments[s]=this.resolveComment(t.comment)),this.emit("threads",this.threads)}break}case"comment:deleted":{const e=this._threads.get(t.threadId);e&&(e.comments=e.comments.filter(s=>s.id!==t.commentId),e.comments.length===0&&this._threads.delete(t.threadId),this.emit("threads",this.threads));break}case"thread:resolved":{const e=this._threads.get(t.threadId);e&&(e.resolved=t.resolved,this.emit("threads",this.threads));break}case"thread:deleted":this._threads.delete(t.threadId),this.emit("threads",this.threads);break;case"reaction:added":{const e=this._reactions.get(t.reaction.targetId)??[];e.push(t.reaction),this._reactions.set(t.reaction.targetId,e),this.emit("reactions",{targetId:t.reaction.targetId,reactions:e});break}case"reaction:removed":{const e=this._reactions.get(t.targetId);if(e){const s=e.filter(r=>r.id!==t.reactionId);this._reactions.set(t.targetId,s),this.emit("reactions",{targetId:t.targetId,reactions:s})}break}case"notification":this._notifications.unshift(t.notification),this.emit("notifications",this._notifications);break;case"typing:indicator":{this._typing.has(t.threadId)||this._typing.set(t.threadId,new Map),this._typing.get(t.threadId).set(t.userId,Date.now()),this.emit("typing",{threadId:t.threadId,userId:t.userId});break}case"viewport:update":{this._viewports.set(t.userId,{scrollX:t.scrollX,scrollY:t.scrollY,viewportWidth:t.viewportWidth,viewportHeight:t.viewportHeight,pageWidth:t.pageWidth,pageHeight:t.pageHeight}),this.emit("viewport",{userId:t.userId});break}case"selection:update":this._selections.set(t.userId,t.selection),this.emit("selection",{userId:t.userId,selection:t.selection});break;case"emoji:drop":this.emit("emoji-drop",{userId:t.userId,emoji:t.emoji,position:t.position});break;case"draw:stroke":this.emit("draw-stroke",{userId:t.userId,points:t.points,color:t.color,width:t.width});break;case"draw:clear":this.emit("draw-clear",{userId:t.userId});break;case"activity:logged":this._activityLogs.unshift(t.activityLog),this._activityLogs.length>100&&(this._activityLogs=this._activityLogs.slice(0,100)),this.emit("activity-logs",this._activityLogs);break;case"error":this.emit("error",t);break}}reset(){this._user=null,this._users.clear(),this._presence.clear(),this._threads.clear(),this._reactions.clear(),this._notifications=[],this._activityLogs=[],this._typing.clear(),this._viewports.clear(),this._selections.clear()}}class y extends h{constructor(t){var s;super();i(this,"state");i(this,"connection");i(this,"config");i(this,"heartbeatTimer",null);i(this,"lastCursorSend",0);i(this,"pendingCursor",null);i(this,"cursorTimer",null);this.config=t,this.state=new p,this.state.baseUrl=(t.endpoint??"").replace(/^ws(s?):/,"http$1:").replace(/\/$/,"");const e=((s=t.endpoint)==null?void 0:s.replace(/^http/,"ws"))??void 0;this.connection=new l(e),this.connection.on("message",r=>{this.state.handleMessage(r),this.emit(r.type,r)}),this.connection.on("state",r=>{this.emit("connection",r),r==="connected"?(this.authenticate(),this.startHeartbeat()):r==="disconnected"&&this.stopHeartbeat()})}get connectionState(){return this.connection.state}connect(){this.connection.connect()}disconnect(){this.stopHeartbeat(),this.connection.disconnect(),this.state.reset()}authenticate(){this.send({type:"auth",apiKey:this.config.apiKey,token:this.config.token,room:this.config.room})}send(t){this.connection.send(t)}moveCursor(t){const e=Date.now();this.pendingCursor=t,e-this.lastCursorSend>=d?this.flushCursor():this.cursorTimer||(this.cursorTimer=setTimeout(()=>{this.cursorTimer=null,this.flushCursor()},d))}flushCursor(){this.pendingCursor&&(this.send({type:"cursor:move",position:this.pendingCursor}),this.lastCursorSend=Date.now(),this.pendingCursor=null)}updatePresence(t){this.send({type:"presence:update",status:t})}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.send({type:"presence:update",status:"online"})},m)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}createThread(t,e={}){const s=crypto.randomUUID();return this.send({type:"thread:create",id:s,body:t,mentions:e.mentions??[],position:e.position??null,attachmentIds:e.attachmentIds}),s}reply(t,e,s=[],r){const a=crypto.randomUUID();return this.send({type:"comment:create",threadId:t,id:a,body:e,mentions:s,attachmentIds:r}),a}editComment(t,e,s=[]){this.send({type:"comment:edit",commentId:t,body:e,mentions:s})}deleteComment(t){this.send({type:"comment:delete",commentId:t})}resolveThread(t,e=!0){this.send({type:"thread:resolve",threadId:t,resolved:e})}addReaction(t,e,s){this.send({type:"reaction:add",targetId:t,targetType:e,emoji:s})}removeReaction(t){this.send({type:"reaction:remove",reactionId:t})}markRead(t){this.send({type:"notification:read",notificationId:t})}markAllRead(){this.send({type:"notification:read-all"})}performClick(t){this.send({type:"click:perform",position:t})}sendTyping(t){this.send({type:"typing:start",threadId:t})}updateViewport(t){this.send({type:"viewport:update",...t})}updateSelection(t){this.send({type:"selection:update",selection:t})}dropEmoji(t,e){this.send({type:"emoji:drop",emoji:t,position:e})}drawStroke(t,e,s){this.send({type:"draw:stroke",points:t,color:e,width:s})}clearDrawing(){this.send({type:"draw:clear"})}async uploadFile(t){const e=(this.config.endpoint??window.location.origin).replace(/^ws(s?):/,"http$1:"),s=new FormData;s.append("file",t);const r=await fetch(`${e}/api/v1/upload`,{method:"POST",headers:{"X-Pulse-Key":this.config.apiKey,"X-Pulse-Token":this.config.token},body:s});if(!r.ok){const c=await r.json().catch(()=>({error:"Upload failed"}));throw new Error(c.error??"Upload failed")}const a=await r.json();return a.url&&!a.url.startsWith("http")&&(a.url=`${e}${a.url}`),a.thumbnailUrl&&!a.thumbnailUrl.startsWith("http")&&(a.thumbnailUrl=`${e}${a.thumbnailUrl}`),a}setAppearOffline(t){t?(this.stopHeartbeat(),this.send({type:"presence:update",status:"idle"})):(this.startHeartbeat(),this.send({type:"presence:update",status:"online"}))}}exports.Connection=l;exports.Emitter=h;exports.PulseClient=y;exports.StateManager=p;
@@ -1,37 +1,37 @@
1
1
  var d = Object.defineProperty;
2
- var p = (a, n, e) => n in a ? d(a, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[n] = e;
3
- var i = (a, n, e) => p(a, typeof n != "symbol" ? n + "" : n, e);
4
- const l = "ws://localhost:4567";
2
+ var l = (o, n, t) => n in o ? d(o, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[n] = t;
3
+ var i = (o, n, t) => l(o, typeof n != "symbol" ? n + "" : n, t);
4
+ const p = "ws://localhost:4567";
5
5
  class c {
6
6
  constructor() {
7
7
  i(this, "handlers", /* @__PURE__ */ new Map());
8
8
  }
9
- on(n, e) {
9
+ on(n, t) {
10
10
  this.handlers.has(n) || this.handlers.set(n, /* @__PURE__ */ new Set());
11
- const t = this.handlers.get(n);
12
- return t.add(e), () => t.delete(e);
11
+ const e = this.handlers.get(n);
12
+ return e.add(t), () => e.delete(t);
13
13
  }
14
- off(n, e) {
15
- var t;
16
- (t = this.handlers.get(n)) == null || t.delete(e);
14
+ off(n, t) {
15
+ var e;
16
+ (e = this.handlers.get(n)) == null || e.delete(t);
17
17
  }
18
- emit(n, e) {
19
- var t;
20
- (t = this.handlers.get(n)) == null || t.forEach((s) => s(e));
18
+ emit(n, t) {
19
+ var e;
20
+ (e = this.handlers.get(n)) == null || e.forEach((s) => s(t));
21
21
  }
22
22
  removeAll() {
23
23
  this.handlers.clear();
24
24
  }
25
25
  }
26
26
  class u extends c {
27
- constructor(e) {
27
+ constructor(t) {
28
28
  super();
29
29
  i(this, "ws", null);
30
30
  i(this, "endpoint");
31
31
  i(this, "reconnectAttempt", 0);
32
32
  i(this, "reconnectTimer", null);
33
33
  i(this, "_state", "disconnected");
34
- this.endpoint = e ?? l;
34
+ this.endpoint = t ?? p;
35
35
  }
36
36
  get state() {
37
37
  return this._state;
@@ -39,37 +39,39 @@ class u extends c {
39
39
  connect() {
40
40
  this.ws || (this._state = "connecting", this.emit("state", this._state), this.ws = new WebSocket(this.endpoint), this.ws.addEventListener("open", () => {
41
41
  this._state = "connected", this.reconnectAttempt = 0, this.emit("state", this._state);
42
- }), this.ws.addEventListener("message", (e) => {
43
- const t = JSON.parse(e.data);
44
- this.emit("message", t);
42
+ }), this.ws.addEventListener("message", (t) => {
43
+ const e = JSON.parse(t.data);
44
+ this.emit("message", e);
45
45
  }), this.ws.addEventListener("close", () => {
46
46
  this.ws = null, this._state = "disconnected", this.emit("state", this._state), this.scheduleReconnect();
47
47
  }), this.ws.addEventListener("error", () => {
48
- var e;
49
- (e = this.ws) == null || e.close();
48
+ var t;
49
+ (t = this.ws) == null || t.close();
50
50
  }));
51
51
  }
52
52
  disconnect() {
53
- var e;
54
- this.reconnectTimer && (clearTimeout(this.reconnectTimer), this.reconnectTimer = null), this.reconnectAttempt = 0, (e = this.ws) == null || e.close(), this.ws = null, this._state = "disconnected", this.emit("state", this._state);
55
- }
56
- send(e) {
57
53
  var t;
58
- ((t = this.ws) == null ? void 0 : t.readyState) === WebSocket.OPEN && this.ws.send(JSON.stringify(e));
54
+ this.reconnectTimer && (clearTimeout(this.reconnectTimer), this.reconnectTimer = null), this.reconnectAttempt = 0, (t = this.ws) == null || t.close(), this.ws = null, this._state = "disconnected", this.emit("state", this._state);
55
+ }
56
+ send(t) {
57
+ var e;
58
+ ((e = this.ws) == null ? void 0 : e.readyState) === WebSocket.OPEN && this.ws.send(JSON.stringify(t));
59
59
  }
60
60
  scheduleReconnect() {
61
- const e = Math.min(
61
+ const t = Math.min(
62
62
  1e3 * 2 ** this.reconnectAttempt,
63
63
  3e4
64
64
  );
65
65
  this.reconnectAttempt++, this.reconnectTimer = setTimeout(() => {
66
66
  this.reconnectTimer = null, this.connect();
67
- }, e);
67
+ }, t);
68
68
  }
69
69
  }
70
70
  class _ extends c {
71
71
  constructor() {
72
72
  super(...arguments);
73
+ /** HTTP base URL for resolving relative attachment paths (e.g. "http://localhost:4000"). */
74
+ i(this, "baseUrl", "");
73
75
  i(this, "_user", null);
74
76
  i(this, "_users", /* @__PURE__ */ new Map());
75
77
  i(this, "_presence", /* @__PURE__ */ new Map());
@@ -94,163 +96,183 @@ class _ extends c {
94
96
  return this._notifications;
95
97
  }
96
98
  get unreadCount() {
97
- return this._notifications.filter((e) => !e.read).length;
99
+ return this._notifications.filter((t) => !t.read).length;
98
100
  }
99
101
  get activityLogs() {
100
102
  return this._activityLogs;
101
103
  }
102
- getUser(e) {
103
- return this._users.get(e);
104
+ getUser(t) {
105
+ return this._users.get(t);
104
106
  }
105
- getReactions(e) {
106
- return this._reactions.get(e) ?? [];
107
+ getReactions(t) {
108
+ return this._reactions.get(t) ?? [];
107
109
  }
108
- getTypingUsers(e) {
109
- const t = this._typing.get(e);
110
- if (!t) return [];
110
+ getTypingUsers(t) {
111
+ const e = this._typing.get(t);
112
+ if (!e) return [];
111
113
  const s = Date.now(), r = [];
112
- for (const [o, h] of t)
113
- s - h < 3e3 && r.push(o);
114
+ for (const [a, h] of e)
115
+ s - h < 3e3 && r.push(a);
114
116
  return r;
115
117
  }
116
118
  get viewports() {
117
119
  return this._viewports;
118
120
  }
119
- getViewport(e) {
120
- return this._viewports.get(e);
121
+ getViewport(t) {
122
+ return this._viewports.get(t);
121
123
  }
122
124
  get selections() {
123
125
  return this._selections;
124
126
  }
125
- handleMessage(e) {
126
- switch (e.type) {
127
+ /** Resolve a relative URL to absolute using baseUrl. */
128
+ resolveUrl(t) {
129
+ return !this.baseUrl || !t || t.startsWith("http://") || t.startsWith("https://") ? t : `${this.baseUrl}${t}`;
130
+ }
131
+ resolveAttachments(t) {
132
+ return t.map((e) => ({
133
+ ...e,
134
+ url: this.resolveUrl(e.url),
135
+ thumbnailUrl: e.thumbnailUrl ? this.resolveUrl(e.thumbnailUrl) : void 0
136
+ }));
137
+ }
138
+ resolveComment(t) {
139
+ return !t.attachments || t.attachments.length === 0 ? t : { ...t, attachments: this.resolveAttachments(t.attachments) };
140
+ }
141
+ resolveThread(t) {
142
+ return {
143
+ ...t,
144
+ comments: t.comments.map((e) => this.resolveComment(e))
145
+ };
146
+ }
147
+ handleMessage(t) {
148
+ switch (t.type) {
127
149
  case "auth:ok":
128
- this._user = e.user, this._users.clear();
129
- for (const t of e.users) this._users.set(t.id, t);
150
+ this._user = t.user, this._users.clear();
151
+ for (const e of t.users) this._users.set(e.id, e);
130
152
  this._presence.clear();
131
- for (const t of e.presence)
132
- this._presence.set(t.user.id, t), this._users.set(t.user.id, t.user);
133
- this._users.set(e.user.id, e.user), this._threads.clear();
134
- for (const t of e.threads) this._threads.set(t.id, t);
135
- this._notifications = e.notifications, this._reactions.clear();
136
- for (const t of e.reactions) {
137
- const s = this._reactions.get(t.targetId) ?? [];
138
- s.push(t), this._reactions.set(t.targetId, s);
153
+ for (const e of t.presence)
154
+ this._presence.set(e.user.id, e), this._users.set(e.user.id, e.user);
155
+ this._users.set(t.user.id, t.user), this._threads.clear();
156
+ for (const e of t.threads) this._threads.set(e.id, this.resolveThread(e));
157
+ this._notifications = t.notifications, this._reactions.clear();
158
+ for (const e of t.reactions) {
159
+ const s = this._reactions.get(e.targetId) ?? [];
160
+ s.push(e), this._reactions.set(e.targetId, s);
139
161
  }
140
- this._activityLogs = [...e.activityLogs], this.emit("auth", e.user), this.emit("presence", this.presence), this.emit("threads", this.threads), this.emit("notifications", this._notifications), this.emit("reactions", null), this.emit("activity-logs", this._activityLogs);
162
+ this._activityLogs = [...t.activityLogs], this.emit("auth", t.user), this.emit("presence", this.presence), this.emit("threads", this.threads), this.emit("notifications", this._notifications), this.emit("reactions", null), this.emit("activity-logs", this._activityLogs);
141
163
  break;
142
164
  case "presence:join":
143
- this._presence.set(e.user.user.id, e.user), this._users.set(e.user.user.id, e.user.user), this.emit("presence", this.presence);
165
+ this._presence.set(t.user.user.id, t.user), this._users.set(t.user.user.id, t.user.user), this.emit("presence", this.presence);
144
166
  break;
145
167
  case "presence:leave":
146
- this._presence.delete(e.userId), this._viewports.delete(e.userId), this._selections.delete(e.userId);
147
- for (const t of this._typing.values())
148
- t.delete(e.userId);
168
+ this._presence.delete(t.userId), this._viewports.delete(t.userId), this._selections.delete(t.userId);
169
+ for (const e of this._typing.values())
170
+ e.delete(t.userId);
149
171
  this.emit("presence", this.presence);
150
172
  break;
151
173
  case "presence:update": {
152
- const t = this._presence.get(e.userId);
153
- t && (t.status = e.status, this.emit("presence", this.presence));
174
+ const e = this._presence.get(t.userId);
175
+ e && (e.status = t.status, this.emit("presence", this.presence));
154
176
  break;
155
177
  }
156
178
  case "cursor:move":
157
- this.emit("cursor", { userId: e.userId, position: e.position });
179
+ this.emit("cursor", { userId: t.userId, position: t.position });
158
180
  break;
159
181
  case "click:perform":
160
- this.emit("click", { userId: e.userId, position: e.position });
182
+ this.emit("click", { userId: t.userId, position: t.position });
161
183
  break;
162
184
  case "thread:created":
163
- this._threads.set(e.thread.id, e.thread), this.emit("threads", this.threads);
185
+ this._threads.set(t.thread.id, this.resolveThread(t.thread)), this.emit("threads", this.threads);
164
186
  break;
165
187
  case "comment:created": {
166
- const t = this._threads.get(e.threadId);
167
- t && (t.comments.push(e.comment), t.updatedAt = e.comment.createdAt, this.emit("threads", this.threads));
188
+ const e = this._threads.get(t.threadId);
189
+ e && (e.comments.push(this.resolveComment(t.comment)), e.updatedAt = t.comment.createdAt, this.emit("threads", this.threads));
168
190
  break;
169
191
  }
170
192
  case "comment:edited": {
171
- const t = this._threads.get(e.threadId);
172
- if (t) {
173
- const s = t.comments.findIndex((r) => r.id === e.comment.id);
174
- s !== -1 && (t.comments[s] = e.comment), this.emit("threads", this.threads);
193
+ const e = this._threads.get(t.threadId);
194
+ if (e) {
195
+ const s = e.comments.findIndex((r) => r.id === t.comment.id);
196
+ s !== -1 && (e.comments[s] = this.resolveComment(t.comment)), this.emit("threads", this.threads);
175
197
  }
176
198
  break;
177
199
  }
178
200
  case "comment:deleted": {
179
- const t = this._threads.get(e.threadId);
180
- t && (t.comments = t.comments.filter((s) => s.id !== e.commentId), t.comments.length === 0 && this._threads.delete(e.threadId), this.emit("threads", this.threads));
201
+ const e = this._threads.get(t.threadId);
202
+ e && (e.comments = e.comments.filter((s) => s.id !== t.commentId), e.comments.length === 0 && this._threads.delete(t.threadId), this.emit("threads", this.threads));
181
203
  break;
182
204
  }
183
205
  case "thread:resolved": {
184
- const t = this._threads.get(e.threadId);
185
- t && (t.resolved = e.resolved, this.emit("threads", this.threads));
206
+ const e = this._threads.get(t.threadId);
207
+ e && (e.resolved = t.resolved, this.emit("threads", this.threads));
186
208
  break;
187
209
  }
188
210
  case "thread:deleted":
189
- this._threads.delete(e.threadId), this.emit("threads", this.threads);
211
+ this._threads.delete(t.threadId), this.emit("threads", this.threads);
190
212
  break;
191
213
  case "reaction:added": {
192
- const t = this._reactions.get(e.reaction.targetId) ?? [];
193
- t.push(e.reaction), this._reactions.set(e.reaction.targetId, t), this.emit("reactions", {
194
- targetId: e.reaction.targetId,
195
- reactions: t
214
+ const e = this._reactions.get(t.reaction.targetId) ?? [];
215
+ e.push(t.reaction), this._reactions.set(t.reaction.targetId, e), this.emit("reactions", {
216
+ targetId: t.reaction.targetId,
217
+ reactions: e
196
218
  });
197
219
  break;
198
220
  }
199
221
  case "reaction:removed": {
200
- const t = this._reactions.get(e.targetId);
201
- if (t) {
202
- const s = t.filter((r) => r.id !== e.reactionId);
203
- this._reactions.set(e.targetId, s), this.emit("reactions", {
204
- targetId: e.targetId,
222
+ const e = this._reactions.get(t.targetId);
223
+ if (e) {
224
+ const s = e.filter((r) => r.id !== t.reactionId);
225
+ this._reactions.set(t.targetId, s), this.emit("reactions", {
226
+ targetId: t.targetId,
205
227
  reactions: s
206
228
  });
207
229
  }
208
230
  break;
209
231
  }
210
232
  case "notification":
211
- this._notifications.unshift(e.notification), this.emit("notifications", this._notifications);
233
+ this._notifications.unshift(t.notification), this.emit("notifications", this._notifications);
212
234
  break;
213
235
  case "typing:indicator": {
214
- this._typing.has(e.threadId) || this._typing.set(e.threadId, /* @__PURE__ */ new Map()), this._typing.get(e.threadId).set(e.userId, Date.now()), this.emit("typing", { threadId: e.threadId, userId: e.userId });
236
+ this._typing.has(t.threadId) || this._typing.set(t.threadId, /* @__PURE__ */ new Map()), this._typing.get(t.threadId).set(t.userId, Date.now()), this.emit("typing", { threadId: t.threadId, userId: t.userId });
215
237
  break;
216
238
  }
217
239
  case "viewport:update": {
218
- this._viewports.set(e.userId, {
219
- scrollX: e.scrollX,
220
- scrollY: e.scrollY,
221
- viewportWidth: e.viewportWidth,
222
- viewportHeight: e.viewportHeight,
223
- pageWidth: e.pageWidth,
224
- pageHeight: e.pageHeight
225
- }), this.emit("viewport", { userId: e.userId });
240
+ this._viewports.set(t.userId, {
241
+ scrollX: t.scrollX,
242
+ scrollY: t.scrollY,
243
+ viewportWidth: t.viewportWidth,
244
+ viewportHeight: t.viewportHeight,
245
+ pageWidth: t.pageWidth,
246
+ pageHeight: t.pageHeight
247
+ }), this.emit("viewport", { userId: t.userId });
226
248
  break;
227
249
  }
228
250
  case "selection:update":
229
- this._selections.set(e.userId, e.selection), this.emit("selection", { userId: e.userId, selection: e.selection });
251
+ this._selections.set(t.userId, t.selection), this.emit("selection", { userId: t.userId, selection: t.selection });
230
252
  break;
231
253
  case "emoji:drop":
232
254
  this.emit("emoji-drop", {
233
- userId: e.userId,
234
- emoji: e.emoji,
235
- position: e.position
255
+ userId: t.userId,
256
+ emoji: t.emoji,
257
+ position: t.position
236
258
  });
237
259
  break;
238
260
  case "draw:stroke":
239
261
  this.emit("draw-stroke", {
240
- userId: e.userId,
241
- points: e.points,
242
- color: e.color,
243
- width: e.width
262
+ userId: t.userId,
263
+ points: t.points,
264
+ color: t.color,
265
+ width: t.width
244
266
  });
245
267
  break;
246
268
  case "draw:clear":
247
- this.emit("draw-clear", { userId: e.userId });
269
+ this.emit("draw-clear", { userId: t.userId });
248
270
  break;
249
271
  case "activity:logged":
250
- this._activityLogs.unshift(e.activityLog), this._activityLogs.length > 100 && (this._activityLogs = this._activityLogs.slice(0, 100)), this.emit("activity-logs", this._activityLogs);
272
+ this._activityLogs.unshift(t.activityLog), this._activityLogs.length > 100 && (this._activityLogs = this._activityLogs.slice(0, 100)), this.emit("activity-logs", this._activityLogs);
251
273
  break;
252
274
  case "error":
253
- this.emit("error", e);
275
+ this.emit("error", t);
254
276
  break;
255
277
  }
256
278
  }
@@ -258,8 +280,8 @@ class _ extends c {
258
280
  this._user = null, this._users.clear(), this._presence.clear(), this._threads.clear(), this._reactions.clear(), this._notifications = [], this._activityLogs = [], this._typing.clear(), this._viewports.clear(), this._selections.clear();
259
281
  }
260
282
  }
261
- class w extends c {
262
- constructor(e) {
283
+ class m extends c {
284
+ constructor(t) {
263
285
  var s;
264
286
  super();
265
287
  i(this, "state");
@@ -269,9 +291,9 @@ class w extends c {
269
291
  i(this, "lastCursorSend", 0);
270
292
  i(this, "pendingCursor", null);
271
293
  i(this, "cursorTimer", null);
272
- this.config = e, this.state = new _();
273
- const t = ((s = e.endpoint) == null ? void 0 : s.replace(/^http/, "ws")) ?? void 0;
274
- this.connection = new u(t), this.connection.on("message", (r) => {
294
+ this.config = t, this.state = new _(), this.state.baseUrl = (t.endpoint ?? "").replace(/^ws(s?):/, "http$1:").replace(/\/$/, "");
295
+ const e = ((s = t.endpoint) == null ? void 0 : s.replace(/^http/, "ws")) ?? void 0;
296
+ this.connection = new u(e), this.connection.on("message", (r) => {
275
297
  this.state.handleMessage(r), this.emit(r.type, r);
276
298
  }), this.connection.on("state", (r) => {
277
299
  this.emit("connection", r), r === "connected" ? (this.authenticate(), this.startHeartbeat()) : r === "disconnected" && this.stopHeartbeat();
@@ -295,13 +317,13 @@ class w extends c {
295
317
  room: this.config.room
296
318
  });
297
319
  }
298
- send(e) {
299
- this.connection.send(e);
320
+ send(t) {
321
+ this.connection.send(t);
300
322
  }
301
323
  // ── Cursors ──
302
- moveCursor(e) {
303
- const t = Date.now();
304
- this.pendingCursor = e, t - this.lastCursorSend >= 50 ? this.flushCursor() : this.cursorTimer || (this.cursorTimer = setTimeout(() => {
324
+ moveCursor(t) {
325
+ const e = Date.now();
326
+ this.pendingCursor = t, e - this.lastCursorSend >= 50 ? this.flushCursor() : this.cursorTimer || (this.cursorTimer = setTimeout(() => {
305
327
  this.cursorTimer = null, this.flushCursor();
306
328
  }, 50));
307
329
  }
@@ -309,8 +331,8 @@ class w extends c {
309
331
  this.pendingCursor && (this.send({ type: "cursor:move", position: this.pendingCursor }), this.lastCursorSend = Date.now(), this.pendingCursor = null);
310
332
  }
311
333
  // ── Presence ──
312
- updatePresence(e) {
313
- this.send({ type: "presence:update", status: e });
334
+ updatePresence(t) {
335
+ this.send({ type: "presence:update", status: t });
314
336
  }
315
337
  startHeartbeat() {
316
338
  this.heartbeatTimer = setInterval(() => {
@@ -321,76 +343,76 @@ class w extends c {
321
343
  this.heartbeatTimer && (clearInterval(this.heartbeatTimer), this.heartbeatTimer = null);
322
344
  }
323
345
  // ── Threads & Comments ──
324
- createThread(e, t = {}) {
346
+ createThread(t, e = {}) {
325
347
  const s = crypto.randomUUID();
326
348
  return this.send({
327
349
  type: "thread:create",
328
350
  id: s,
329
- body: e,
330
- mentions: t.mentions ?? [],
331
- position: t.position ?? null,
332
- attachmentIds: t.attachmentIds
351
+ body: t,
352
+ mentions: e.mentions ?? [],
353
+ position: e.position ?? null,
354
+ attachmentIds: e.attachmentIds
333
355
  }), s;
334
356
  }
335
- reply(e, t, s = [], r) {
336
- const o = crypto.randomUUID();
337
- return this.send({ type: "comment:create", threadId: e, id: o, body: t, mentions: s, attachmentIds: r }), o;
357
+ reply(t, e, s = [], r) {
358
+ const a = crypto.randomUUID();
359
+ return this.send({ type: "comment:create", threadId: t, id: a, body: e, mentions: s, attachmentIds: r }), a;
338
360
  }
339
- editComment(e, t, s = []) {
340
- this.send({ type: "comment:edit", commentId: e, body: t, mentions: s });
361
+ editComment(t, e, s = []) {
362
+ this.send({ type: "comment:edit", commentId: t, body: e, mentions: s });
341
363
  }
342
- deleteComment(e) {
343
- this.send({ type: "comment:delete", commentId: e });
364
+ deleteComment(t) {
365
+ this.send({ type: "comment:delete", commentId: t });
344
366
  }
345
- resolveThread(e, t = !0) {
346
- this.send({ type: "thread:resolve", threadId: e, resolved: t });
367
+ resolveThread(t, e = !0) {
368
+ this.send({ type: "thread:resolve", threadId: t, resolved: e });
347
369
  }
348
370
  // ── Reactions ──
349
- addReaction(e, t, s) {
350
- this.send({ type: "reaction:add", targetId: e, targetType: t, emoji: s });
371
+ addReaction(t, e, s) {
372
+ this.send({ type: "reaction:add", targetId: t, targetType: e, emoji: s });
351
373
  }
352
- removeReaction(e) {
353
- this.send({ type: "reaction:remove", reactionId: e });
374
+ removeReaction(t) {
375
+ this.send({ type: "reaction:remove", reactionId: t });
354
376
  }
355
377
  // ── Notifications ──
356
- markRead(e) {
357
- this.send({ type: "notification:read", notificationId: e });
378
+ markRead(t) {
379
+ this.send({ type: "notification:read", notificationId: t });
358
380
  }
359
381
  markAllRead() {
360
382
  this.send({ type: "notification:read-all" });
361
383
  }
362
384
  // ── Clicks ──
363
- performClick(e) {
364
- this.send({ type: "click:perform", position: e });
385
+ performClick(t) {
386
+ this.send({ type: "click:perform", position: t });
365
387
  }
366
388
  // ── Typing ──
367
- sendTyping(e) {
368
- this.send({ type: "typing:start", threadId: e });
389
+ sendTyping(t) {
390
+ this.send({ type: "typing:start", threadId: t });
369
391
  }
370
392
  // ── Viewport ──
371
- updateViewport(e) {
372
- this.send({ type: "viewport:update", ...e });
393
+ updateViewport(t) {
394
+ this.send({ type: "viewport:update", ...t });
373
395
  }
374
396
  // ── Selection ──
375
- updateSelection(e) {
376
- this.send({ type: "selection:update", selection: e });
397
+ updateSelection(t) {
398
+ this.send({ type: "selection:update", selection: t });
377
399
  }
378
400
  // ── Emoji Drops ──
379
- dropEmoji(e, t) {
380
- this.send({ type: "emoji:drop", emoji: e, position: t });
401
+ dropEmoji(t, e) {
402
+ this.send({ type: "emoji:drop", emoji: t, position: e });
381
403
  }
382
404
  // ── Drawing ──
383
- drawStroke(e, t, s) {
384
- this.send({ type: "draw:stroke", points: e, color: t, width: s });
405
+ drawStroke(t, e, s) {
406
+ this.send({ type: "draw:stroke", points: t, color: e, width: s });
385
407
  }
386
408
  clearDrawing() {
387
409
  this.send({ type: "draw:clear" });
388
410
  }
389
411
  // ── File Upload ──
390
- async uploadFile(e) {
391
- const t = (this.config.endpoint ?? window.location.origin).replace(/^ws(s?):/, "http$1:"), s = new FormData();
392
- s.append("file", e);
393
- const r = await fetch(`${t}/api/v1/upload`, {
412
+ async uploadFile(t) {
413
+ const e = (this.config.endpoint ?? window.location.origin).replace(/^ws(s?):/, "http$1:"), s = new FormData();
414
+ s.append("file", t);
415
+ const r = await fetch(`${e}/api/v1/upload`, {
394
416
  method: "POST",
395
417
  headers: {
396
418
  "X-Pulse-Key": this.config.apiKey,
@@ -399,19 +421,20 @@ class w extends c {
399
421
  body: s
400
422
  });
401
423
  if (!r.ok) {
402
- const o = await r.json().catch(() => ({ error: "Upload failed" }));
403
- throw new Error(o.error ?? "Upload failed");
424
+ const h = await r.json().catch(() => ({ error: "Upload failed" }));
425
+ throw new Error(h.error ?? "Upload failed");
404
426
  }
405
- return r.json();
427
+ const a = await r.json();
428
+ return a.url && !a.url.startsWith("http") && (a.url = `${e}${a.url}`), a.thumbnailUrl && !a.thumbnailUrl.startsWith("http") && (a.thumbnailUrl = `${e}${a.thumbnailUrl}`), a;
406
429
  }
407
430
  // ── Presence control ──
408
- setAppearOffline(e) {
409
- e ? (this.stopHeartbeat(), this.send({ type: "presence:update", status: "idle" })) : (this.startHeartbeat(), this.send({ type: "presence:update", status: "online" }));
431
+ setAppearOffline(t) {
432
+ t ? (this.stopHeartbeat(), this.send({ type: "presence:update", status: "idle" })) : (this.startHeartbeat(), this.send({ type: "presence:update", status: "online" }));
410
433
  }
411
434
  }
412
435
  export {
413
436
  u as Connection,
414
437
  c as Emitter,
415
- w as PulseClient,
438
+ m as PulseClient,
416
439
  _ as StateManager
417
440
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamention/pulse-core",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Core client SDK for Pulse — WebSocket connection, state management, and API for real-time collaboration",
5
5
  "type": "module",
6
6
  "main": "./dist/pulse-core.cjs",
@@ -36,7 +36,7 @@
36
36
  "access": "public"
37
37
  },
38
38
  "dependencies": {
39
- "@gamention/pulse-shared": "0.1.9"
39
+ "@gamention/pulse-shared": "0.1.10"
40
40
  },
41
41
  "devDependencies": {
42
42
  "typescript": "^5.7.0",