nostr-websocket-utils
Version:
Robust WebSocket utilities for Nostr applications with automatic reconnection, supporting both ESM and CommonJS. Features channel-based messaging, heartbeat monitoring, message queueing, and comprehensive error handling with type-safe handlers.
4 lines (3 loc) • 14.2 kB
JavaScript
"use strict";var NostrWebSocketUtils=(()=>{var re=Object.create;var L=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ie=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var W=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),ae=(e,t)=>{for(var r in t)L(e,r,{get:t[r],enumerable:!0})},G=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of se(t))!oe.call(e,i)&&i!==r&&L(e,i,{get:()=>t[i],enumerable:!(s=ne(t,i))||s.enumerable});return e};var V=(e,t,r)=>(r=e!=null?re(ie(e)):{},G(t||!e||!e.__esModule?L(r,"default",{value:e,enumerable:!0}):r,e)),ce=e=>G(L({},"__esModule",{value:!0}),e);var H=W((je,Q)=>{"use strict";Q.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}});var J=W((qe,B)=>{"use strict";function le(e){try{return JSON.stringify(e)}catch{return'"[Circular]"'}}B.exports=ue;function ue(e,t,r){var s=r&&r.stringify||le,i=1;if(typeof e=="object"&&e!==null){var a=t.length+i;if(a===1)return e;var f=new Array(a);f[0]=s(e);for(var u=1;u<a;u++)f[u]=s(t[u]);return f.join(" ")}if(typeof e!="string")return e;var m=t.length;if(m===0)return e;for(var n="",c=1-i,o=-1,g=e&&e.length||0,l=0;l<g;){if(e.charCodeAt(l)===37&&l+1<g){switch(o=o>-1?o:0,e.charCodeAt(l+1)){case 100:case 102:if(c>=m||t[c]==null)break;o<l&&(n+=e.slice(o,l)),n+=Number(t[c]),o=l+2,l++;break;case 105:if(c>=m||t[c]==null)break;o<l&&(n+=e.slice(o,l)),n+=Math.floor(Number(t[c])),o=l+2,l++;break;case 79:case 111:case 106:if(c>=m||t[c]===void 0)break;o<l&&(n+=e.slice(o,l));var w=typeof t[c];if(w==="string"){n+="'"+t[c]+"'",o=l+2,l++;break}if(w==="function"){n+=t[c].name||"<anonymous>",o=l+2,l++;break}n+=s(t[c]),o=l+2,l++;break;case 115:if(c>=m)break;o<l&&(n+=e.slice(o,l)),n+=String(t[c]),o=l+2,l++;break;case 37:o<l&&(n+=e.slice(o,l)),n+="%",o=l+2,l++,c--;break}++c}++l}return o===-1?e:(o<g&&(n+=e.slice(o)),n)}});var ee=W((Ie,j)=>{"use strict";var K=J();j.exports=p;var N=Ce().console||{},fe={mapHttpRequest:T,mapHttpResponse:T,wrapRequestSerializer:M,wrapResponseSerializer:M,wrapErrorSerializer:M,req:T,res:T,err:$,errWithCause:$};function b(e,t){return e==="silent"?1/0:t.levels.values[e]}var q=Symbol("pino.logFuncs"),z=Symbol("pino.hierarchy"),me={error:"log",fatal:"error",warn:"error",info:"log",debug:"log",trace:"log"};function U(e,t){let r={logger:t,parent:e[z]};t[z]=r}function he(e,t,r){let s={};t.forEach(i=>{s[i]=r[i]?r[i]:N[i]||N[me[i]||"log"]||v}),e[q]=s}function ge(e,t){return Array.isArray(e)?e.filter(function(s){return s!=="!stdSerializers.err"}):e===!0?Object.keys(t):!1}function p(e){e=e||{},e.browser=e.browser||{};let t=e.browser.transmit;if(t&&typeof t.send!="function")throw Error("pino: transmit option must have a send function");let r=e.browser.write||N;e.browser.write&&(e.browser.asObject=!0);let s=e.serializers||{},i=ge(e.browser.serialize,s),a=e.browser.serialize;Array.isArray(e.browser.serialize)&&e.browser.serialize.indexOf("!stdSerializers.err")>-1&&(a=!1);let f=Object.keys(e.customLevels||{}),u=["error","fatal","warn","info","debug","trace"].concat(f);typeof r=="function"&&u.forEach(function(h){r[h]=r}),(e.enabled===!1||e.browser.disabled)&&(e.level="silent");let m=e.level||"info",n=Object.create(r);n.log||(n.log=v),he(n,u,r),U({},n),Object.defineProperty(n,"levelVal",{get:o}),Object.defineProperty(n,"level",{get:g,set:l});let c={transmit:t,serialize:i,asObject:e.browser.asObject,asObjectBindingsOnly:e.browser.asObjectBindingsOnly,formatters:e.browser.formatters,reportCaller:e.browser.reportCaller,levels:u,timestamp:Ee(e),messageKey:e.messageKey||"msg",onChild:e.onChild||v};n.levels=de(e),n.level=m,n.isLevelEnabled=function(h){return this.levels.values[h]?this.levels.values[h]>=this.levels.values[this.level]:!1},n.setMaxListeners=n.getMaxListeners=n.emit=n.addListener=n.on=n.prependListener=n.once=n.prependOnceListener=n.removeListener=n.removeAllListeners=n.listeners=n.listenerCount=n.eventNames=n.write=n.flush=v,n.serializers=s,n._serialize=i,n._stdErrSerialize=a,n.child=function(...h){return w.call(this,c,...h)},t&&(n._logEvent=A());function o(){return b(this.level,this)}function g(){return this._level}function l(h){if(h!=="silent"&&!this.levels.values[h])throw Error("unknown level "+h);this._level=h,y(this,c,n,"error"),y(this,c,n,"fatal"),y(this,c,n,"warn"),y(this,c,n,"info"),y(this,c,n,"debug"),y(this,c,n,"trace"),f.forEach(d=>{y(this,c,n,d)})}function w(h,d,S){if(!d)throw new Error("missing bindings for child Pino");S=S||{},i&&d.serializers&&(S.serializers=d.serializers);let R=S.serializers;if(i&&R){var C=Object.assign({},s,R),F=e.browser.serialize===!0?Object.keys(C):i;delete d.serializers,I([d],F,C,this._stdErrSerialize)}function P(x){this._childLevel=(x._childLevel|0)+1,this.bindings=d,C&&(this.serializers=C,this._serialize=F),t&&(this._logEvent=A([].concat(x._logEvent.bindings,d)))}P.prototype=this;let E=new P(this);return U(this,E),E.child=function(...x){return w.call(this,h,...x)},E.level=S.level||this.level,h.onChild(E),E}return n}function de(e){let t=e.customLevels||{},r=Object.assign({},p.levels.values,t),s=Object.assign({},p.levels.labels,pe(t));return{values:r,labels:s}}function pe(e){let t={};return Object.keys(e).forEach(function(r){t[e[r]]=r}),t}p.levels={values:{fatal:60,error:50,warn:40,info:30,debug:20,trace:10},labels:{10:"trace",20:"debug",30:"info",40:"warn",50:"error",60:"fatal"}};p.stdSerializers=fe;p.stdTimeFunctions=Object.assign({},{nullTime:Y,epochTime:Z,unixTime:Ne,isoTime:Oe});function be(e){let t=[];e.bindings&&t.push(e.bindings);let r=e[z];for(;r.parent;)r=r.parent,r.logger.bindings&&t.push(r.logger.bindings);return t.reverse()}function y(e,t,r,s){if(Object.defineProperty(e,s,{value:b(e.level,r)>b(s,r)?v:r[q][s],writable:!0,enumerable:!0,configurable:!0}),e[s]===v){if(!t.transmit)return;let a=t.transmit.level||e.level,f=b(a,r);if(b(s,r)<f)return}e[s]=ve(e,t,r,s);let i=be(e);i.length!==0&&(e[s]=ye(i,e[s]))}function ye(e,t){return function(){return t.apply(this,[...e,...arguments])}}function ve(e,t,r,s){return(function(i){return function(){let f=t.timestamp(),u=new Array(arguments.length),m=Object.getPrototypeOf&&Object.getPrototypeOf(this)===N?N:this;for(var n=0;n<u.length;n++)u[n]=arguments[n];var c=!1;if(t.serialize&&(I(u,this._serialize,this.serializers,this._stdErrSerialize),c=!0),t.asObject||t.formatters){let o=we(this,s,u,f,t);if(t.reportCaller&&o&&o.length>0&&o[0]&&typeof o[0]=="object")try{let g=X();g&&(o[0].caller=g)}catch{}i.call(m,...o)}else{if(t.reportCaller)try{let o=X();o&&u.push(o)}catch{}i.apply(m,u)}if(t.transmit){let o=t.transmit.level||e._level,g=b(o,r),l=b(s,r);if(l<g)return;Se(this,{ts:f,methodLevel:s,methodValue:l,transmitLevel:o,transmitValue:r.levels.values[t.transmit.level||e._level],send:t.transmit.send,val:b(e._level,r)},u,c)}}})(e[q][s])}function we(e,t,r,s,i){let{level:a,log:f=o=>o}=i.formatters||{},u=r.slice(),m=u[0],n={},c=(e._childLevel|0)+1;if(c<1&&(c=1),s&&(n.time=s),a){let o=a(t,e.levels.values[t]);Object.assign(n,o)}else n.level=e.levels.values[t];if(i.asObjectBindingsOnly){if(m!==null&&typeof m=="object")for(;c--&&typeof u[0]=="object";)Object.assign(n,u.shift());return[f(n),...u]}else{if(m!==null&&typeof m=="object"){for(;c--&&typeof u[0]=="object";)Object.assign(n,u.shift());m=u.length?K(u.shift(),u):void 0}else typeof m=="string"&&(m=K(u.shift(),u));return m!==void 0&&(n[i.messageKey]=m),[f(n)]}}function I(e,t,r,s){for(let i in e)if(s&&e[i]instanceof Error)e[i]=p.stdSerializers.err(e[i]);else if(typeof e[i]=="object"&&!Array.isArray(e[i])&&t)for(let a in e[i])t.indexOf(a)>-1&&a in r&&(e[i][a]=r[a](e[i][a]))}function Se(e,t,r,s=!1){let i=t.send,a=t.ts,f=t.methodLevel,u=t.methodValue,m=t.val,n=e._logEvent.bindings;s||I(r,e._serialize||Object.keys(e.serializers),e.serializers,e._stdErrSerialize===void 0?!0:e._stdErrSerialize),e._logEvent.ts=a,e._logEvent.messages=r.filter(function(c){return n.indexOf(c)===-1}),e._logEvent.level.label=f,e._logEvent.level.value=u,i(f,e._logEvent,m),e._logEvent=A(n)}function A(e){return{ts:0,messages:[],bindings:e||[],level:{label:"",value:0}}}function $(e){let t={type:e.constructor.name,msg:e.message,stack:e.stack};for(let r in e)t[r]===void 0&&(t[r]=e[r]);return t}function Ee(e){return typeof e.timestamp=="function"?e.timestamp:e.timestamp===!1?Y:Z}function T(){return{}}function M(e){return e}function v(){}function Y(){return!1}function Z(){return Date.now()}function Ne(){return Math.round(Date.now()/1e3)}function Oe(){return new Date(Date.now()).toISOString()}function Ce(){function e(t){return typeof t<"u"&&t}try{return typeof globalThis<"u"||Object.defineProperty(Object.prototype,"globalThis",{get:function(){return delete Object.prototype.globalThis,this.globalThis=this},configurable:!0}),globalThis}catch{return e(self)||e(window)||e(this)||{}}}j.exports.default=p;j.exports.pino=p;function X(){let e=new Error().stack;if(!e)return null;let t=e.split(`
`);for(let r=1;r<t.length;r++){let s=t[r].trim();if(/(^at\s+)?(createWrap|LOG|set\s*\(|asObject|Object\.apply|Function\.apply)/.test(s)||s.indexOf("browser.js")!==-1||s.indexOf("node:internal")!==-1||s.indexOf("node_modules")!==-1)continue;let i=s.match(/\((.*?):(\d+):(\d+)\)/);if(i||(i=s.match(/at\s+(.*?):(\d+):(\d+)/)),i){let a=i[1],f=i[2],u=i[3];return a+":"+f+":"+u}}return null}});var Le={};ae(Le,{NostrWSClient:()=>O,default:()=>xe});var te=V(H(),1);var _=V(ee(),1);function k(e){return(0,_.default)({name:e,level:process.env.LOG_LEVEL||"info",timestamp:_.default.stdTimeFunctions.isoTime})}var D=class{constructor(t,r={}){this.sender=t;this.options=r;this.queue=[];this.processing=!1;this.logger=k("MessageQueue")}async enqueue(t){if(this.options.maxSize&&this.queue.length>=this.options.maxSize)throw new Error("Queue is full");let[r,...s]=t,i={type:r,data:s.length===1?s[0]:s,priority:"NORMAL",queuedAt:Date.now(),retryCount:0};this.queue.push(i),this.queue.sort((a,f)=>a.priority===f.priority?a.queuedAt-f.queuedAt:a.priority==="HIGH"?-1:1),this.processing||this.processQueue().catch(a=>{this.logger.error({error:a},"Error processing queue")})}async processQueue(){if(!(this.processing||this.queue.length===0)){this.processing=!0;try{for(;this.queue.length>0;){let t=this.queue[0],r=[t.type,t.data];try{await this.sender(r),this.queue.shift()}catch(s){if(this.logger.error({error:s,message:r},"Failed to send message"),this.options.maxRetries&&t.retryCount>=this.options.maxRetries){this.logger.warn({message:r},"Max retries reached, removing message from queue"),this.queue.shift();continue}t.retryCount++,await new Promise(i=>setTimeout(i,this.options.retryDelay||1e3))}}}finally{this.processing=!1}if(this.options.staleTimeout){let t=Date.now(),r=this.options.staleTimeout;for(let s=this.queue.length-1;s>=0;s--)t-this.queue[s].queuedAt>r&&(this.logger.warn({message:this.queue[s]},"Message is stale, removing from queue"),this.queue.splice(s,1))}}}getSize(){return this.queue.length}clear(){this.queue.length=0}};var O=class{constructor(t,r={}){this.relayUrls=t;this.options=r;this.ws=null;this.connectionState="DISCONNECTED";this.reconnectAttempts=0;this.reconnectTimeout=null;this.logger=r.logger||k("NostrWSClient"),this.queue=new D(async s=>{if(!this.ws||this.connectionState!=="CONNECTED")throw new Error("Not connected to relay");try{this.ws.send(JSON.stringify(s)),this.logger.debug({message:s},"Message sent")}catch(i){throw this.logger.error({error:i,message:s},"Failed to send message"),i}},{maxSize:r.queueSize,maxRetries:r.maxRetries,retryDelay:r.retryDelay})}async connect(){if(this.connectionState==="CONNECTED"){this.logger.debug("Already connected");return}if(this.connectionState==="CONNECTING"){this.logger.debug("Connection already in progress");return}this.connectionState="CONNECTING";try{let t=this.relayUrls[0];t.startsWith("ws://")&&!t.includes("localhost")&&!t.includes("127.0.0.1")&&console.warn("[nostr-websocket] WARNING: Connecting over plaintext ws:// \u2014 messages are not encrypted"),this.ws=new te.default(t),await new Promise((r,s)=>{let i=setTimeout(()=>{s(new Error("Connection timeout"))},this.options.connectionTimeout||5e3);this.ws.on("open",()=>{clearTimeout(i),this.connectionState="CONNECTED",this.reconnectAttempts=0,this.logger.info("Connected to relay"),r()}),this.ws.on("error",a=>{clearTimeout(i),this.logger.error({error:a},"WebSocket error"),this.options.onError&&this.options.onError(a),s(a)}),this.ws.on("close",()=>{this.handleDisconnect()}),this.ws.on("message",a=>{this.handleMessage(a)})})}catch(t){throw this.logger.error({error:t},"Failed to connect"),this.handleDisconnect(),t}}async disconnect(){if(this.connectionState==="DISCONNECTED"){this.logger.debug("Already disconnected");return}this.connectionState="DISCONNECTED",this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.ws&&(this.ws.close(),this.ws=null),this.logger.info("Disconnected from relay")}async sendMessage(t){if(this.connectionState!=="CONNECTED")throw new Error("Not connected to relay");await this.queue.enqueue(t)}handleMessage(t){try{let r=JSON.parse(t.toString());this.logger.debug({message:r},"Received message"),this.options.onMessage&&this.options.onMessage(t.toString())}catch(r){this.logger.error({error:r,data:t},"Failed to parse message"),this.options.onError&&this.options.onError(r)}}handleDisconnect(){if(this.connectionState="DISCONNECTED",this.ws=null,this.options.retryAttempts&&this.reconnectAttempts<this.options.retryAttempts){this.connectionState="RECONNECTING",this.reconnectAttempts++;let t=this.options.retryDelay||1e3,s=Math.min(t*Math.pow(2,this.reconnectAttempts),3e4),i=s*.1*Math.random(),a=s+i;this.logger.info({attempt:this.reconnectAttempts,maxAttempts:this.options.retryAttempts,delay:Math.round(a)},`Reconnecting in ${Math.round(a)}ms`),this.reconnectTimeout=setTimeout(()=>{this.connect().catch(f=>{this.logger.error({error:f},"Reconnection failed")})},a)}else this.logger.warn("Max reconnection attempts reached"),this.connectionState="FAILED"}getConnectionState(){return this.connectionState}};var xe={NostrWSClient:O};return ce(Le);})();
//# sourceMappingURL=nostr-websocket-utils.min.js.map