UNPKG

erebus-sdk

Version:

To install dependencies:

8 lines 62.1 kB
'use strict';var zod=require('zod'),Me=require('ky'),react=require('react'),client=require('hono/client'),nanoid=require('nanoid'),h=require('consola'),jsxRuntime=require('react/jsx-runtime'),hono=require('hono'),nodeServer=require('@hono/node-server'),zodValidator=require('@hono/zod-validator'),vercel=require('hono/vercel');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Me__default=/*#__PURE__*/_interopDefault(Me);var h__default=/*#__PURE__*/_interopDefault(h);var Re="erebus-service-client",le=({base_url:i})=>Me__default.default.create({prefixUrl:i??"https://api.erebus.sh/v1/",headers:{"content-type":"application/json"},hooks:{beforeRequest:[e=>{e.headers.set("x-client",Re);}]}});var c=class extends Error{constructor(e,t){super(e+(t?`: ${t}`:""));}};var R=class{constructor(e,t){this.grantRequest=e;this.client=t;if(!this.grantRequest.secret_key)throw new c(["Secret key is required to create a session. Use the ErebusService class method.","example:","const service = new ErebusService({ secret_api_key: 'test' });","const session = await service.prepareSession({ userId: 'test' });"].join(` `));if(!this.grantRequest.userId)throw new c(["User ID is required to create a session. Use the ErebusService class method.","example:","const service = new ErebusService({ secret_api_key: 'test' });","const session = await service.prepareSession({ userId: 'test' });"].join(` `));this.grantRequest.expiresAt=Math.floor(Date.now()/1e3)+7200;}join(e){if(!e)throw new c("Channel is required to join.");if(!/^[A-Za-z0-9_]+$/.test(e))throw new c("Channel name must not contain spaces or special characters. Only letters, numbers, and underscores are allowed.");if(e.length>64)throw new c("Channel name must be less than 64 characters.");this.grantRequest.channel=e;}allow(e,t){if(!this.grantRequest.channel)throw new c(["You must set a channel before allowing access to any topics.","Please call join(channel) first.","example:","session.join('channel_name');"].join(` `));if(!e)throw new c("Topic is required to allow access.");if(e!=="*"&&!/^[A-Za-z0-9_]+$/.test(e))throw new c("Topic name must not contain spaces or special characters. Only letters, numbers, and underscores are allowed. Use '*' to allow all topics.");if(e.length>64)throw new c("Topic name must be less than 64 characters.");if(this.grantRequest.topics.length>64)throw new c("Connecting to more than 64 topics at once is inefficient. Please reduce the number of topics in this grant.");if(!t)throw new c("Scope is required to allow access.");this.grantRequest.topics.push({topic:e,scope:t});}setExpiration(e){let t=Math.floor(Date.now()/1e3),n=t+600,s=t+7200;if(typeof e!="number"||!Number.isFinite(e))throw new c("Expiration must be a valid number (unix timestamp in seconds).");if(e<n)throw new c("Expiration must be at least 10 minutes from now.");if(e>s)throw new c("Expiration cannot be more than 2 hours from now.");this.grantRequest.expiresAt=e;}async authorize(){if(!this.grantRequest.channel)throw new c("You must set a channel before allowing access to any topics. Please call join(channel) first.");if(this.grantRequest.topics.length===0)throw new c(["At least one topic is required. Please use the allow method to grant access to topics.","example:","session.allow('topic-1', Access.Read);"].join(` `));if(this.grantRequest.topics.length>64)throw new c(["Connecting to more than 64 topics at once is inefficient. Please reduce the number of topics in this grant."].join(` `));let e=this.grantRequest;try{let t=await this.client.post("api/v1/grant-channel",{json:e});if(!t.ok)throw new c(["The server returned an error.","Please check your API key and ensure you have provided a valid topics array (at least one and less than 64 topics). Please try again.","If the problem persists, please contact support: sdk@erebus.sh"].join(` `));console.log("[ErebusSession.authorize] Response received for user:",this.grantRequest.userId);let n=await t.json();if(!n)throw new c(["The server returned an empty response or invalid data.","Please check your API key and try again.","If the problem persists, please contact support: sdk@erebus.sh"].join(` `));return n.grant_jwt}catch(t){throw t instanceof Me.HTTPError?(console.error(t),t.response.status===401?new c("Invalid API key or token. Please check your API key and try again.",JSON.stringify(await t.response.json())):t.response.status===400?new c("Invalid topics array. Please check your topics array and try again.",JSON.stringify(await t.response.json())):t.response.status===500?new c("Internal server error. Please try again later.",JSON.stringify(await t.response.json())):new c(`Failed to get token: ${t.response.statusText}`,JSON.stringify(await t.response.json()))):t}}get __debugObject(){return {grant:this.grantRequest,client:this.client}}};var he=(s=>(s.Read="read",s.Write="write",s.ReadWrite="read-write",s.Huh="huh?",s))(he||{}),Z=zod.z.enum(Object.values(he));zod.z.object({project_id:zod.z.string(),key_id:zod.z.string(),channel:zod.z.string(),topics:zod.z.array(zod.z.object({topic:zod.z.string(),scope:Z})),userId:zod.z.string().min(1),issuedAt:zod.z.number(),expiresAt:zod.z.number()});var _e=/^(sk-er-|dv-er-)[\w-]{48}$/,J=zod.z.string().min(1).regex(_e,"Invalid API key format");zod.z.object({secret_key:J,channel:zod.z.string().min(1),topics:zod.z.array(zod.z.object({topic:zod.z.string(),scope:Z})).min(1),userId:zod.z.string().min(1),expiresAt:zod.z.number()});var pe=class{client;grantRequest={userId:"",channel:"",topics:[],expiresAt:0,secret_key:"dv-er-abcdefghijklmnopqrstuvwxyzABCDEFGsH1234abcdddddd"};constructor({secret_api_key:e,base_url:t}){if(!J.safeParse(e).success)throw new c("Invalid API key format");this.grantRequest.secret_key=e,this.client=le({base_url:t??"https://api.erebus.sh"});}async prepareSession({userId:e}){if(!e)throw new c("User ID is required to create a session. Use the ErebusService.prepareSession() method.");return this.grantRequest.userId=e,new R(this.grantRequest,this.client)}};function vt(i){return `${i}:*`}function Et(i,e){return e?`${i}:${e}:*`:`${i}:*`}var Ue=(s=>(s.Read="read",s.Write="write",s.ReadWrite="read-write",s.Huh="huh?",s))(Ue||{});var V=react.createContext(null);function de(){let i=react.useContext(V);if(!i)throw new c("useTopic must be used within TopicProvider");return i}async function ge(i,e,t){let n,s=false;return new Promise(o=>{n=setTimeout(()=>{s||(s=true,o({error:new c(`${t} timeout after ${e}ms`)}));},e),i.then(a=>{s||(s=true,clearTimeout(n),o({value:a}));},a=>{if(!s){s=true,clearTimeout(n);let l=a instanceof Error?a:new c(typeof a=="string"?a:`Unknown error in ${t}: ${JSON.stringify(a)}`);o({error:l});}});})}async function be(i,e,t=1e4){try{i.joinChannel(e);let{error:n}=await ge(i.connect(),t,"joinAndConnect");return n?(console.error("[joinAndConnect] Failed to join and connect (timeout or connect error)",n),{success:!1,error:n instanceof c?n:new c(n?.message??"Failed to join and connect")}):(console.log("[joinAndConnect] Successfully joined and connected"),{success:!0,error:null})}catch(n){return console.error("[joinAndConnect] Failed to join and connect (exception)",n),{success:false,error:n instanceof c?n:new c(n instanceof Error?n.message:"Failed to join and connect")}}}var fe=()=>crypto.randomUUID()??String(Date.now())+Math.random().toString(16).slice(2);function me(i,e){let{client:t,topic:n}=de(),[s,o]=react.useState([]),[a,l]=react.useState(false),[P,k]=react.useState(null),[F,ae]=react.useState(false),T=react.useRef(new Map),[,Pe]=react.useState(0),m=(b,y)=>{let p=T.current.get(b)??{id:b,status:"idle",attempts:0,error:null};T.current.set(b,{...p,...y}),Pe(w=>w+1);},Te=b=>T.current.get(b);react.useEffect(()=>((async()=>{let{success:b,error:y}=await be(t,i);if(!b){l(true),k(y);return}t.subscribe(n,p=>{if(!e[i])throw new c(`Schema for channel "${i}" is not defined.`);let w=e[i].parse(p.payload);o(L=>[...L,{...p,payload:w}]);},p=>{if(console.log("[useChannelInternal] Subscription ACK",p),!p.success)if(console.log("[useChannelInternal] Subscription ACK failed",p),l(true),p.error.code==="TIMEOUT"){k(new c("Subscription failed: the server could not process your request to subscribe to the specified topic due to a timeout."));return}else {console.log("[useChannelInternal] Subscription ACK failed",p),k(new c("Subscription failed: the server could not process your request to subscribe to the specified topic."));return}console.log("[useChannelInternal] Subscription ACK success",p),ae(true);}),t.onPresence(n,p=>{p.status==="online"?console.log("[useChannelInternal] Presence online",p):console.log("[useChannelInternal] Presence offline",p);});})(),()=>{try{t.unsubscribe(n),ae(!1);}catch{}}),[t,n]);async function $e(b,y={}){if(!e[i])throw new c(`Schema for channel "${i}" is not defined.`);e[i].parse(b);let p=fe(),w=y.maxRetries??2,L=y.baseDelayMs??250;m(p,{id:p,status:"sending",attempts:0,error:null});let $=0,Ae=()=>new Promise((S,A)=>{$+=1,m(p,{attempts:$}),t.publishWithAck({topic:n,messageBody:JSON.stringify(b),onAck:M=>{if(M.success)m(p,{status:"success",error:null}),S();else {let ue=(M.error?.code??"UNKNOWN")==="TIMEOUT"?new c("Publish failed: the server timed out processing your publish request."):new c("Publish failed: the server could not process your publish request.");m(p,{status:"failed",error:ue}),A(ue);}}});}),G=null;for(let S=0;S<=w;S++)try{return await Ae(),{id:p,success:!0,attempts:$,error:null}}catch(A){if(G=A instanceof c?A:new c("Unknown publish error"),S<w){let M=L*Math.pow(2,S);await new Promise(ce=>setTimeout(ce,M)),m(p,{status:"sending"});}}return m(p,{status:"failed",error:G}),{id:p,success:false,attempts:$,error:G}}return {publish:$e,messages:s,getMessageStatus:Te,messageStatuses:T.current,isError:a,error:P,isSubscribed:F}}function Ft(i){return function(t){return me(t,i)}}var Y=react.createContext(null);function ye(){let i=react.useContext(Y);if(!i)throw new c("useErebusContext must be used within a ErebusProvider");return i}var X=i=>client.hc(i);var E=class{client;constructor(e){console.log("[AUTHORIZE] Constructor called with baseUrl:",e),this.client=X(e),console.log("[AUTHORIZE] RPC client created successfully");}async generateToken(e){console.log("[AUTHORIZE] generateToken called with channel:",e);try{console.log("[AUTHORIZE] Making POST request to generate-token endpoint");let t=await this.client.api["generate-token"].$post({json:{channel:e}});if(!t.ok)throw console.error("[AUTHORIZE] generateToken failed with status:",t.status,t.statusText),new Error(`The auth call failed: ${t.status} ${t.statusText} ${await t.text()}`);let n=await t.json();return console.log("[AUTHORIZE] generateToken successful, token generated"),n.grant_jwt}catch(t){throw console.error("[AUTHORIZE] generateToken error:",t instanceof Error?t.message:"Unknown error"),new Error(`Failed to generate token: ${t instanceof Error?t.message:"Unknown error"}`)}}async healthCheck(){console.log("[AUTHORIZE] healthCheck called");try{console.log("[AUTHORIZE] Making GET request to health-not-meaningful endpoint");let t=await(await this.client.api["health-not-meaningful"].$get()).json();return console.log("[AUTHORIZE] healthCheck successful, response received"),t}catch(e){throw console.error("[AUTHORIZE] healthCheck error:",e instanceof Error?e.message:"Unknown error"),new Error(`Health check failed: ${e instanceof Error?e.message:"Unknown error"}`)}}async generateTestToken(){console.log("[AUTHORIZE] generateTestToken called");try{console.log("[AUTHORIZE] Making GET request to generate-token-test endpoint");let e=await this.client.api["generate-token-test"].$get();if(!e.ok){console.error("[AUTHORIZE] generateTestToken failed with status:",e.status);let n=await e.json();return console.log("[AUTHORIZE] generateTestToken error response:",n),n}let t=await e.json();return console.log("[AUTHORIZE] generateTestToken successful, test token generated"),t}catch(e){throw console.error("[AUTHORIZE] generateTestToken error:",e instanceof Error?e.message:"Unknown error"),new Error(`Failed to generate test token: ${e instanceof Error?e.message:"Unknown error"}`)}}};var r=h.createConsola({level:3,formatOptions:{colors:true,date:true,compact:false,columns:typeof process<"u"&&process.stdout&&process.stdout.columns?process.stdout.columns:80},defaults:{tag:"Erebus"}});var x=zod.z.object({id:zod.z.string(),topic:zod.z.string().min(1),senderId:zod.z.string().min(1),seq:zod.z.string().min(1),sentAt:zod.z.coerce.date(),payload:zod.z.string(),t_ingress:zod.z.number().optional(),t_enqueued:zod.z.number().optional(),t_broadcast_begin:zod.z.number().optional(),t_ws_write_end:zod.z.number().optional(),t_broadcast_end:zod.z.number().optional(),clientMsgId:zod.z.uuid().optional(),clientPublishTs:zod.z.number().optional()}).strict();var _=zod.z.string().min(1).optional(),Ke=zod.z.object({packetType:zod.z.literal("connect"),grantJWT:zod.z.string().min(1)}),Ne=zod.z.object({packetType:zod.z.literal("subscribe"),requestId:_,topic:zod.z.string().min(1)}),Fe=zod.z.object({packetType:zod.z.literal("unsubscribe"),requestId:_,topic:zod.z.string().min(1)}),Q=zod.z.object({packetType:zod.z.literal("presence"),clientId:zod.z.string().min(1),topic:zod.z.string().min(1),status:zod.z.enum(["online","offline"])}),Le=zod.z.object({packetType:zod.z.literal("publish"),ack:zod.z.boolean().optional(),requestId:_,topic:zod.z.string().min(1),payload:x,clientMsgId:zod.z.string().min(1)}),ee=zod.z.object({type:zod.z.literal("ack"),path:zod.z.enum(["publish","subscribe","unsubscribe"]),seq:zod.z.string().min(1),serverAssignedId:zod.z.string().min(1),clientMsgId:zod.z.string().min(1)}),te=ee.extend({path:zod.z.literal("publish"),topic:zod.z.string().min(1),result:zod.z.object({ok:zod.z.literal(true),t_ingress:zod.z.number()})}),re=ee.extend({path:zod.z.literal("publish"),topic:zod.z.string().min(1),result:zod.z.object({ok:zod.z.literal(false),code:zod.z.enum(["UNAUTHORIZED","FORBIDDEN","INVALID","RATE_LIMITED","INTERNAL"]),message:zod.z.string().min(1)})}),ne=ee.extend({path:zod.z.enum(["subscribe","unsubscribe"]),topic:zod.z.string().min(1),result:zod.z.object({ok:zod.z.literal(true),status:zod.z.enum(["subscribed","unsubscribed"])})}),Ge=zod.z.discriminatedUnion("path",[te,re,ne]),Ze=zod.z.object({packetType:zod.z.literal("ack"),clientMsgId:_,result:Ge}),we=zod.z.discriminatedUnion("packetType",[Ke,Ne,Fe,Q,Le,Ze]);function Se(i){r.info("[encodeEnvelope] called",{packetType:i.packetType}),r.info("[encodeEnvelope] validating packet",{packetType:i.packetType}),we.parse(i),r.info("[encodeEnvelope] packet validated",{packetType:i.packetType});let e=JSON.stringify(i);return r.info("[encodeEnvelope] packet encoded",{packetType:i.packetType,encodedLength:e.length}),e}function Ce(i){if(r.info("[parseServerFrame] called",{rawLength:i.length}),!i||typeof i!="string"||i.trim().length===0)return r.warn("[parseServerFrame] Invalid raw data",{raw:i}),null;try{r.info("[parseServerFrame] parsing JSON");let e=JSON.parse(i);if(r.info("[parseServerFrame] JSON parsed",{keys:Object.keys(e)}),!e||typeof e!="object")return r.warn("[parseServerFrame] Parsed data is not an object",{data:e}),null;if(e.packetType==="ack"){r.info("[parseServerFrame] validating ACK packet schema");let t=Je(e);return t?(r.info("[parseServerFrame] ACK packet validated",{clientMsgId:e.clientMsgId}),t):(r.warn("[parseServerFrame] ACK packet parsing failed"),null)}else if(e.packetType==="presence"){r.info("[parseServerFrame] validating presence packet schema");let t=Q.parse(e);return r.info("[parseServerFrame] presence packet validated",{topic:t.topic,clientId:t.clientId}),t}else {if(!e.topic||typeof e.topic!="string")return r.warn("[parseServerFrame] Missing or invalid topic",{data:e}),null;r.info("[parseServerFrame] validating message schema");let t=x.parse(e);return r.info("[parseServerFrame] message schema validated",{topic:t.topic}),{packetType:"publish",topic:t.topic,payload:t}}}catch(e){return r.warn("[parseServerFrame] failed",{error:e instanceof Error?e.message:String(e)},i),null}}function Je(i){try{if(i.packetType!=="ack"||!i.result||typeof i.result!="object")return null;let e=i.result,t=e.path;if(t==="subscribe"||t==="unsubscribe"){let n=ne.parse(e);return {packetType:"ack",clientMsgId:i.clientMsgId,result:n}}else if(t==="publish"&&e.result&&typeof e.result=="object"&&"ok"in e.result)if(e.result.ok){let n=te.parse(e);return {packetType:"ack",clientMsgId:i.clientMsgId,result:n}}else {let n=re.parse(e);return {packetType:"ack",clientMsgId:i.clientMsgId,result:n}}return r.warn("[parseAckPacket] Unknown ACK type",{path:t}),null}catch(e){return r.warn("[parseAckPacket] Failed to parse ACK packet",{error:e}),null}}function ke(i){if(!i)return false;try{let e=new URL(i);return !(e.protocol!=="ws:"&&e.protocol!=="wss:"||!e.hostname.includes("localhost")&&e.protocol==="ws:")}catch{return false}}function ve(i,e=5e3){let t=250*Math.pow(2,i),n=Math.floor(Math.random()*200),s=Math.min(e,t)+n;return r.info("backoff computed",{attempt:i,capMs:e,base:t,jitter:n,delay:s}),s}var U=class extends Error{},C=class extends Error{},j=class{#e;#t;#r;#i=0;#n="idle";#s;#a;#h;#o;#c;#l;constructor(e){if(this.#s=`conn_${Math.random().toString(36).substring(2,8)}`,r.info(`[${this.#s}] ConnectionManager created`,{url:e.url}),!ke(e.url))throw r.error(`[${this.#s}] Invalid WebSocket URL`,{url:e.url}),new Error("Invalid WebSocket URL");this.#e=e.url,this.#t=e.channel,this.#a="",this.#o=e.onMessage,this.#c=e.log??(()=>{}),this.#h=e.autoReconnect??false,this.#l=e.debugHexDump??false;}get state(){return this.#n}get isConnected(){return this.#n==="open"}get isConnecting(){return this.#n==="connecting"}get isClosed(){return this.#n==="closed"}get isIdle(){return this.#n==="idle"}get connectionId(){return this.#s}get url(){return this.#e}get bufferedAmount(){return this.#r?.bufferedAmount??0}get readyState(){return this.#r?.readyState}get channel(){return this.#t}async open(e){if(r.info(`[${this.#s}] Opening connection`,{timeout:e.timeout}),this.#c("info","connection.open called"),this.#n==="connecting"||this.#n==="open"){r.info(`[${this.#s}] Connection already ${this.#n}, returning early`);return}if(!this.#t||this.#t.trim().length===0){let n="Channel must be set before opening connection";throw r.error(`[${this.#s}] ${n}`,{channel:this.#t}),new Error(n)}r.info(`[${this.#s}] Setting state to connecting`),this.#n="connecting",this.#a=e.grant;let t=await this.#u(e.grant);if(this.#r=t,this.#p(t),e.timeout){r.info(`[${this.#s}] Setting connection timeout`,{timeout:e.timeout});let n=setTimeout(()=>{throw r.error(`[${this.#s}] Connection timeout reached`,{timeout:e.timeout}),t.close(),new Error("Connection timeout")},e.timeout);await new Promise(s=>t.addEventListener("open",()=>{r.info(`[${this.#s}] Connection opened within timeout, clearing timeout`),clearTimeout(n),s();},{once:true}));}else r.info(`[${this.#s}] Waiting for connection to open (no timeout)`),await new Promise(n=>t.addEventListener("open",()=>{r.info(`[${this.#s}] Connection opened successfully`),n();},{once:true}));}close(){if(r.info(`[${this.#s}] Connection close called (url: ${this.#e})`),this.#c("info",`connection close called (url: ${this.#e})`),this.#n="closed",r.info(`[${this.#s}] Connection state set to closed (url: ${this.#e})`),this.#r){try{(this.#r.readyState===WebSocket.OPEN||this.#r.readyState===WebSocket.CONNECTING)&&this.#r.close();}catch(e){r.warn(`[${this.#s}] Error closing WebSocket`,{error:e});}this.#r=void 0;}r.info(`[${this.#s}] Connection closed and cleaned up`);}send(e){if(r.info(`[${this.#s}] Sending packet`,{packetType:e.packetType}),!e||typeof e!="object"){let n="Invalid packet: must be an object";throw r.error(`[${this.#s}] ${n}`,{pkt:e}),new Error(n)}if(!this.#r||this.#r.readyState!==WebSocket.OPEN)throw r.error(`[${this.#s}] Cannot send packet - WebSocket not ready`,{hasWs:!!this.#r,readyState:this.#r?.readyState,state:this.#n}),new C("Not connected readyState: "+this.#r?.readyState);let t=this.#r.bufferedAmount;if(t>1e6)throw r.error(`[${this.#s}] Critical backpressure - closing connection`,{buffered:t,limit:1e6}),this.#c("error","critical backpressure; reconnecting",{buffered:t}),this.#r.close(),new U("Critical backpressure");try{let n=Se(e);r.info(`[ConnectionManager] [${this.#s}] Packet encoded, sending via WebSocket`,{packetType:e.packetType,encodedLength:n.length}),this.#r.send(n);}catch(n){throw r.error(`[${this.#s}] Error sending packet`,{error:n,packetType:e.packetType}),n}}sendRaw(e){if(r.info(`[${this.#s}] Sending raw data`,{dataType:typeof e,dataLength:e.length}),!this.#r||this.#r.readyState!==WebSocket.OPEN)throw r.error(`[${this.#s}] Cannot send raw data - WebSocket not ready`,{hasWs:!!this.#r,readyState:this.#r?.readyState,state:this.#n}),new C("Not connected readyState: "+this.#r?.readyState);try{this.#r.send(e),r.info(`[${this.#s}] Raw data sent successfully`);}catch(t){throw r.error(`[${this.#s}] Error sending raw data`,{error:t,dataLength:e.length}),t}}setChannel(e){if(r.info(`[${this.#s}] Setting channel`,{channel:e}),!e||typeof e!="string"||e.trim().length===0){let t="Invalid channel: must be a non-empty string";throw r.error(`[${this.#s}] ${t}`,{channel:e}),new Error(t)}this.#t=e,r.info(`[${this.#s}] Channel set successfully`,{channel:this.#t});}async#u(e){let t=new URL(this.#e);t.searchParams.set("grant",e),r.info(`[${this.#s}] Creating WebSocket connection`,{url:this.#e,grant:e.substring(0,10)+"..."});let n=new WebSocket(t.toString());try{"binaryType"in n&&(n.binaryType="arraybuffer");}catch{}return n}#p(e){e.addEventListener("open",()=>{r.info(`[${this.#s}] WebSocket opened successfully`),this.#i=0,this.#n="open",this.#c("info","ws open");},{once:true}),e.addEventListener("message",t=>{this.#d(t);}),e.addEventListener("close",()=>{this.#c("warn","ws close encountered",{retry:this.#i}),r.warn(`[${this.#s}] WebSocket close encountered`,{retry:this.#i,state:this.#n}),this.#n!=="closed"&&this.#h?this.#f():this.#n="closed";}),e.addEventListener("error",t=>{this.#c("warn","ws error",t),r.warn(`[${this.#s}] WebSocket error`,{error:t,state:this.#n,readyState:e.readyState,url:this.#e});});}async#d(e){if(this.#c("info","ws message received"),this.#l){let t=this.#g(e.data);console.log(`[${this.#s}] WS message hex:`,t);}try{let t=await this.#b(e.data);if(t.length===0){r.warn(`[${this.#s}] Data string is empty, skipping`);return}this.#o({rawData:t});}catch(t){r.error(`[${this.#s}] Error handling message`,{error:t});}}#g(e){return typeof e=="string"?Buffer.from(e,"utf8").toString("hex"):e instanceof ArrayBuffer?Buffer.from(e).toString("hex"):typeof e=="object"&&e&&"arrayBuffer"in e?"[blob-data]":""}async#b(e){if(typeof e=="string")return e;if(typeof e>"u"||e===null)return r.warn(`[${this.#s}] Data is undefined or null, using empty string`),"";if(e&&typeof e=="object"&&"arrayBuffer"in e&&typeof e.arrayBuffer=="function"){let t=await e.arrayBuffer(),n=new Uint8Array(t);return new TextDecoder().decode(n)}else if(typeof Buffer<"u"&&Buffer.isBuffer(e)){let t=e,n=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);return new TextDecoder().decode(n)}else if(e instanceof ArrayBuffer){let t=new Uint8Array(e);return new TextDecoder().decode(t)}else return e instanceof Uint8Array?new TextDecoder().decode(e):String(e)}#f(){if(this.#n==="closed"||this.#n==="connecting"){r.info(`[${this.#s}] Reconnect called but connection is ${this.#n}, ignoring`);return}r.info(`[${this.#s}] Starting reconnect process`),this.#n="idle";let e=ve(this.#i,5e3);this.#c("info","scheduling reconnect",{retry:this.#i,bckOff:e}),r.info(`[${this.#s}] Scheduling reconnect`,{retry:this.#i,backoff:e}),this.#i++,setTimeout(()=>{r.info(`[${this.#s}] Executing scheduled reconnect`),this.open({grant:this.#a,timeout:5e3}).catch(t=>{r.error(`[${this.#s}] Reconnect failed`,{error:t}),this.#n="closed";});},e);}};var O=class{#e=new Map;#t=new Map;#r=new Map;#i=new Map;#n;constructor(e){this.#n=e,r.info(`[${this.#n}] AckManager created`);}trackPublish(e,t){r.info(`[${this.#n}] Tracking publish for ACK`,{requestId:e,clientMsgId:t.clientMsgId,topic:t.topic}),this.#e.set(e,t),this.#t.set(t.clientMsgId,e),t.timeoutId&&r.info(`[${this.#n}] ACK timeout set`,{requestId:e,timeout:"already configured"});}trackSubscription(e,t){r.info(`[${this.#n}] Tracking subscription for ACK`,{requestId:e,clientMsgId:t.clientMsgId,topic:t.topic,path:t.path,clientMsgIdType:typeof t.clientMsgId,clientMsgIdLength:t.clientMsgId?.length}),this.#r.set(e,t),t.clientMsgId?(this.#i.set(t.clientMsgId,e),r.info(`[${this.#n}] Stored clientMsgId mapping`,{clientMsgId:t.clientMsgId,requestId:e,totalMappings:this.#i.size})):r.info(`[${this.#n}] No clientMsgId provided for subscription tracking`,{requestId:e,note:"ACK matching will rely on other mechanisms"}),t.timeoutId&&r.info(`[${this.#n}] Subscription ACK timeout set`,{requestId:e,timeout:"already configured"});}handleAck(e){r.info(`[${this.#n}] Handling ACK packet`,{clientMsgId:e.clientMsgId,path:e.result.path}),e.result.path==="publish"?this.#s(e):e.result.path==="subscribe"||e.result.path==="unsubscribe"?this.#a(e):r.warn(`[${this.#n}] Unknown ACK path`,{path:e.result.path,clientMsgId:e.clientMsgId});}#s(e){let t=e.clientMsgId;if(!t){r.warn(`[${this.#n}] Publish ACK packet missing clientMsgId`);return}let n=this.#t.get(t);if(!n){r.warn(`[${this.#n}] No requestId found for publish clientMsgId`,{clientMsgId:t});return}let s=this.#e.get(n);if(!s){r.warn(`[${this.#n}] No pending publish found for ACK`,{requestId:n,clientMsgId:t});return}s.timeoutId&&clearTimeout(s.timeoutId),this.#e.delete(n),this.#t.delete(t);let o=this.#h(e,s);r.info(`[${this.#n}] Calling publish ACK callback`,{requestId:n,clientMsgId:t,success:o.success});try{s.callback(o);}catch(a){r.error(`[${this.#n}] Error in publish ACK callback`,{error:a,requestId:n,clientMsgId:t});}}#a(e){let t=e.clientMsgId,n,s;if(r.info(`[${this.#n}] Processing subscription ACK`,{path:e.result.path,topic:e.result.topic,clientMsgId:t,pendingSubscriptionCount:this.#r.size,subscriptionMsgIdMappings:Array.from(this.#i.entries()).map(([a,l])=>`${a} -> ${l}`)}),t&&(n=this.#i.get(t),r.info(`[${this.#n}] Looking up clientMsgId in mappings`,{clientMsgId:t,foundRequestId:n}),n?(s=this.#r.get(n),r.info(`[${this.#n}] Looking up requestId in pending subscriptions`,{requestId:n,foundPending:!!s})):(r.info(`[${this.#n}] No direct mapping found, checking if clientMsgId matches a requestId`,{clientMsgId:t}),s=this.#r.get(t),s&&(n=t,r.info(`[${this.#n}] Found pending subscription by matching clientMsgId to requestId`,{clientMsgId:t,requestId:n})))),!s){r.info(`[${this.#n}] Received untracked subscription ACK`,{path:e.result.path,topic:e.result.topic,clientMsgId:t,note:"This is normal for optimistic subscriptions"});return}r.info(`[${this.#n}] Handling tracked subscription ACK`,{requestId:n,clientMsgId:t,topic:s.topic,path:s.path}),s.timeoutId&&clearTimeout(s.timeoutId),this.#r.delete(n),t&&this.#i.delete(t);let o=this.#o(e,s);r.info(`[${this.#n}] Calling subscription ACK callback`,{requestId:n,clientMsgId:t,topic:s.topic,success:o.success});try{s.callback(o);}catch(a){r.error(`[${this.#n}] Error in subscription ACK callback`,{error:a,requestId:n,clientMsgId:t,topic:s.topic});}}handlePublishTimeout(e){r.warn(`[${this.#n}] Publish ACK timeout`,{requestId:e});let t=this.#e.get(e);if(!t)return;this.#e.delete(e),this.#t.delete(t.clientMsgId);let n={success:false,ack:{},error:{code:"TIMEOUT",message:"Publish ACK not received within timeout"},topic:t.topic};try{t.callback(n);}catch(s){r.error(`[${this.#n}] Error in publish timeout callback`,{error:s,requestId:e});}}handleSubscriptionTimeout(e){r.warn(`[${this.#n}] Subscription ACK timeout`,{requestId:e});let t=this.#r.get(e);if(!t)return;this.#r.delete(e),t.clientMsgId&&this.#i.delete(t.clientMsgId);let n={success:false,error:{code:"TIMEOUT",message:"Subscription ACK not received within timeout"},topic:t.topic,path:t.path};try{t.callback(n);}catch(s){r.error(`[${this.#n}] Error in subscription timeout callback`,{error:s,requestId:e,topic:t.topic});}}cleanup(e){r.info(`[${this.#n}] Cleaning up pending operations`,{publishCount:this.#e.size,subscriptionCount:this.#r.size,reason:e});for(let[t,n]of this.#e){n.timeoutId&&clearTimeout(n.timeoutId);let s={success:false,ack:{},error:{code:"CONNECTION_ERROR",message:e},topic:n.topic};try{n.callback(s);}catch(o){r.error(`[${this.#n}] Error in publish cleanup callback`,{error:o,requestId:t,clientMsgId:n.clientMsgId});}}for(let[t,n]of this.#r){n.timeoutId&&clearTimeout(n.timeoutId);let s={success:false,error:{code:"CONNECTION_ERROR",message:e},topic:n.topic,path:n.path};try{n.callback(s);}catch(o){r.error(`[${this.#n}] Error in subscription cleanup callback`,{error:o,requestId:t,clientMsgId:n.clientMsgId,topic:n.topic});}}this.#e.clear(),this.#t.clear(),this.#r.clear(),this.#i.clear();}getPendingCount(){return this.#e.size}getPendingSubscriptionCount(){return this.#r.size}#h(e,t){return e.result.path==="publish"?"result"in e.result&&e.result.result.ok?{success:true,ack:e,seq:e.result.seq,serverMsgId:e.result.serverAssignedId,topic:e.result.topic}:"result"in e.result&&!e.result.result.ok?{success:false,ack:e,error:{code:e.result.result.code,message:e.result.result.message},topic:e.result.topic}:{success:false,ack:e,error:{code:"MALFORMED_ACK",message:"ACK packet has invalid structure"},topic:t.topic}:(r.info(`[${this.#n}] Received non-publish ACK`,{path:e.result.path,clientMsgId:e.clientMsgId,note:"Subscription ACKs are not tracked in AckManager"}),{success:false,ack:e,error:{code:"INVALID_ACK_TYPE",message:"Received non-publish ACK for publish operation"},topic:t.topic})}#o(e,t){if(e.result.path==="subscribe"||e.result.path==="unsubscribe")if("result"in e.result){let n=e.result.result;if(n.ok)return {success:true,ack:e,topic:e.result.topic,status:n.status,path:e.result.path};{let s=n;return {success:false,ack:e,error:{code:s.code||"SUBSCRIPTION_ERROR",message:s.message||"Subscription operation failed"},topic:e.result.topic,path:e.result.path}}}else return {success:false,ack:e,error:{code:"MALFORMED_ACK",message:"Subscription ACK packet has invalid structure"},topic:t.topic,path:t.path};else return {success:false,ack:e,error:{code:"INVALID_ACK_TYPE",message:"Received non-subscription ACK for subscription operation"},topic:t.topic,path:t.path}}};var q=class{#e=new Set;#t=new Set;#r=new Set;#i;constructor(e){this.#i=e,r.info(`[${this.#i}] SubscriptionManager created`);}get subscriptions(){return Array.from(this.#e)}get subscribedTopics(){return Array.from(this.#t).filter(e=>!this.#r.has(e))}get unsubscribedTopics(){return Array.from(this.#r)}get subscriptionCount(){return this.#e.size}subscribe(e){if(r.info(`[${this.#i}] Subscribe to topic`,{topic:e}),!e||typeof e!="string"||e.trim().length===0){let t="Invalid topic: must be a non-empty string";throw r.error(`[${this.#i}] ${t}`,{topic:e}),new Error(t)}this.#t.add(e),this.#r.delete(e),this.#e.add(e),r.info(`[${this.#i}] Topic added to subscriptions`,{topic:e,totalSubs:this.#e.size});}unsubscribe(e){if(r.info(`[${this.#i}] Unsubscribe from topic`,{topic:e}),!e||typeof e!="string"||e.trim().length===0){let t="Invalid topic: must be a non-empty string";throw r.error(`[${this.#i}] ${t}`,{topic:e}),new Error(t)}this.#r.add(e),this.#t.delete(e),this.#e.delete(e),r.info(`[${this.#i}] Topic removed from subscriptions`,{topic:e,totalSubs:this.#e.size});}isSubscribed(e){return this.#t.has(e)&&!this.#r.has(e)}getSubscriptionStatus(e){return this.#t.has(e)&&!this.#r.has(e)?"subscribed":this.#r.has(e)?"unsubscribed":"pending"}clear(){r.info(`[${this.#i}] Clearing all subscriptions`),this.#e.clear(),this.#t.clear(),this.#r.clear();}getSubscriptionTracking(){let e=Array.from(this.#e).filter(t=>!this.#t.has(t)&&!this.#r.has(t));return {subscribed:this.subscribedTopics,unsubscribed:this.unsubscribedTopics,pending:e}}getTopicsForResubscription(){return Array.from(this.#e)}confirmSubscription(e){r.info(`[${this.#i}] Confirming subscription`,{topic:e}),this.#t.add(e),this.#r.delete(e);}confirmUnsubscription(e){r.info(`[${this.#i}] Confirming unsubscription`,{topic:e}),this.#r.add(e),this.#t.delete(e),this.#e.delete(e);}};var H=class{#e;#t;#r;#i;constructor(e,t,n,s){this.#e=e,this.#t=t,this.#r=n,this.#i=s,r.info(`[${this.#e}] MessageProcessor created`);}async processMessage(e){if(r.info(`[${this.#e}] Processing message`,{dataLength:e.length}),e.length===0)return r.warn(`[${this.#e}] Data string is empty, skipping`),null;if(e==="ping")return r.warn(`[${this.#e}] Ping packet, skipping`,{rawDataPreview:e.slice(0,200)}),null;let t=Ce(e);return t?(r.info(`[${this.#e}] Parsed packet`,{packetType:t.packetType}),await this.handlePacket(t),t):(r.warn(`[${this.#e}] Failed to parse server frame`,{rawDataPreview:e.slice(0,200)}),null)}async handlePacket(e){try{e.packetType==="ack"?await this.#n(e):e.packetType==="publish"?(r.info(`[${this.#e}] Handling publish message`,{topic:e.payload?.topic,messageId:e.payload?.id||"unknown"}),this.#t(e)):e.packetType==="presence"?(r.info(`[${this.#e}] Handling presence packet`,{topic:e.topic,clientId:e.clientId,status:e.status}),this.#i.handlePresencePacket(e)):r.warn(`[${this.#e}] Unknown packet type`,{packetType:e.packetType});}catch(t){r.error(`[${this.#e}] Error handling packet`,{error:t,packetType:e.packetType});}}async#n(e){r.info(`[${this.#e}] Processing ACK packet`,{clientMsgId:e.clientMsgId,path:e.result.path}),this.#r.handleAck(e);}};var se="erebus:grant";function ie(i,e="data"){return !i||i.length<8?`${e}: [too short]`:`${e}: ${i.substring(0,4)}...${i.substring(i.length-4)}`}var W=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t,r.info(`[${this.#e}] GrantManager created`);}getCachedGrant(){r.info(`[${this.#e}] Attempting to get cached grant`);try{if(typeof localStorage<"u"){let e=localStorage.getItem(se);return e?r.info(`[${this.#e}] Cached grant found`,{grantPreview:ie(e,"cached_grant")}):r.info(`[${this.#e}] No cached grant found`),e??void 0}else r.info(`[${this.#e}] localStorage not available (non-browser environment)`);}catch(e){r.warn(`[${this.#e}] Error accessing cached grant`,{error:e});}}setCachedGrant(e){r.info(`[${this.#e}] Setting cached grant`,{grantPreview:ie(e,"grant_to_cache")});try{typeof localStorage<"u"?(localStorage.setItem(se,e),r.info(`[${this.#e}] Grant cached successfully`)):r.info(`[${this.#e}] Cannot cache grant - localStorage not available`);}catch(t){r.warn(`[${this.#e}] Error caching grant`,{error:t});}}clearCachedGrant(){r.info(`[${this.#e}] Clearing cached grant`);try{typeof localStorage<"u"?(localStorage.removeItem(se),r.info(`[${this.#e}] Cached grant cleared successfully`)):r.info(`[${this.#e}] Cannot clear grant - localStorage not available`);}catch(e){r.warn(`[${this.#e}] Error clearing cached grant`,{error:e});}}async getToken(e){r.info(`[${this.#e}] Getting token from provider`,{channel:e});try{let t=await this.#t(e);if(!t)throw r.error(`[${this.#e}] No token provided by token provider`),new Error("No token provided");return r.info(`[${this.#e}] Token received`,{tokenPreview:ie(t,"token"),channel:e}),t}catch(t){throw r.error(`[${this.#e}] Error getting token from provider`,{error:t,channel:e}),t}}async getTokenWithCache(e){let t=this.getCachedGrant();return t?r.info(`[${this.#e}] Using cached grant`,{channel:e}):(r.info(`[${this.#e}] No cached grant, requesting fresh token`,{channel:e}),t=await this.getToken(e),t&&(r.info(`[${this.#e}] Fresh token received, caching it`),this.setCachedGrant(t))),t}};var z=class{#e;#t;#r;#i;#n;constructor(e,t,n,s){this.#e=e,this.#t=t,this.#i=n,this.#n=s,r.info(`[${this.#e}] HeartbeatManager created`,{intervalMs:t});}get isRunning(){return this.#r!==void 0}start(){r.info(`[${this.#e}] Starting heartbeat`,{intervalMs:this.#t}),this.stop(),this.#r=setInterval(()=>{try{this.#n("info","sending heartbeat ping"),r.info(`[${this.#e}] Sending heartbeat ping`),this.#i();}catch(e){throw r.error(`[${this.#e}] Error sending heartbeat`,{error:e}),e}},this.#t),r.info(`[${this.#e}] Heartbeat started successfully`);}stop(){this.#r?(r.info(`[${this.#e}] Stopping heartbeat`),clearInterval(this.#r),this.#r=void 0):r.debug(`[${this.#e}] No heartbeat to stop`);}setInterval(e){r.info(`[${this.#e}] Updating heartbeat interval`,{oldInterval:this.#t,newInterval:e});let t=this.isRunning;this.stop(),this.#t=e,t&&this.start();}getInterval(){return this.#t}};var D=class{#e;#t=new Map;constructor(e){this.#e=e,r.info(`[${this.#e}] PresenceManager created`);}onPresence(e,t){if(r.info(`[${this.#e}] Adding presence handler for topic`,{topic:e}),!e||typeof e!="string"||e.trim().length===0){let n="Invalid topic: must be a non-empty string";throw r.error(`[${this.#e}] ${n}`,{topic:e}),new Error(n)}if(typeof t!="function"){let n="Invalid handler: must be a function";throw r.error(`[${this.#e}] ${n}`),new Error(n)}this.#t.has(e)||this.#t.set(e,new Set),this.#t.get(e).add(t),r.info(`[${this.#e}] Presence handler added`,{topic:e,totalHandlers:this.#t.get(e).size});}offPresence(e,t){r.info(`[${this.#e}] Removing presence handler for topic`,{topic:e});let n=this.#t.get(e);if(!n){r.warn(`[${this.#e}] No handlers found for topic`,{topic:e});return}n.delete(t),n.size===0&&this.#t.delete(e),r.info(`[${this.#e}] Presence handler removed`,{topic:e,remainingHandlers:n.size});}clearPresenceHandlers(e){r.info(`[${this.#e}] Clearing all presence handlers for topic`,{topic:e}),this.#t.delete(e);}clearAllPresenceHandlers(){r.info(`[${this.#e}] Clearing all presence handlers`),this.#t.clear();}handlePresencePacket(e){r.info(`[${this.#e}] Handling presence packet`,{clientId:e.clientId,topic:e.topic,status:e.status});let t=this.#t.get(e.topic);if(!t||t.size===0){r.debug(`[${this.#e}] No presence handlers found for topic`,{topic:e.topic});return}let n={clientId:e.clientId,topic:e.topic,status:e.status,timestamp:Date.now(),...e.subscribers&&{subscribers:e.subscribers}};for(let s of t)try{s(n);}catch(o){r.error(`[${this.#e}] Error in presence handler`,{error:o,topic:e.topic,clientId:e.clientId});}r.info(`[${this.#e}] Presence packet handled successfully`,{topic:e.topic,handlersCount:t.size});}getTopicsWithPresenceHandlers(){return Array.from(this.#t.keys())}getPresenceHandlerCount(e){return this.#t.get(e)?.size||0}getTotalPresenceHandlerCount(){let e=0;for(let t of this.#t.values())e+=t.size;return e}get __debugPresenceHandlers(){return new Map(this.#t)}};var B=class{#e;#t;#r;#i;#n;#s;#a;#h=3e4;#o;constructor(e){this.#o=`conn_${Math.random().toString(36).slice(2,8)}`,r.info(`[${this.#o}] PubSubConnection constructor called`,{url:e.url,heartbeatMs:e.heartbeatMs??25e3}),this.#t=new O(this.#o),this.#r=new q(this.#o),this.#a=new D(this.#o),this.#n=new W(this.#o,e.tokenProvider),this.#i=new H(this.#o,e.onMessage,this.#t,this.#a);let t={...e,onMessage:async n=>{n.rawData?await this.#i.processMessage(n.rawData):await this.#i.handlePacket(n);}};this.#e=new j(t),this.#s=new z(this.#o,e.heartbeatMs??25e3,()=>this.#l(),e.log??(()=>{})),r.info(`[${this.#o}] PubSubConnection initialized`);}get state(){return this.#e.state}get isConnected(){return this.#e.isConnected}get isConnecting(){return this.#e.isConnecting}get isClosed(){return this.#e.isClosed}get isIdle(){return this.#e.isIdle}get isReadable(){return this.#e.isConnected}get isWritable(){return this.#e.isConnected}get channel(){return this.#e.channel}get subscriptionCount(){return this.#r.subscriptionCount}get subscriptions(){return this.#r.subscriptions}get readyState(){return this.#e.readyState}get bufferedAmount(){return this.#e.bufferedAmount}get connectionId(){return this.#e.connectionId}get url(){return this.#e.url}get connectionHealth(){return {state:this.state,isConnected:this.isConnected,isReadable:this.isReadable,isWritable:this.isWritable,channel:this.channel,subscriptionCount:this.subscriptionCount,readyState:this.readyState,bufferedAmount:this.bufferedAmount,connectionId:this.connectionId,url:this.url}}get subscribedTopics(){return this.#r.subscribedTopics}get unsubscribedTopics(){return this.#r.unsubscribedTopics}get subscriptionTracking(){return this.#r.getSubscriptionTracking()}async open(e){let t=await this.#n.getTokenWithCache(this.channel);await this.#e.open({grant:t,timeout:e}),this.#e.send({packetType:"connect",grantJWT:t}),this.#s.start();let n=this.#r.getTopicsForResubscription();for(let s of n)r.info(`[${this.#o}] Resubscribing to topic`,{topic:s}),this.#e.send({packetType:"subscribe",topic:s});}close(){this.#s.stop(),this.#t.cleanup("Connection closed"),this.#e.close(),this.#r.clear(),this.#n.clearCachedGrant();}subscribe(e){this.subscribeWithCallback(e);}subscribeWithCallback(e,t,n){if(r.info(`[${this.#o}] Subscribe called`,{topic:e,hasCallback:!!t,timeout:n}),this.#r.subscribe(e),this.isConnected){r.info(`[${this.#o}] Connection open, sending subscribe packet`,{topic:e});try{let s,o;if(t){s=`sub_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,typeof crypto<"u"&&typeof crypto.randomUUID=="function"&&(o=crypto.randomUUID());let a={requestId:s,clientMsgId:o,topic:e,path:"subscribe",callback:t,timestamp:Date.now()};n&&n>0&&(a.timeoutId=setTimeout(()=>{this.#t.handleSubscriptionTimeout(s);},n)),this.#t.trackSubscription(s,a);}this.#e.send({packetType:"subscribe",topic:e,...s&&{requestId:s},...o&&{clientMsgId:o}});}catch(s){throw r.error(`[${this.#o}] Error sending subscribe packet`,{error:s,topic:e}),this.#r.unsubscribe(e),s}}else r.info(`[${this.#o}] Connection not open, subscription will be sent when connected`,{topic:e});}unsubscribe(e){this.unsubscribeWithCallback(e);}unsubscribeWithCallback(e,t,n){if(r.info(`[${this.#o}] Unsubscribe called`,{topic:e,hasCallback:!!t,timeout:n}),this.#r.unsubscribe(e),this.isConnected){r.info(`[${this.#o}] Connection open, sending unsubscribe packet`,{topic:e});try{let s,o;if(t){s=`unsub_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,typeof crypto<"u"&&typeof crypto.randomUUID=="function"&&(o=crypto.randomUUID());let a={requestId:s,clientMsgId:o,topic:e,path:"unsubscribe",callback:t,timestamp:Date.now()};n&&n>0&&(a.timeoutId=setTimeout(()=>{this.#t.handleSubscriptionTimeout(s);},n)),this.#t.trackSubscription(s,a);}this.#e.send({packetType:"unsubscribe",topic:e,...s&&{requestId:s},...o&&{clientMsgId:o}});}catch(s){r.error(`[${this.#o}] Error sending unsubscribe packet`,{error:s,topic:e});}}else r.info(`[${this.#o}] Connection not open, unsubscription will be sent when connected`,{topic:e});}publish(e){this.#c(e,false);}publishWithAck(e,t,n=this.#h){return this.#c(e,true,t,n)}isSubscribed(e){return this.#r.isSubscribed(e)}getSubscriptionStatus(e){return this.#r.getSubscriptionStatus(e)}setChannel(e){this.#e.setChannel(e),this.#n.clearCachedGrant();}onPresence(e,t){this.#a.onPresence(e,t);}offPresence(e,t){this.#a.offPresence(e,t);}clearPresenceHandlers(e){this.#a.clearPresenceHandlers(e);}#c(e,t=false,n,s){if(r.info(`[${this.#o}] Publish called`,{topic:e.topic,withAck:t}),!e||typeof e!="object"){let l="Invalid payload: must be an object";throw r.error(`[${this.#o}] ${l}`,{payload:e}),new Error(l)}if(t&&!n){let l="ACK callback is required when withAck is true";throw r.error(`[${this.#o}] ${l}`),new Error(l)}if(!this.isConnected)throw r.error(`[${this.#o}] Cannot publish - not connected`,{state:this.state}),new C("Not connected");let o=`fallback_${Date.now()}_${Math.random().toString(36).slice(2,9)}`,a;try{if(e.clientPublishTs||Object.assign(e,{clientPublishTs:Date.now()}),e.clientMsgId?o=e.clientMsgId:(typeof crypto<"u"&&typeof crypto.randomUUID=="function"?o=crypto.randomUUID():o=`msg_${Date.now()}_${Math.random().toString(36).slice(2,9)}`,Object.assign(e,{clientMsgId:o})),t&&n){a=`req_${Date.now()}_${nanoid.nanoid()}`;let l={requestId:a,clientMsgId:o,topic:e.topic,callback:n,timestamp:Date.now()};s&&s>0&&(l.timeoutId=setTimeout(()=>{this.#t.handlePublishTimeout(a);},s)),this.#t.trackPublish(a,l);}}catch(l){Object.assign(e,{clientMsgId:o}),r.warn(`[${this.#o}] Error setting client fields, using fallback`,{err:l,clientMsgId:o});}r.info(`[${this.#o}] Publishing message`,{topic:e.topic,withAck:t,requestId:a,clientMsgId:o}),h__default.default.info(`[PubSubConnection] [${this.#o}] Publishing message`,{topic:e.topic,withAck:t});try{this.#e.send({packetType:"publish",topic:e.topic,ack:t,payload:e,clientMsgId:o,...t&&a&&{requestId:a}});}catch(l){throw t&&a&&this.#t.getPendingCount()>0,r.error(`[${this.#o}] Error publishing message`,{error:l,topic:e.topic}),l}return Promise.resolve(o)}#l(){if(!this.isConnected){r.debug(`[${this.#o}] Skipping heartbeat - not connected`);return}try{this.#e.sendRaw("ping");}catch(e){throw r.error(`[${this.#o}] Error sending heartbeat`,{error:e}),this.#e.close(),e}}get __debugObject(){return {connectionManager:this.#e,ackManager:this.#t,subscriptionManager:this.#r,messageProcessor:this.#i,grantManager:this.#n,heartbeatManager:this.#s}}get __debugState(){return this.#e.state}};var K=class{#e;#t="idle";#r=null;#i=new Map;#n=new Set;#s=new Set;#a=new Map;#h=Date.now();#o=null;#c=false;#l=0;constructor(e){this.#e=e,r.info(`[${this.#e}] StateManager created`);}get connectionState(){return this.#t}setConnectionState(e){r.info(`[${this.#e}] Connection state changed`,{from:this.#t,to:e}),this.#t=e,this.#u();}get isConnected(){return this.#t==="open"}get isConnecting(){return this.#t==="connecting"}get isClosed(){return this.#t==="closed"}get isIdle(){return this.#t==="idle"}get channel(){return this.#r}setChannel(e){r.info(`[${this.#e}] Channel set`,{channel:e}),this.#r=e,this.#u();}get subscriptionCount(){return this.#i.size}get activeTopics(){return Array.from(this.#i.entries()).filter(([e,t])=>t==="subscribed").map(([e,t])=>e)}get pendingTopics(){return Array.from(this.#i.entries()).filter(([e,t])=>t==="pending").map(([e,t])=>e)}get unsubscribedTopics(){return Array.from(this.#i.entries()).filter(([e,t])=>t==="unsubscribed").map(([e,t])=>e)}setSubscriptionStatus(e,t){r.info(`[${this.#e}] Subscription status updated`,{topic:e,status:t}),this.#i.set(e,t),this.#u(),t==="subscribed"?this.#s.delete(e):t==="pending"&&this.#s.add(e);}getSubscriptionStatus(e){return this.#i.get(e)||"unsubscribed"}isSubscribed(e){return this.getSubscriptionStatus(e)==="subscribed"}get processedMessagesCount(){return this.#n.size}addProcessedMessage(e){if(this.#n.add(e),this.#u(),this.#n.size>1e3){let t=Array.from(this.#n);this.#n.clear(),t.slice(-500).forEach(n=>this.#n.add(n));}}isMessageProcessed(e){return this.#n.has(e)}clearProcessedMessages(){r.info(`[${this.#e}] Clearing processed messages`),this.#n.clear();}get handlerCount(){let e=0;for(let t of this.#a.values())e+=t.size;return e}getTopicsWithHandlers(){return Array.from(this.#a.keys())}getHandlerCountForTopic(e){return this.#a.get(e)?.size||0}addHandler(e,t){this.#a.has(e)||this.#a.set(e,new Set),this.#a.get(e).add(t),this.#u();}removeHandler(e,t){let n=this.#a.get(e);n&&(n.delete(t),n.size===0&&this.#a.delete(e)),this.#u();}clearHandlers(e){this.#a.delete(e),this.#u();}getHandlers(e){return this.#a.get(e)}get pendingSubscriptionsCount(){return this.#s.size}addPendingSubscription(e){this.#s.add(e),this.#u();}removePendingSubscription(e){this.#s.delete(e),this.#u();}clearPendingSubscriptions(){r.info(`[${this.#e}] Clearing pending subscriptions`),this.#s.clear();}get hasError(){return this.#o!==null}get error(){return this.#o}setError(e){r.error(`[${this.#e}] Error set`,{error:e}),this.#o=e,this.#u();}clearError(){r.info(`[${this.#e}] Error cleared`),this.#o=null,this.#u();}get isReconnecting(){return this.#c}setReconnecting(e){this.#c=e,this.#u();}get reconnectAttempts(){return this.#l}incrementReconnectAttempts(){this.#l++,this.#u();}resetReconnectAttempts(){this.#l=0,this.#u();}get lastActivity(){return this.#h}#u(){this.#h=Date.now();}get stateSummary(){return {connectionState:this.#t,channel:this.#r,subscriptionCount:this.subscriptionCount,handlerCount:this.handlerCount,processedMessagesCount:this.processedMessagesCount,pendingSubscriptionsCount:this.pendingSubscriptionsCount,hasError:this.hasError,isReconnecting:this.#c,reconnectAttempts:this.#l,lastActivity:this.#h}}reset(){r.info(`[${this.#e}] Resetting state manager`),this.#t="idle",this.#r=null,this.#i.clear(),this.#n.clear(),this.#s.clear(),this.#a.clear(),this.#o=null,this.#c=false,this.#l=0,this.#u();}clear(){r.info(`[${this.#e}] Clearing state manager`),this.#i.clear(),this.#n.clear(),this.#s.clear(),this.#a.clear(),this.#o=null,this.#c=false,this.#l=0,this.#u();}get __debugState(){return {connectionState:this.#t,channel:this.#r,subscriptions:new Map(this.#i),handlers:new Map(this.#a),processedMessages:new Set(this.#n),pendingSubscriptions:new Set(this.#s),error:this.#o,isReconnecting:this.#c,reconnectAttempts:this.#l,lastActivity:this.#h}}};var I=class{#e;#t;#r;constructor(e){this.#r=e.debug??false;let t=Math.random().toString(36).substring(2,8);h__default.default.info(`[Erebus:${t}] Constructor called`,{wsUrl:e.wsUrl,hasTokenProvider:!!e.tokenProvider,hasCustomLog:!!e.log}),r.info("Erebus constructor called",{opts:e}),this.#e=new B({url:e.wsUrl,tokenProvider:e.tokenProvider,channel:"",heartbeatMs:e.heartbeatMs,log:e.log,onMessage:n=>this.#n(n)}),this.#t=new K(this.#e.connectionId),h__default.default.info(`[Erebus:${t}] Instance created successfully`,{wsUrl:e.wsUrl}),r.info("Erebus instance created",{wsUrl:e.wsUrl});}connect(e){let t=this.#e.connectionId;if(h__default.default.info(`[Erebus:${t}] Connect called`,{timeout:e}),r.info("Erebus.connect() called"),!this.#t.channel){let n="Channel must be set before connecting. Call joinChannel(channel) first.";throw h__default.default.error(`[Erebus:${t}] ${n}`),r.error("Connect failed - no channel set"),new Error(n)}return this.#t.clearProcessedMessages(),this.#e.open(e)}joinChannel(e){let t=this.#e.connectionId;if(h__default.default.info(`[Erebus:${t}] Joining channel`,{channel:e}),r.info("Erebus.joinChannel() called",{channel:e}),!e||typeof e!="string"||e.trim().length===0){let n="Invalid channel: must be a non-empty string";throw h__default.default.error(`[Erebus:${t}] ${n}`,{channel:e}),r.error("Invalid channel",{channel:e}),new Error(n)}this.