@candypoets/nipworker 0.0.58 → 0.0.59

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/registry.js CHANGED
@@ -1 +1 @@
1
- class y{constructor(t){this.dataStart=32,this.dropped=0,this.sab=t,this.dataView=new DataView(t),this.capacity=this.dataView.getUint32(0,!0)}getHead(){return this.dataView.getUint32(4,!0)%this.capacity}setHead(t){this.dataView.setUint32(4,t%this.capacity,!0)}getTail(){return this.dataView.getUint32(8,!0)%this.capacity}setTail(t){this.dataView.setUint32(8,t%this.capacity,!0)}getSeq(){return this.dataView.getUint32(12,!0)}setSeq(t){this.dataView.setUint32(12,t,!0)}getFreeSpace(){const t=this.getHead(),e=this.getTail(),s=(t-e+this.capacity)%this.capacity;return this.capacity-s}getDropped(){return this.dropped}hasRecords(){return this.getHead()!==this.getTail()}readNext(){const t=this.read();return t?{payload:t}:null}write(t){const e=t.byteLength,s=8+e,n=4+s+4;let a=0;for(;this.getFreeSpace()<n;){if(!this.skipRecord())return this.dropped+=a+1,-1;a++}const r=this.getSeq()+1;this.setSeq(r);let i=this.getHead();return this.dataView.setUint32(this.dataStart+i,s,!0),i=(i+4)%this.capacity,this.dataView.setUint16(this.dataStart+i,0,!0),i=(i+2)%this.capacity,this.dataView.setUint16(this.dataStart+i,0,!0),i=(i+2)%this.capacity,this.dataView.setUint32(this.dataStart+i,r,!0),i=(i+4)%this.capacity,this.copyBytes(i,t,0,e),i=(i+e)%this.capacity,this.dataView.setUint32(this.dataStart+i,s,!0),i=(i+4)%this.capacity,this.setHead(i),this.dropped+=a,r}read(){let t=this.getTail();if(t===this.getHead())return null;const e=this.dataView.getUint32(this.dataStart+t,!0);if(e===0)return null;const s=(t+4+e)%this.capacity;if(this.dataView.getUint32(this.dataStart+s,!0)!==e)return null;const n=new Uint8Array(e);this.copyFromRing((t+4)%this.capacity,n,0,e);const a=n.subarray(8),r=4+e+4;return this.setTail((this.getTail()+r)%this.capacity),a}skipRecord(){let t=this.getTail();if(t===this.getHead())return!1;const e=this.dataView.getUint32(this.dataStart+t,!0);if(e===0)return!1;const s=(t+4+e)%this.capacity;if(this.dataView.getUint32(this.dataStart+s,!0)!==e)return!1;const n=4+e+4;return this.setTail((this.getTail()+n)%this.capacity),!0}copyBytes(t,e,s,n){let a=n,r=s,i=t;for(;a>0;){const d=this.capacity-i%this.capacity,c=Math.min(a,d),l=this.dataStart+i%this.capacity,u=e.subarray(r,r+c);new Uint8Array(this.sab,l,c).set(u),a-=c,r+=c,i+=c}}copyFromRing(t,e,s,n){let a=n,r=s,i=t;for(;a>0;){const d=this.capacity-i%this.capacity,c=Math.min(a,d),l=this.dataStart+i%this.capacity,u=new Uint8Array(this.sab,l,c);e.set(u,r),a-=c,r+=c,i+=c}}}var o=(h=>(h.Idle="idle",h.Connecting="connecting",h.Ready="ready",h.Closing="closing",h.Closed="closed",h.Error="error",h))(o||{});class w{constructor(t,e={}){this.wantReconnect=!0,this.status=o.Closed,this.ws=null,this.reconnectTimer=null,this.abortController=null,this.attempts=0,this.givenUp=!1,this.lastActivity=Date.now(),this.stats={dropped:0,sent:0,received:0,reconnects:0,lastActivity:Date.now()},this.readyWaiters=[],this.messageHandler=null,this.url=t,this.config={connectTimeoutMs:5e3,writeTimeoutMs:1e4,retry:{baseMs:300,maxMs:1e4,multiplier:1.6,jitter:.1},maxReconnectAttempts:2,idleTimeoutMs:3e5,...e}}getUrl(){return this.url}getStatus(){return this.status}getStats(){return{...this.stats,reconnects:this.attempts,lastActivity:this.lastActivity}}getLastActivity(){return this.lastActivity}hasGivenUp(){return this.givenUp}setMessageHandler(t){this.messageHandler=t}connect(){if(this.givenUp||this.status===o.Connecting||this.status===o.Ready)return;this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.status=o.Connecting,this.abortController=new AbortController;const t=this.abortController.signal;try{this.ws=new WebSocket(this.url),this.ws.binaryType="arraybuffer";const e=()=>{this.ws&&(this.ws.onopen=null,this.ws.onclose=null,this.ws.onerror=null,this.ws.onmessage=null)},s=()=>{clearTimeout(i),this.status=o.Ready,this.attempts=0,this.givenUp=!1,this.lastActivity=Date.now(),this.stats.lastActivity=this.lastActivity,this.resolveReady(!0)},n=c=>{clearTimeout(i),e(),this.status=o.Closed,this.resolveReady(!1),c.code!==1e3&&this.scheduleReconnect()},a=c=>{clearTimeout(i),e(),this.status=o.Closed,this.resolveReady(!1),this.scheduleReconnect()},r=c=>{if(this.lastActivity=Date.now(),this.stats.lastActivity=this.lastActivity,this.stats.received++,typeof c.data=="string"){const l=c.data,u=this.extractSubId(l);this.messageHandler&&this.messageHandler(this.url,u,l)}};this.ws.onopen=s,this.ws.onclose=n,this.ws.onerror=a,this.ws.onmessage=r;const i=setTimeout(()=>{if(this.status===o.Connecting){e();try{this.ws?.close()}catch{}this.status=o.Closed,this.resolveReady(!1),this.scheduleReconnect()}},this.config.connectTimeoutMs),d=()=>{clearTimeout(i),e();try{this.ws?.close()}catch{}this.status=o.Closed,this.resolveReady(!1),this.wantReconnect&&this.scheduleReconnect()};t.addEventListener("abort",d)}catch{this.status=o.Closed,this.resolveReady(!1),this.scheduleReconnect()}}async sendMessage(t){if(this.status!==o.Ready||!this.ws)throw new Error("Connection not ready");this.ws.send(t),this.stats.sent++,this.lastActivity=Date.now(),this.stats.lastActivity=this.lastActivity}async waitForReady(t=this.config.connectTimeoutMs??5e3){if(this.status!==o.Ready)return new Promise((e,s)=>{const n=setTimeout(()=>{this.removeReadyResolver(a),s(new Error("Timeout waiting for ready"))},t),a=r=>{clearTimeout(n),r?e():s(new Error("Connection closed"))};this.readyWaiters.push(a)})}async close(){this.wantReconnect=!1,this.abortController&&this.abortController.abort(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.closeWebSocket(),this.status=o.Closed,this.resolveReady(!1)}shouldCloseDueToInactivity(){return Date.now()-this.lastActivity>(this.config.idleTimeoutMs??3e5)}resolveReady(t){if(this.readyWaiters.length===0)return;const e=this.readyWaiters.slice();this.readyWaiters.length=0;for(const s of e)try{s(t)}catch{}}removeReadyResolver(t){const e=this.readyWaiters.indexOf(t);e>=0&&this.readyWaiters.splice(e,1)}closeWebSocket(){if(this.ws){try{this.ws.close(1e3,"Normal closure")}catch{}this.ws=null}}scheduleReconnect(){if(!this.wantReconnect||this.status!==o.Closed||!this.config.retry?.baseMs)return;const t=this.config.maxReconnectAttempts??2;if(t>0&&this.attempts>=t){this.givenUp=!0;return}const e=this.config.retry.baseMs,s=this.config.retry.maxMs??1e4,n=this.config.retry.multiplier??1.6,a=this.config.retry.jitter??.1,r=Math.min(e*Math.pow(n,this.attempts),s)*(1+(Math.random()-.5)*a*2);this.reconnectTimer=setTimeout(()=>{this.attempts++,this.stats.reconnects=this.attempts,this.connect()},r)}extractSubId(t){let e=0;const s=t.length;for(;e<s&&t.charCodeAt(e)<=32;)e++;if(e>=s||t[e]!=="[")return null;for(e++;e<s&&t.charCodeAt(e)<=32;)e++;if(e>=s)return null;if(t[e]==='"'){for(e++;e<s&&t[e]!=='"';)e++;if(e>=s)return null;e++}else for(;e<s&&t[e]!==","&&t[e]!=="]";)e++;for(;e<s&&t[e]!==",";)e++;if(e>=s||t[e]!==",")return null;for(e++;e<s&&t.charCodeAt(e)<=32;)e++;if(e>=s)return null;if(t[e]==='"'){e++;const n=e;for(;e<s&&t[e]!=='"';)e++;return e>s?null:t.slice(n,e)}return null}}class g{constructor(t){this.connections=new Map,this.disabledRelays=new Set,this.nextAllowed=new Map,this.subCounts=new Map,this.cooldownMs=6e4,this.config={maxReconnectAttempts:2,...t}}now(){return Date.now()}detectKind(t){const e=t.match(/^\s*\[\s*"([^"]+)"/);if(!e)return"OTHER";const s=e[1].toUpperCase();return s==="REQ"?"REQ":s==="CLOSE"?"CLOSE":"OTHER"}getCount(t){return this.subCounts.get(t)??0}setCount(t,e){this.subCounts.set(t,Math.max(0,e))}bumpCount(t,e){const s=Math.max(0,this.getCount(t)+e);return this.subCounts.set(t,s),s}giveUpOrCooldown(t,e){e?.hasGivenUp()?(this.disabledRelays.has(t)||console.warn(`[registry] disabling relay ${t}: max attempts reached`),this.disabledRelays.add(t),this.nextAllowed.set(t,this.now()+this.cooldownMs)):this.nextAllowed.set(t,this.now()+Math.min(this.cooldownMs,1e4))}isCoolingDown(t){const e=this.nextAllowed.get(t)??0;return this.now()<e}async ensureConnection(t){if(this.disabledRelays.has(t))throw new Error(`Relay disabled: ${t}`);if(this.isCoolingDown(t))throw new Error(`Relay ${t} cooling down until ${new Date(this.nextAllowed.get(t)).toISOString()}`);let e=this.connections.get(t);if(e?e.getStatus()!==o.Ready&&e.connect():(e=new w(t,this.config),this.connections.set(t,e),e.connect()),e.getStatus()!==o.Ready)try{await e.waitForReady(this.config.connectTimeoutMs??5e3)}catch(s){throw this.giveUpOrCooldown(t,e),s}if(e.getStatus()!==o.Ready)throw this.giveUpOrCooldown(t,e),new Error(`Relay ${t} not ready`);return e}async sendFrame(t,e){if(this.disabledRelays.has(t)||this.isCoolingDown(t))return;const s=this.detectKind(e),n=await this.ensureConnection(t);try{await n.sendMessage(e)}catch(a){throw this.giveUpOrCooldown(t,n),await this.disconnect(t),a}s==="REQ"?this.bumpCount(t,1):s==="CLOSE"&&this.bumpCount(t,-1)===0&&(console.log("disconnecting",t),await this.disconnect(t))}async sendAllFramesToRelay(t,e){for(const s of e)await this.sendFrame(t,s)}async sendToRelays(t,e){const s=[];for(const n of t)this.disabledRelays.has(n)||this.isCoolingDown(n)||s.push(this.sendAllFramesToRelay(n,e).catch(a=>{console.error(`[registry] failed to send to ${n}:`,a)}));await Promise.allSettled(s)}async disconnect(t){const e=this.connections.get(t);e&&(await e.close(),this.connections.delete(t)),this.subCounts.delete(t)}async disconnectAll(){for(const[t]of this.connections)await this.disconnect(t)}enableRelay(t){this.disabledRelays.delete(t),this.nextAllowed.delete(t)}isRelayDisabled(t){return this.disabledRelays.has(t)}getActiveReqCount(t){return this.getCount(t)}getConnectionStatus(t){const e=this.connections.get(t);return e?e.getStatus():void 0}getAllStatuses(){return new Map(Array.from(this.connections.entries()).map(([t,e])=>[t,e.getStatus()]))}}export{y as B,g as C};
1
+ class p{constructor(t){this.dataStart=32,this.dropped=0,this.sab=t,this.dataView=new DataView(t),this.capacity=this.dataView.getUint32(0,!0)}getHead(){return this.dataView.getUint32(4,!0)%this.capacity}setHead(t){this.dataView.setUint32(4,t%this.capacity,!0)}getTail(){return this.dataView.getUint32(8,!0)%this.capacity}setTail(t){this.dataView.setUint32(8,t%this.capacity,!0)}getSeq(){return this.dataView.getUint32(12,!0)}setSeq(t){this.dataView.setUint32(12,t,!0)}getFreeSpace(){const t=this.getHead(),e=this.getTail(),s=(t-e+this.capacity)%this.capacity;return this.capacity-s}getDropped(){return this.dropped}hasRecords(){return this.getHead()!==this.getTail()}readNext(){const t=this.read();return t?{payload:t}:null}write(t){const e=t.byteLength,s=8+e,a=4+s+4;let n=0;for(;this.getFreeSpace()<a;){if(!this.skipRecord())return this.dropped+=n+1,-1;n++}const o=this.getSeq()+1;this.setSeq(o);let i=this.getHead();return this.dataView.setUint32(this.dataStart+i,s,!0),i=(i+4)%this.capacity,this.dataView.setUint16(this.dataStart+i,0,!0),i=(i+2)%this.capacity,this.dataView.setUint16(this.dataStart+i,0,!0),i=(i+2)%this.capacity,this.dataView.setUint32(this.dataStart+i,o,!0),i=(i+4)%this.capacity,this.copyBytes(i,t,0,e),i=(i+e)%this.capacity,this.dataView.setUint32(this.dataStart+i,s,!0),i=(i+4)%this.capacity,this.setHead(i),this.dropped+=n,o}read(){let t=this.getTail();if(t===this.getHead())return null;const e=this.dataView.getUint32(this.dataStart+t,!0);if(e===0)return null;const s=(t+4+e)%this.capacity;if(this.dataView.getUint32(this.dataStart+s,!0)!==e)return null;const a=new Uint8Array(e);this.copyFromRing((t+4)%this.capacity,a,0,e);const n=a.subarray(8),o=4+e+4;return this.setTail((this.getTail()+o)%this.capacity),n}skipRecord(){let t=this.getTail();if(t===this.getHead())return!1;const e=this.dataView.getUint32(this.dataStart+t,!0);if(e===0)return!1;const s=(t+4+e)%this.capacity;if(this.dataView.getUint32(this.dataStart+s,!0)!==e)return!1;const a=4+e+4;return this.setTail((this.getTail()+a)%this.capacity),!0}copyBytes(t,e,s,a){let n=a,o=s,i=t;for(;n>0;){const l=this.capacity-i%this.capacity,r=Math.min(n,l),d=this.dataStart+i%this.capacity,u=e.subarray(o,o+r);new Uint8Array(this.sab,d,r).set(u),n-=r,o+=r,i+=r}}copyFromRing(t,e,s,a){let n=a,o=s,i=t;for(;n>0;){const l=this.capacity-i%this.capacity,r=Math.min(n,l),d=this.dataStart+i%this.capacity,u=new Uint8Array(this.sab,d,r);e.set(u,o),n-=r,o+=r,i+=r}}}var c=(h=>(h.Idle="idle",h.Connecting="connecting",h.Ready="ready",h.Closing="closing",h.Closed="closed",h.Error="error",h))(c||{});class m{constructor(t,e={}){this.wantReconnect=!0,this.status=c.Closed,this.ws=null,this.reconnectTimer=null,this.abortController=null,this.attempts=0,this.givenUp=!1,this.lastActivity=Date.now(),this.stats={dropped:0,sent:0,received:0,reconnects:0,lastActivity:Date.now()},this.readyWaiters=[],this.messageHandler=null,this.url=t,this.config={connectTimeoutMs:5e3,writeTimeoutMs:1e4,retry:{baseMs:300,maxMs:1e4,multiplier:1.6,jitter:.1},maxReconnectAttempts:2,idleTimeoutMs:3e5,...e}}getUrl(){return this.url}getStatus(){return this.status}getStats(){return{...this.stats,reconnects:this.attempts,lastActivity:this.lastActivity}}getLastActivity(){return this.lastActivity}hasGivenUp(){return this.givenUp}setMessageHandler(t){this.messageHandler=t}connect(){if(this.givenUp||this.status===c.Connecting||this.status===c.Ready)return;this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.status=c.Connecting,this.abortController=new AbortController;const t=this.abortController.signal;try{this.ws=new WebSocket(this.url),this.ws.binaryType="arraybuffer";const e=()=>{this.ws&&(this.ws.onopen=null,this.ws.onclose=null,this.ws.onerror=null,this.ws.onmessage=null)},s=()=>{clearTimeout(i),this.status=c.Ready,this.attempts=0,this.givenUp=!1,this.lastActivity=Date.now(),this.stats.lastActivity=this.lastActivity,this.resolveReady(!0)},a=r=>{clearTimeout(i),e(),this.status=c.Closed,this.resolveReady(!1),r.code!==1e3&&this.scheduleReconnect()},n=r=>{clearTimeout(i),e(),this.status=c.Closed,this.resolveReady(!1),this.scheduleReconnect()},o=r=>{if(this.lastActivity=Date.now(),this.stats.lastActivity=this.lastActivity,this.stats.received++,typeof r.data=="string"){const d=r.data,u=this.extractSubId(d);this.messageHandler&&this.messageHandler(this.url,u,d)}};this.ws.onopen=s,this.ws.onclose=a,this.ws.onerror=n,this.ws.onmessage=o;const i=setTimeout(()=>{if(this.status===c.Connecting){e();try{this.ws?.close()}catch{}this.status=c.Closed,this.resolveReady(!1),this.scheduleReconnect()}},this.config.connectTimeoutMs),l=()=>{clearTimeout(i),e();try{this.ws?.close()}catch{}this.status=c.Closed,this.resolveReady(!1),this.wantReconnect&&this.scheduleReconnect()};t.addEventListener("abort",l)}catch{this.status=c.Closed,this.resolveReady(!1),this.scheduleReconnect()}}async sendMessage(t){if(this.status!==c.Ready||!this.ws)throw new Error("Connection not ready");this.ws.send(t),this.stats.sent++,this.lastActivity=Date.now(),this.stats.lastActivity=this.lastActivity}async waitForReady(t=this.config.connectTimeoutMs??5e3){if(this.status!==c.Ready)return new Promise((e,s)=>{const a=setTimeout(()=>{this.removeReadyResolver(n),s(new Error("Timeout waiting for ready"))},t),n=o=>{clearTimeout(a),o?e():s(new Error("Connection closed"))};this.readyWaiters.push(n)})}async close(){this.wantReconnect=!1,this.abortController&&this.abortController.abort(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.closeWebSocket(),this.status=c.Closed,this.resolveReady(!1)}shouldCloseDueToInactivity(){return Date.now()-this.lastActivity>(this.config.idleTimeoutMs??3e5)}resolveReady(t){if(this.readyWaiters.length===0)return;const e=this.readyWaiters.slice();this.readyWaiters.length=0;for(const s of e)try{s(t)}catch{}}removeReadyResolver(t){const e=this.readyWaiters.indexOf(t);e>=0&&this.readyWaiters.splice(e,1)}closeWebSocket(){if(this.ws){try{this.ws.close(1e3,"Normal closure")}catch{}this.ws=null}}scheduleReconnect(){if(!this.wantReconnect||this.status!==c.Closed||!this.config.retry?.baseMs)return;const t=this.config.maxReconnectAttempts??2;if(t>0&&this.attempts>=t){this.givenUp=!0;return}const e=this.config.retry.baseMs,s=this.config.retry.maxMs??1e4,a=this.config.retry.multiplier??1.6,n=this.config.retry.jitter??.1,o=Math.min(e*Math.pow(a,this.attempts),s)*(1+(Math.random()-.5)*n*2);this.reconnectTimer=setTimeout(()=>{this.attempts++,this.stats.reconnects=this.attempts,this.connect()},o)}extractSubId(t){let e=0;const s=t.length;for(;e<s&&t.charCodeAt(e)<=32;)e++;if(e>=s||t[e]!=="[")return null;for(e++;e<s&&t.charCodeAt(e)<=32;)e++;if(e>=s)return null;if(t[e]==='"'){for(e++;e<s&&t[e]!=='"';)e++;if(e>=s)return null;e++}else for(;e<s&&t[e]!==","&&t[e]!=="]";)e++;for(;e<s&&t[e]!==",";)e++;if(e>=s||t[e]!==",")return null;for(e++;e<s&&t.charCodeAt(e)<=32;)e++;if(e>=s)return null;if(t[e]==='"'){e++;const a=e;for(;e<s&&t[e]!=='"';)e++;return e>s?null:t.slice(a,e)}return null}}class f{constructor(t){this.connections=new Map,this.disabledRelays=new Set,this.nextAllowed=new Map,this.subCounts=new Map,this.cooldownMs=6e4,this.config={maxReconnectAttempts:2,...t}}now(){return Date.now()}detectKind(t){const e=t.match(/^\s*\[\s*"([^"]+)"/);if(!e)return"OTHER";const s=e[1].toUpperCase();return s==="REQ"?"REQ":s==="CLOSE"?"CLOSE":"OTHER"}getCount(t){return this.subCounts.get(t)??0}setCount(t,e){this.subCounts.set(t,Math.max(0,e))}bumpCount(t,e){const s=Math.max(0,this.getCount(t)+e);return this.subCounts.set(t,s),s}giveUpOrCooldown(t,e){e?.hasGivenUp()?(this.disabledRelays.has(t)||console.warn(`[registry] disabling relay ${t}: max attempts reached`),this.disabledRelays.add(t),this.nextAllowed.set(t,this.now()+this.cooldownMs)):this.nextAllowed.set(t,this.now()+Math.min(this.cooldownMs,1e4))}isCoolingDown(t){const e=this.nextAllowed.get(t)??0;return this.now()<e}async ensureConnection(t){if(this.disabledRelays.has(t))throw new Error(`Relay disabled: ${t}`);if(this.isCoolingDown(t))throw new Error(`Relay ${t} cooling down until ${new Date(this.nextAllowed.get(t)).toISOString()}`);let e=this.connections.get(t);if(e?e.getStatus()!==c.Ready&&e.connect():(e=new m(t,this.config),this.connections.set(t,e),e.connect()),e.getStatus()!==c.Ready)try{await e.waitForReady(this.config.connectTimeoutMs??5e3)}catch(s){throw this.giveUpOrCooldown(t,e),s}if(e.getStatus()!==c.Ready)throw this.giveUpOrCooldown(t,e),new Error(`Relay ${t} not ready`);return e}async sendFrame(t,e){if(this.disabledRelays.has(t)||this.isCoolingDown(t))return;const s=this.detectKind(e),a=await this.ensureConnection(t);try{await a.sendMessage(e)}catch(n){throw this.giveUpOrCooldown(t,a),await this.disconnect(t),n}s==="REQ"?this.bumpCount(t,1):s==="CLOSE"&&this.bumpCount(t,-1)===0&&(console.log("disconnecting",t),await this.disconnect(t))}async sendAllFramesToRelay(t,e){for(const s of e)await this.sendFrame(t,s)}async sendToRelays(t,e,s=5,a=5){const n=t.filter(y=>!this.disabledRelays.has(y)&&!this.isCoolingDown(y)),o=Math.min(s,n.length);if(o===0)return;let i=0,l=0,r=0,d;const u=new Promise(y=>{d=y}),g=()=>l>=o||i>=n.length&&r===0?(d(),!0):!1,w=()=>{for(;r<a&&i<n.length&&l<o;){const y=n[i++];r++,this.sendAllFramesToRelay(y,e).then(()=>{l++}).catch(()=>{}).finally(()=>{r--,g()||w()})}};w(),await u}async disconnect(t){const e=this.connections.get(t);e&&(await e.close(),this.connections.delete(t)),this.subCounts.delete(t)}async disconnectAll(){for(const[t]of this.connections)await this.disconnect(t)}enableRelay(t){this.disabledRelays.delete(t),this.nextAllowed.delete(t)}isRelayDisabled(t){return this.disabledRelays.has(t)}getActiveReqCount(t){return this.getCount(t)}getConnectionStatus(t){const e=this.connections.get(t);return e?e.getStatus():void 0}getAllStatuses(){return new Map(Array.from(this.connections.entries()).map(([t,e])=>[t,e.getStatus()]))}}export{p as B,f as C};
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sources":["../src/ws/ring-buffer.ts","../src/ws/types.ts","../src/ws/connection.ts","../src/ws/registry.ts"],"sourcesContent":["export class ByteRingBuffer {\n\tprivate readonly sab: SharedArrayBuffer;\n\tprivate readonly dataView: DataView;\n\tprivate readonly dataStart: number = 32;\n\tprivate readonly capacity: number;\n\tprivate dropped: number = 0;\n\n\tconstructor(buffer: SharedArrayBuffer) {\n\t\tthis.sab = buffer;\n\t\tthis.dataView = new DataView(buffer);\n\t\tthis.capacity = this.dataView.getUint32(0, true); // little-endian\n\n\t\t// Read initial state (assume Rust sets capacity, head=0, tail=0, seq=0)\n\t\t// We don't reset here; assume initialized\n\t}\n\n\tprivate getHead(): number {\n\t\treturn this.dataView.getUint32(4, true) % this.capacity;\n\t}\n\n\tprivate setHead(value: number): void {\n\t\tthis.dataView.setUint32(4, value % this.capacity, true);\n\t}\n\n\tprivate getTail(): number {\n\t\treturn this.dataView.getUint32(8, true) % this.capacity;\n\t}\n\n\tprivate setTail(value: number): void {\n\t\tthis.dataView.setUint32(8, value % this.capacity, true);\n\t}\n\n\tprivate getSeq(): number {\n\t\treturn this.dataView.getUint32(12, true);\n\t}\n\n\tprivate setSeq(value: number): void {\n\t\tthis.dataView.setUint32(12, value, true);\n\t}\n\n\tgetFreeSpace(): number {\n\t\tconst head = this.getHead();\n\t\tconst tail = this.getTail();\n\t\tconst used = (head - tail + this.capacity) % this.capacity;\n\t\treturn this.capacity - used;\n\t}\n\n\tgetDropped(): number {\n\t\treturn this.dropped;\n\t}\n\n\thasRecords(): boolean {\n\t\treturn this.getHead() !== this.getTail();\n\t}\n\n\treadNext(): { payload: Uint8Array } | null {\n\t\tconst p = this.read();\n\t\treturn p ? { payload: p } : null;\n\t}\n\n\t/**\n\t * Writes a payload to the ring buffer, overwriting old records if necessary.\n\t * Returns the sequence number if successful, -1 if dropped (couldn't make space).\n\t */\n\twrite(payload: Uint8Array): number {\n\t\tconst N = payload.byteLength;\n\t\tconst len = 8 + N; // type(2) + pad(2) + seq(4) + payload(N)\n\t\tconst totalSize = 4 + len + 4; // len + variable + trailer\n\n\t\t// Make space by skipping records\n\t\tlet droppedThisWrite = 0;\n\t\twhile (this.getFreeSpace() < totalSize) {\n\t\t\tif (!this.skipRecord()) {\n\t\t\t\t// Can't skip more (uncommitted record), drop this write\n\t\t\t\tthis.dropped += droppedThisWrite + 1;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tdroppedThisWrite++;\n\t\t}\n\n\t\t// Enough space, write\n\t\tconst mySeq = this.getSeq() + 1;\n\t\tthis.setSeq(mySeq);\n\n\t\tlet writePos = this.getHead();\n\n\t\t// Write len\n\t\tthis.dataView.setUint32(this.dataStart + writePos, len, true);\n\t\twritePos = (writePos + 4) % this.capacity;\n\n\t\t// Write type (0)\n\t\tthis.dataView.setUint16(this.dataStart + writePos, 0, true);\n\t\twritePos = (writePos + 2) % this.capacity;\n\n\t\t// Write pad (0)\n\t\tthis.dataView.setUint16(this.dataStart + writePos, 0, true);\n\t\twritePos = (writePos + 2) % this.capacity;\n\n\t\t// Write seq\n\t\tthis.dataView.setUint32(this.dataStart + writePos, mySeq, true);\n\t\twritePos = (writePos + 4) % this.capacity;\n\n\t\t// Write payload (possibly wrapped)\n\t\tthis.copyBytes(writePos, payload, 0, N);\n\t\twritePos = (writePos + N) % this.capacity;\n\n\t\t// Write trailer (len)\n\t\tthis.dataView.setUint32(this.dataStart + writePos, len, true);\n\t\twritePos = (writePos + 4) % this.capacity;\n\n\t\t// Advance head\n\t\tthis.setHead(writePos);\n\n\t\tthis.dropped += droppedThisWrite;\n\t\treturn mySeq;\n\t}\n\n\t/**\n\t * Reads the next committed payload or null if none ready.\n\t * Advances tail on success.\n\t */\n\tread(): Uint8Array | null {\n\t\tlet readPos = this.getTail();\n\t\tif (readPos === this.getHead()) return null; // empty\n\n\t\tconst len = this.dataView.getUint32(this.dataStart + readPos, true);\n\t\tif (len === 0) return null;\n\n\t\tconst trailerPos = (readPos + 4 + len) % this.capacity;\n\t\tconst trailer = this.dataView.getUint32(this.dataStart + trailerPos, true);\n\n\t\tif (trailer !== len) return null; // not committed\n\n\t\t// Read variable part (len bytes from readPos + 4)\n\t\tconst variable = new Uint8Array(len);\n\t\tthis.copyFromRing((readPos + 4) % this.capacity, variable, 0, len);\n\n\t\t// Extract payload (skip type+pad+seq = 8 bytes)\n\t\tconst payload = variable.subarray(8);\n\n\t\t// Advance tail\n\t\tconst advance = 4 + len + 4; // len field + variable + trailer\n\t\tthis.setTail((this.getTail() + advance) % this.capacity);\n\n\t\treturn payload;\n\t}\n\n\tprivate skipRecord(): boolean {\n\t\tlet readPos = this.getTail();\n\t\tif (readPos === this.getHead()) return false; // empty\n\n\t\tconst len = this.dataView.getUint32(this.dataStart + readPos, true);\n\t\tif (len === 0) return false;\n\n\t\tconst trailerPos = (readPos + 4 + len) % this.capacity;\n\t\tconst trailer = this.dataView.getUint32(this.dataStart + trailerPos, true);\n\n\t\tif (trailer !== len) return false; // not committed\n\n\t\t// Skip by advancing tail\n\t\tconst advance = 4 + len + 4;\n\t\tthis.setTail((this.getTail() + advance) % this.capacity);\n\t\treturn true;\n\t}\n\n\tprivate copyBytes(\n\t\ttargetPos: number,\n\t\tsource: Uint8Array,\n\t\tsourceOffset: number,\n\t\tlength: number\n\t): void {\n\t\tlet remaining = length;\n\t\tlet srcOffset = sourceOffset;\n\t\tlet tgt = targetPos;\n\n\t\twhile (remaining > 0) {\n\t\t\tconst spaceToEnd = this.capacity - (tgt % this.capacity);\n\t\t\tconst chunkSize = Math.min(remaining, spaceToEnd);\n\t\t\tconst tgtAbs = this.dataStart + (tgt % this.capacity);\n\t\t\tconst srcChunk = source.subarray(srcOffset, srcOffset + chunkSize);\n\t\t\tnew Uint8Array(this.sab, tgtAbs, chunkSize).set(srcChunk);\n\t\t\tremaining -= chunkSize;\n\t\t\tsrcOffset += chunkSize;\n\t\t\ttgt += chunkSize;\n\t\t}\n\t}\n\n\tprivate copyFromRing(\n\t\tsourcePos: number,\n\t\ttarget: Uint8Array,\n\t\ttargetOffset: number,\n\t\tlength: number\n\t): void {\n\t\tlet remaining = length;\n\t\tlet tgtOffset = targetOffset;\n\t\tlet src = sourcePos;\n\n\t\twhile (remaining > 0) {\n\t\t\tconst spaceToEnd = this.capacity - (src % this.capacity);\n\t\t\tconst chunkSize = Math.min(remaining, spaceToEnd);\n\t\t\tconst srcAbs = this.dataStart + (src % this.capacity);\n\t\t\tconst srcChunk = new Uint8Array(this.sab, srcAbs, chunkSize);\n\t\t\ttarget.set(srcChunk, tgtOffset);\n\t\t\tremaining -= chunkSize;\n\t\t\ttgtOffset += chunkSize;\n\t\t\tsrc += chunkSize;\n\t\t}\n\t}\n}\n","export enum ConnectionStatus {\n Idle = 'idle',\n Connecting = 'connecting',\n Ready = 'ready',\n Closing = 'closing',\n Closed = 'closed',\n Error = 'error',\n}\n\nexport enum MsgKind {\n Unknown = 0,\n Event = 1,\n Eose = 2,\n Ok = 3,\n Closed = 4,\n Notice = 5,\n Auth = 6,\n}\n\n // MsgKind is defined in FlatBuffers schema; import from generated code\n // export type { MsgKind } from '../fb/worker_messages_generated';\n\nexport interface RelayConfig {\n connectTimeoutMs?: number;\n writeTimeoutMs?: number;\n retryBaseMs?: number;\n retryMaxMs?: number;\n retryMultiplier?: number;\n retryJitter?: number;\n retry?: {\n baseMs: number;\n maxMs: number;\n multiplier: number;\n jitter: number;\n };\n idleTimeoutMs?: number;\n}\n\nexport interface RelayStats {\n sent: number;\n received: number;\n reconnects: number;\n lastActivity: number; // timestamp\n uptime?: number;\n dropped: number; // for ring buffer overwrites\n}\n\nexport interface InboundEnvelope {\n relays: string[];\n frames: string[];\n}\n\nexport interface WorkerLine {\n relay: {\n url: string;\n };\n kind: number; // MsgKind from generated\n sub_id?: string;\n raw: Uint8Array; // UTF-8 bytes\n}\n\nexport type FrameCallback = (frame: string) => void;\nexport type MessageHandler = (url: string, kind: MsgKind, subId: string | null, rawText: string) => void;\n","import { ConnectionStatus, RelayConfig, RelayStats } from './types';\n\n// Callback invoked for every incoming websocket text frame\nexport type MessageHandler = (\n\turl: string,\n\tsubId: string | null, // present for EVENT/EOSE/CLOSED\n\trawText: string\n) => void;\n\nexport class RelayConnection {\n\tprivate wantReconnect = true;\n\tprivate url: string;\n\tprivate config: RelayConfig;\n\tprivate status: ConnectionStatus = ConnectionStatus.Closed;\n\tprivate ws: WebSocket | null = null;\n\tprivate reconnectTimer: number | null = null;\n\tprivate abortController: AbortController | null = null;\n\n\tprivate attempts: number = 0; // reconnection attempts after first failure\n\tprivate givenUp: boolean = false; // set when attempts reached cap\n\tprivate lastActivity: number = Date.now();\n\tprivate stats: RelayStats = {\n\t\tdropped: 0,\n\t\tsent: 0,\n\t\treceived: 0,\n\t\treconnects: 0, // mirrors attempts for visibility\n\t\tlastActivity: Date.now()\n\t};\n\tprivate readyWaiters: Array<(ok: boolean) => void> = [];\n\tpublic messageHandler: MessageHandler | null = null;\n\n\tconstructor(url: string, config: Partial<RelayConfig> = {}) {\n\t\tthis.url = url;\n\t\tthis.config = {\n\t\t\tconnectTimeoutMs: 5_000,\n\t\t\twriteTimeoutMs: 10_000,\n\t\t\tretry: {\n\t\t\t\tbaseMs: 300,\n\t\t\t\tmaxMs: 10_000,\n\t\t\t\tmultiplier: 1.6,\n\t\t\t\tjitter: 0.1\n\t\t\t},\n\t\t\tmaxReconnectAttempts: 2, // default: 2 retries\n\t\t\tidleTimeoutMs: 300_000,\n\t\t\t...config\n\t\t};\n\t}\n\n\tgetUrl(): string {\n\t\treturn this.url;\n\t}\n\tgetStatus(): ConnectionStatus {\n\t\treturn this.status;\n\t}\n\tgetStats(): RelayStats {\n\t\treturn { ...this.stats, reconnects: this.attempts, lastActivity: this.lastActivity };\n\t}\n\tgetLastActivity(): number {\n\t\treturn this.lastActivity;\n\t}\n\thasGivenUp(): boolean {\n\t\treturn this.givenUp;\n\t}\n\n\tsetMessageHandler(handler: MessageHandler): void {\n\t\tthis.messageHandler = handler;\n\t}\n\n\t// Fire-and-forget connect. It never throws. Use waitForReady() to await.\n\tconnect(): void {\n\t\tif (this.givenUp) return;\n\t\tif (this.status === ConnectionStatus.Connecting || this.status === ConnectionStatus.Ready)\n\t\t\treturn;\n\n\t\t// Clear pending reconnect\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\n\t\tthis.status = ConnectionStatus.Connecting;\n\t\tthis.abortController = new AbortController();\n\t\tconst signal = this.abortController.signal;\n\n\t\ttry {\n\t\t\tthis.ws = new WebSocket(this.url);\n\t\t\tthis.ws.binaryType = 'arraybuffer';\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tif (!this.ws) return;\n\t\t\t\tthis.ws.onopen = null as any;\n\t\t\t\tthis.ws.onclose = null as any;\n\t\t\t\tthis.ws.onerror = null as any;\n\t\t\t\tthis.ws.onmessage = null as any;\n\t\t\t};\n\n\t\t\tconst onOpen = () => {\n\t\t\t\t// Clear connect timeout\n\t\t\t\tclearTimeout(to);\n\t\t\t\t// Success: reset attempts and flags\n\t\t\t\tthis.status = ConnectionStatus.Ready;\n\t\t\t\tthis.attempts = 0;\n\t\t\t\tthis.givenUp = false;\n\t\t\t\tthis.lastActivity = Date.now();\n\t\t\t\tthis.stats.lastActivity = this.lastActivity;\n\t\t\t\tthis.resolveReady(true);\n\t\t\t};\n\n\t\t\tconst onClose = (ev: CloseEvent) => {\n\t\t\t\t// Clear connect timeout\n\t\t\t\tclearTimeout(to);\n\t\t\t\tcleanup();\n\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\tthis.resolveReady(false);\n\t\t\t\tif (ev.code !== 1000) {\n\t\t\t\t\tthis.scheduleReconnect();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst onError = (_ev: Event) => {\n\t\t\t\t// Clear connect timeout\n\t\t\t\tclearTimeout(to);\n\t\t\t\t// Treat like close; don’t throw\n\t\t\t\tcleanup();\n\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\tthis.resolveReady(false);\n\t\t\t\tthis.scheduleReconnect();\n\t\t\t};\n\n\t\t\tconst onMessage = (event: MessageEvent) => {\n\t\t\t\tthis.lastActivity = Date.now();\n\t\t\t\tthis.stats.lastActivity = this.lastActivity;\n\t\t\t\tthis.stats.received++;\n\n\t\t\t\tif (typeof event.data === 'string') {\n\t\t\t\t\tconst rawText = event.data;\n\t\t\t\t\tconst subId = this.extractSubId(rawText);\n\t\t\t\t\tif (this.messageHandler) {\n\t\t\t\t\t\tthis.messageHandler(this.url, subId, rawText);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Attach\n\t\t\tthis.ws.onopen = onOpen;\n\t\t\tthis.ws.onclose = onClose;\n\t\t\tthis.ws.onerror = onError;\n\t\t\tthis.ws.onmessage = onMessage;\n\n\t\t\t// Timeout and abort\n\t\t\tconst to = setTimeout(() => {\n\t\t\t\tif (this.status === ConnectionStatus.Connecting) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.ws?.close();\n\t\t\t\t\t} catch {}\n\t\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\t\tthis.resolveReady(false);\n\t\t\t\t\tthis.scheduleReconnect();\n\t\t\t\t}\n\t\t\t}, this.config.connectTimeoutMs);\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tclearTimeout(to);\n\t\t\t\tcleanup();\n\t\t\t\ttry {\n\t\t\t\t\tthis.ws?.close();\n\t\t\t\t} catch {}\n\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\tthis.resolveReady(false);\n\t\t\t\tif (this.wantReconnect) this.scheduleReconnect(); // guard here\n\t\t\t};\n\n\t\t\tsignal.addEventListener('abort', onAbort);\n\t\t} catch {\n\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\tthis.resolveReady(false);\n\t\t\tthis.scheduleReconnect();\n\t\t}\n\t}\n\n\tasync sendMessage(frame: string): Promise<void> {\n\t\tif (this.status !== ConnectionStatus.Ready || !this.ws) {\n\t\t\tthrow new Error('Connection not ready');\n\t\t}\n\t\tthis.ws.send(frame);\n\t\tthis.stats.sent++;\n\t\tthis.lastActivity = Date.now();\n\t\tthis.stats.lastActivity = this.lastActivity;\n\t}\n\n\tasync waitForReady(timeoutMs: number = this.config.connectTimeoutMs ?? 5_000): Promise<void> {\n\t\tif (this.status === ConnectionStatus.Ready) return;\n\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.removeReadyResolver(resolver);\n\t\t\t\treject(new Error('Timeout waiting for ready'));\n\t\t\t}, timeoutMs);\n\n\t\t\tconst resolver = (ok: boolean) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tif (ok) resolve();\n\t\t\t\telse reject(new Error('Connection closed'));\n\t\t\t};\n\t\t\tthis.readyWaiters.push(resolver);\n\t\t});\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.wantReconnect = false;\n\t\tif (this.abortController) this.abortController.abort();\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\t\tthis.closeWebSocket();\n\t\tthis.status = ConnectionStatus.Closed;\n\t\tthis.resolveReady(false);\n\t}\n\n\tshouldCloseDueToInactivity(): boolean {\n\t\treturn Date.now() - this.lastActivity > (this.config.idleTimeoutMs ?? 300_000);\n\t}\n\n\t// Internals\n\n\tprivate resolveReady(ok: boolean) {\n\t\tif (this.readyWaiters.length === 0) return;\n\t\tconst waiters = this.readyWaiters.slice();\n\t\tthis.readyWaiters.length = 0;\n\t\tfor (const fn of waiters) {\n\t\t\ttry {\n\t\t\t\tfn(ok);\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\tprivate removeReadyResolver(fn: (ok: boolean) => void) {\n\t\tconst idx = this.readyWaiters.indexOf(fn);\n\t\tif (idx >= 0) this.readyWaiters.splice(idx, 1);\n\t}\n\n\tprivate closeWebSocket() {\n\t\tif (this.ws) {\n\t\t\ttry {\n\t\t\t\tthis.ws.close(1000, 'Normal closure');\n\t\t\t} catch {}\n\t\t\tthis.ws = null;\n\t\t}\n\t}\n\n\tprivate scheduleReconnect(): void {\n\t\tif (!this.wantReconnect) return;\n\t\tif (this.status !== ConnectionStatus.Closed || !this.config.retry?.baseMs) return;\n\n\t\tconst cap = this.config.maxReconnectAttempts ?? 2;\n\t\tif (cap > 0 && this.attempts >= cap) {\n\t\t\tthis.givenUp = true;\n\t\t\treturn;\n\t\t}\n\n\t\tconst base = this.config.retry.baseMs;\n\t\tconst max = this.config.retry.maxMs ?? 10_000;\n\t\tconst mult = this.config.retry.multiplier ?? 1.6;\n\t\tconst jitter = this.config.retry.jitter ?? 0.1;\n\n\t\t// Calculate delay using current attempts\n\t\tconst delay =\n\t\t\tMath.min(base * Math.pow(mult, this.attempts), max) *\n\t\t\t(1 + (Math.random() - 0.5) * jitter * 2);\n\n\t\tthis.reconnectTimer = setTimeout(() => {\n\t\t\t// Increment attempts when actually trying again\n\t\t\tthis.attempts++;\n\t\t\tthis.stats.reconnects = this.attempts;\n\t\t\tthis.connect();\n\t\t}, delay) as unknown as number;\n\t}\n\n\tprivate extractSubId(s: string): string | null {\n\t\tlet i = 0;\n\t\tconst n = s.length;\n\n\t\t// Skip leading whitespace\n\t\twhile (i < n && s.charCodeAt(i) <= 32) i++;\n\t\tif (i >= n || s[i] !== '[') return null;\n\t\ti++;\n\n\t\t// Skip whitespace before first element\n\t\twhile (i < n && s.charCodeAt(i) <= 32) i++;\n\t\tif (i >= n) return null;\n\n\t\t// Skip first element (kind or similar)\n\t\tif (s[i] === '\"') {\n\t\t\t// Fast-skip a quoted string (no escape handling, consistent with existing code)\n\t\t\ti++;\n\t\t\twhile (i < n && s[i] !== '\"') i++;\n\t\t\tif (i >= n) return null;\n\t\t\ti++; // past closing quote\n\t\t} else {\n\t\t\t// Non-quoted first element; skip until comma or end of array\n\t\t\twhile (i < n && s[i] !== ',' && s[i] !== ']') i++;\n\t\t}\n\n\t\t// Move to the comma after first element\n\t\twhile (i < n && s[i] !== ',') i++;\n\t\tif (i >= n || s[i] !== ',') return null;\n\t\ti++; // skip comma\n\n\t\t// Skip whitespace before second element\n\t\twhile (i < n && s.charCodeAt(i) <= 32) i++;\n\t\tif (i >= n) return null;\n\n\t\t// Extract second element only if it's a quoted string\n\t\tif (s[i] === '\"') {\n\t\t\ti++;\n\t\t\tconst start = i;\n\t\t\twhile (i < n && s[i] !== '\"') i++;\n\t\t\tif (i > n) return null;\n\t\t\treturn s.slice(start, i);\n\t\t}\n\t\treturn null;\n\t}\n}\n","import { RelayConfig, ConnectionStatus } from './types';\nimport { RelayConnection } from './connection';\n\nexport class ConnectionRegistry {\n\tprivate connections = new Map<string, RelayConnection>();\n\tprivate disabledRelays = new Set<string>();\n\tprivate nextAllowed = new Map<string, number>(); // cooldown timestamps per URL (ms)\n\tprivate subCounts = new Map<string, number>(); // active subscription count per URL\n\tprivate config: RelayConfig;\n\n\tprivate readonly cooldownMs = 60_000; // after failures\n\n\tconstructor(config: RelayConfig) {\n\t\tthis.config = { maxReconnectAttempts: 2, ...config };\n\t}\n\n\tprivate now(): number {\n\t\treturn Date.now();\n\t}\n\n\tprivate detectKind(frame: string): 'REQ' | 'CLOSE' | 'OTHER' {\n\t\tconst m = frame.match(/^\\s*\\[\\s*\"([^\"]+)\"/);\n\t\tif (!m) return 'OTHER';\n\t\tconst k = m[1].toUpperCase();\n\t\tif (k === 'REQ') return 'REQ';\n\t\tif (k === 'CLOSE') return 'CLOSE';\n\t\treturn 'OTHER';\n\t}\n\n\tprivate getCount(url: string): number {\n\t\treturn this.subCounts.get(url) ?? 0;\n\t}\n\tprivate setCount(url: string, value: number): void {\n\t\tthis.subCounts.set(url, Math.max(0, value));\n\t}\n\n\t// Replace bumpCount with a pure counter that returns the new value.\n\tprivate bumpCount(url: string, delta: number): number {\n\t\tconst next = Math.max(0, this.getCount(url) + delta);\n\t\tthis.subCounts.set(url, next);\n\t\treturn next;\n\t}\n\n\tprivate giveUpOrCooldown(url: string, conn?: RelayConnection) {\n\t\tif (conn?.hasGivenUp()) {\n\t\t\tif (!this.disabledRelays.has(url)) {\n\t\t\t\tconsole.warn(`[registry] disabling relay ${url}: max attempts reached`);\n\t\t\t}\n\t\t\tthis.disabledRelays.add(url);\n\t\t\tthis.nextAllowed.set(url, this.now() + this.cooldownMs);\n\t\t} else {\n\t\t\tthis.nextAllowed.set(url, this.now() + Math.min(this.cooldownMs, 10_000));\n\t\t}\n\t}\n\n\tprivate isCoolingDown(url: string): boolean {\n\t\tconst at = this.nextAllowed.get(url) ?? 0;\n\t\treturn this.now() < at;\n\t}\n\n\tasync ensureConnection(url: string): Promise<RelayConnection> {\n\t\tif (this.disabledRelays.has(url)) {\n\t\t\tthrow new Error(`Relay disabled: ${url}`);\n\t\t}\n\t\tif (this.isCoolingDown(url)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Relay ${url} cooling down until ${new Date(this.nextAllowed.get(url)!).toISOString()}`\n\t\t\t);\n\t\t}\n\n\t\tlet conn = this.connections.get(url);\n\t\tif (!conn) {\n\t\t\tconn = new RelayConnection(url, this.config);\n\t\t\tthis.connections.set(url, conn);\n\t\t\tconn.connect(); // fire-and-forget\n\t\t} else if (conn.getStatus() !== ConnectionStatus.Ready) {\n\t\t\t// nudge reconnect on existing connection\n\t\t\tconn.connect();\n\t\t}\n\n\t\tif (conn.getStatus() !== ConnectionStatus.Ready) {\n\t\t\ttry {\n\t\t\t\tawait conn.waitForReady(this.config.connectTimeoutMs ?? 5_000);\n\t\t\t} catch (e) {\n\t\t\t\tthis.giveUpOrCooldown(url, conn);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\n\t\tif (conn.getStatus() !== ConnectionStatus.Ready) {\n\t\t\tthis.giveUpOrCooldown(url, conn);\n\t\t\tthrow new Error(`Relay ${url} not ready`);\n\t\t}\n\n\t\treturn conn;\n\t}\n\n\t// Update sendFrame to disconnect immediately when count becomes 0 after a CLOSE.\n\tasync sendFrame(url: string, frame: string): Promise<void> {\n\t\tif (this.disabledRelays.has(url) || this.isCoolingDown(url)) return;\n\n\t\tconst kind = this.detectKind(frame);\n\t\tconst conn = await this.ensureConnection(url);\n\n\t\ttry {\n\t\t\tawait conn.sendMessage(frame);\n\t\t} catch (e) {\n\t\t\tthis.giveUpOrCooldown(url, conn);\n\t\t\tawait this.disconnect(url);\n\t\t\tthrow e;\n\t\t}\n\n\t\t// Track the sub count for visibility and lifecycle decisions\n\t\tif (kind === 'REQ') {\n\t\t\tthis.bumpCount(url, +1);\n\t\t} else if (kind === 'CLOSE') {\n\t\t\tconst newCount = this.bumpCount(url, -1);\n\t\t\tif (newCount === 0) {\n\t\t\t\tconsole.log('disconnecting', url);\n\t\t\t\t// Immediately disconnect when no more active REQ for this relay\n\t\t\t\tawait this.disconnect(url);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async sendAllFramesToRelay(url: string, frames: string[]): Promise<void> {\n\t\tfor (const frame of frames) {\n\t\t\tawait this.sendFrame(url, frame);\n\t\t}\n\t}\n\n\tasync sendToRelays(relays: string[], frames: string[]): Promise<void> {\n\t\tconst tasks: Promise<void>[] = [];\n\t\tfor (const url of relays) {\n\t\t\tif (this.disabledRelays.has(url) || this.isCoolingDown(url)) continue;\n\n\t\t\ttasks.push(\n\t\t\t\tthis.sendAllFramesToRelay(url, frames).catch((error) => {\n\t\t\t\t\tconsole.error(`[registry] failed to send to ${url}:`, error);\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\tawait Promise.allSettled(tasks);\n\t}\n\n\tasync disconnect(url: string): Promise<void> {\n\t\tconst connection = this.connections.get(url);\n\t\tif (connection) {\n\t\t\tawait connection.close();\n\t\t\tthis.connections.delete(url);\n\t\t}\n\t\t// No pending-disconnects to cancel anymore\n\t\tthis.subCounts.delete(url);\n\t}\n\n\tasync disconnectAll(): Promise<void> {\n\t\tfor (const [url] of this.connections) {\n\t\t\tawait this.disconnect(url);\n\t\t}\n\t}\n\n\tenableRelay(url: string): void {\n\t\tthis.disabledRelays.delete(url);\n\t\tthis.nextAllowed.delete(url);\n\t}\n\n\tisRelayDisabled(url: string): boolean {\n\t\treturn this.disabledRelays.has(url);\n\t}\n\n\tgetActiveReqCount(url: string): number {\n\t\treturn this.getCount(url);\n\t}\n\n\tgetConnectionStatus(url: string): ConnectionStatus | undefined {\n\t\tconst connection = this.connections.get(url);\n\t\treturn connection ? connection.getStatus() : undefined;\n\t}\n\n\tgetAllStatuses(): Map<string, ConnectionStatus> {\n\t\treturn new Map(\n\t\t\tArray.from(this.connections.entries()).map(([url, conn]) => [url, conn.getStatus()])\n\t\t);\n\t}\n}\n"],"names":["ByteRingBuffer","buffer","value","head","tail","used","p","payload","N","len","totalSize","droppedThisWrite","mySeq","writePos","readPos","trailerPos","variable","advance","targetPos","source","sourceOffset","length","remaining","srcOffset","tgt","spaceToEnd","chunkSize","tgtAbs","srcChunk","sourcePos","target","targetOffset","tgtOffset","src","srcAbs","ConnectionStatus","RelayConnection","url","config","handler","signal","cleanup","onOpen","to","onClose","ev","onError","_ev","onMessage","event","rawText","subId","onAbort","frame","timeoutMs","resolve","reject","timer","resolver","ok","waiters","fn","idx","cap","base","max","mult","jitter","delay","s","i","n","start","ConnectionRegistry","m","k","delta","next","conn","at","e","kind","frames","relays","tasks","error","connection"],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sources":["../src/ws/ring-buffer.ts","../src/ws/types.ts","../src/ws/connection.ts","../src/ws/registry.ts"],"sourcesContent":["export class ByteRingBuffer {\n\tprivate readonly sab: SharedArrayBuffer;\n\tprivate readonly dataView: DataView;\n\tprivate readonly dataStart: number = 32;\n\tprivate readonly capacity: number;\n\tprivate dropped: number = 0;\n\n\tconstructor(buffer: SharedArrayBuffer) {\n\t\tthis.sab = buffer;\n\t\tthis.dataView = new DataView(buffer);\n\t\tthis.capacity = this.dataView.getUint32(0, true); // little-endian\n\n\t\t// Read initial state (assume Rust sets capacity, head=0, tail=0, seq=0)\n\t\t// We don't reset here; assume initialized\n\t}\n\n\tprivate getHead(): number {\n\t\treturn this.dataView.getUint32(4, true) % this.capacity;\n\t}\n\n\tprivate setHead(value: number): void {\n\t\tthis.dataView.setUint32(4, value % this.capacity, true);\n\t}\n\n\tprivate getTail(): number {\n\t\treturn this.dataView.getUint32(8, true) % this.capacity;\n\t}\n\n\tprivate setTail(value: number): void {\n\t\tthis.dataView.setUint32(8, value % this.capacity, true);\n\t}\n\n\tprivate getSeq(): number {\n\t\treturn this.dataView.getUint32(12, true);\n\t}\n\n\tprivate setSeq(value: number): void {\n\t\tthis.dataView.setUint32(12, value, true);\n\t}\n\n\tgetFreeSpace(): number {\n\t\tconst head = this.getHead();\n\t\tconst tail = this.getTail();\n\t\tconst used = (head - tail + this.capacity) % this.capacity;\n\t\treturn this.capacity - used;\n\t}\n\n\tgetDropped(): number {\n\t\treturn this.dropped;\n\t}\n\n\thasRecords(): boolean {\n\t\treturn this.getHead() !== this.getTail();\n\t}\n\n\treadNext(): { payload: Uint8Array } | null {\n\t\tconst p = this.read();\n\t\treturn p ? { payload: p } : null;\n\t}\n\n\t/**\n\t * Writes a payload to the ring buffer, overwriting old records if necessary.\n\t * Returns the sequence number if successful, -1 if dropped (couldn't make space).\n\t */\n\twrite(payload: Uint8Array): number {\n\t\tconst N = payload.byteLength;\n\t\tconst len = 8 + N; // type(2) + pad(2) + seq(4) + payload(N)\n\t\tconst totalSize = 4 + len + 4; // len + variable + trailer\n\n\t\t// Make space by skipping records\n\t\tlet droppedThisWrite = 0;\n\t\twhile (this.getFreeSpace() < totalSize) {\n\t\t\tif (!this.skipRecord()) {\n\t\t\t\t// Can't skip more (uncommitted record), drop this write\n\t\t\t\tthis.dropped += droppedThisWrite + 1;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tdroppedThisWrite++;\n\t\t}\n\n\t\t// Enough space, write\n\t\tconst mySeq = this.getSeq() + 1;\n\t\tthis.setSeq(mySeq);\n\n\t\tlet writePos = this.getHead();\n\n\t\t// Write len\n\t\tthis.dataView.setUint32(this.dataStart + writePos, len, true);\n\t\twritePos = (writePos + 4) % this.capacity;\n\n\t\t// Write type (0)\n\t\tthis.dataView.setUint16(this.dataStart + writePos, 0, true);\n\t\twritePos = (writePos + 2) % this.capacity;\n\n\t\t// Write pad (0)\n\t\tthis.dataView.setUint16(this.dataStart + writePos, 0, true);\n\t\twritePos = (writePos + 2) % this.capacity;\n\n\t\t// Write seq\n\t\tthis.dataView.setUint32(this.dataStart + writePos, mySeq, true);\n\t\twritePos = (writePos + 4) % this.capacity;\n\n\t\t// Write payload (possibly wrapped)\n\t\tthis.copyBytes(writePos, payload, 0, N);\n\t\twritePos = (writePos + N) % this.capacity;\n\n\t\t// Write trailer (len)\n\t\tthis.dataView.setUint32(this.dataStart + writePos, len, true);\n\t\twritePos = (writePos + 4) % this.capacity;\n\n\t\t// Advance head\n\t\tthis.setHead(writePos);\n\n\t\tthis.dropped += droppedThisWrite;\n\t\treturn mySeq;\n\t}\n\n\t/**\n\t * Reads the next committed payload or null if none ready.\n\t * Advances tail on success.\n\t */\n\tread(): Uint8Array | null {\n\t\tlet readPos = this.getTail();\n\t\tif (readPos === this.getHead()) return null; // empty\n\n\t\tconst len = this.dataView.getUint32(this.dataStart + readPos, true);\n\t\tif (len === 0) return null;\n\n\t\tconst trailerPos = (readPos + 4 + len) % this.capacity;\n\t\tconst trailer = this.dataView.getUint32(this.dataStart + trailerPos, true);\n\n\t\tif (trailer !== len) return null; // not committed\n\n\t\t// Read variable part (len bytes from readPos + 4)\n\t\tconst variable = new Uint8Array(len);\n\t\tthis.copyFromRing((readPos + 4) % this.capacity, variable, 0, len);\n\n\t\t// Extract payload (skip type+pad+seq = 8 bytes)\n\t\tconst payload = variable.subarray(8);\n\n\t\t// Advance tail\n\t\tconst advance = 4 + len + 4; // len field + variable + trailer\n\t\tthis.setTail((this.getTail() + advance) % this.capacity);\n\n\t\treturn payload;\n\t}\n\n\tprivate skipRecord(): boolean {\n\t\tlet readPos = this.getTail();\n\t\tif (readPos === this.getHead()) return false; // empty\n\n\t\tconst len = this.dataView.getUint32(this.dataStart + readPos, true);\n\t\tif (len === 0) return false;\n\n\t\tconst trailerPos = (readPos + 4 + len) % this.capacity;\n\t\tconst trailer = this.dataView.getUint32(this.dataStart + trailerPos, true);\n\n\t\tif (trailer !== len) return false; // not committed\n\n\t\t// Skip by advancing tail\n\t\tconst advance = 4 + len + 4;\n\t\tthis.setTail((this.getTail() + advance) % this.capacity);\n\t\treturn true;\n\t}\n\n\tprivate copyBytes(\n\t\ttargetPos: number,\n\t\tsource: Uint8Array,\n\t\tsourceOffset: number,\n\t\tlength: number\n\t): void {\n\t\tlet remaining = length;\n\t\tlet srcOffset = sourceOffset;\n\t\tlet tgt = targetPos;\n\n\t\twhile (remaining > 0) {\n\t\t\tconst spaceToEnd = this.capacity - (tgt % this.capacity);\n\t\t\tconst chunkSize = Math.min(remaining, spaceToEnd);\n\t\t\tconst tgtAbs = this.dataStart + (tgt % this.capacity);\n\t\t\tconst srcChunk = source.subarray(srcOffset, srcOffset + chunkSize);\n\t\t\tnew Uint8Array(this.sab, tgtAbs, chunkSize).set(srcChunk);\n\t\t\tremaining -= chunkSize;\n\t\t\tsrcOffset += chunkSize;\n\t\t\ttgt += chunkSize;\n\t\t}\n\t}\n\n\tprivate copyFromRing(\n\t\tsourcePos: number,\n\t\ttarget: Uint8Array,\n\t\ttargetOffset: number,\n\t\tlength: number\n\t): void {\n\t\tlet remaining = length;\n\t\tlet tgtOffset = targetOffset;\n\t\tlet src = sourcePos;\n\n\t\twhile (remaining > 0) {\n\t\t\tconst spaceToEnd = this.capacity - (src % this.capacity);\n\t\t\tconst chunkSize = Math.min(remaining, spaceToEnd);\n\t\t\tconst srcAbs = this.dataStart + (src % this.capacity);\n\t\t\tconst srcChunk = new Uint8Array(this.sab, srcAbs, chunkSize);\n\t\t\ttarget.set(srcChunk, tgtOffset);\n\t\t\tremaining -= chunkSize;\n\t\t\ttgtOffset += chunkSize;\n\t\t\tsrc += chunkSize;\n\t\t}\n\t}\n}\n","export enum ConnectionStatus {\n Idle = 'idle',\n Connecting = 'connecting',\n Ready = 'ready',\n Closing = 'closing',\n Closed = 'closed',\n Error = 'error',\n}\n\nexport enum MsgKind {\n Unknown = 0,\n Event = 1,\n Eose = 2,\n Ok = 3,\n Closed = 4,\n Notice = 5,\n Auth = 6,\n}\n\n // MsgKind is defined in FlatBuffers schema; import from generated code\n // export type { MsgKind } from '../fb/worker_messages_generated';\n\nexport interface RelayConfig {\n connectTimeoutMs?: number;\n writeTimeoutMs?: number;\n retryBaseMs?: number;\n retryMaxMs?: number;\n retryMultiplier?: number;\n retryJitter?: number;\n retry?: {\n baseMs: number;\n maxMs: number;\n multiplier: number;\n jitter: number;\n };\n idleTimeoutMs?: number;\n}\n\nexport interface RelayStats {\n sent: number;\n received: number;\n reconnects: number;\n lastActivity: number; // timestamp\n uptime?: number;\n dropped: number; // for ring buffer overwrites\n}\n\nexport interface InboundEnvelope {\n relays: string[];\n frames: string[];\n}\n\nexport interface WorkerLine {\n relay: {\n url: string;\n };\n kind: number; // MsgKind from generated\n sub_id?: string;\n raw: Uint8Array; // UTF-8 bytes\n}\n\nexport type FrameCallback = (frame: string) => void;\nexport type MessageHandler = (url: string, kind: MsgKind, subId: string | null, rawText: string) => void;\n","import { ConnectionStatus, RelayConfig, RelayStats } from './types';\n\n// Callback invoked for every incoming websocket text frame\nexport type MessageHandler = (\n\turl: string,\n\tsubId: string | null, // present for EVENT/EOSE/CLOSED\n\trawText: string\n) => void;\n\nexport class RelayConnection {\n\tprivate wantReconnect = true;\n\tprivate url: string;\n\tprivate config: RelayConfig;\n\tprivate status: ConnectionStatus = ConnectionStatus.Closed;\n\tprivate ws: WebSocket | null = null;\n\tprivate reconnectTimer: number | null = null;\n\tprivate abortController: AbortController | null = null;\n\n\tprivate attempts: number = 0; // reconnection attempts after first failure\n\tprivate givenUp: boolean = false; // set when attempts reached cap\n\tprivate lastActivity: number = Date.now();\n\tprivate stats: RelayStats = {\n\t\tdropped: 0,\n\t\tsent: 0,\n\t\treceived: 0,\n\t\treconnects: 0, // mirrors attempts for visibility\n\t\tlastActivity: Date.now()\n\t};\n\tprivate readyWaiters: Array<(ok: boolean) => void> = [];\n\tpublic messageHandler: MessageHandler | null = null;\n\n\tconstructor(url: string, config: Partial<RelayConfig> = {}) {\n\t\tthis.url = url;\n\t\tthis.config = {\n\t\t\tconnectTimeoutMs: 5_000,\n\t\t\twriteTimeoutMs: 10_000,\n\t\t\tretry: {\n\t\t\t\tbaseMs: 300,\n\t\t\t\tmaxMs: 10_000,\n\t\t\t\tmultiplier: 1.6,\n\t\t\t\tjitter: 0.1\n\t\t\t},\n\t\t\tmaxReconnectAttempts: 2, // default: 2 retries\n\t\t\tidleTimeoutMs: 300_000,\n\t\t\t...config\n\t\t};\n\t}\n\n\tgetUrl(): string {\n\t\treturn this.url;\n\t}\n\tgetStatus(): ConnectionStatus {\n\t\treturn this.status;\n\t}\n\tgetStats(): RelayStats {\n\t\treturn { ...this.stats, reconnects: this.attempts, lastActivity: this.lastActivity };\n\t}\n\tgetLastActivity(): number {\n\t\treturn this.lastActivity;\n\t}\n\thasGivenUp(): boolean {\n\t\treturn this.givenUp;\n\t}\n\n\tsetMessageHandler(handler: MessageHandler): void {\n\t\tthis.messageHandler = handler;\n\t}\n\n\t// Fire-and-forget connect. It never throws. Use waitForReady() to await.\n\tconnect(): void {\n\t\tif (this.givenUp) return;\n\t\tif (this.status === ConnectionStatus.Connecting || this.status === ConnectionStatus.Ready)\n\t\t\treturn;\n\n\t\t// Clear pending reconnect\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\n\t\tthis.status = ConnectionStatus.Connecting;\n\t\tthis.abortController = new AbortController();\n\t\tconst signal = this.abortController.signal;\n\n\t\ttry {\n\t\t\tthis.ws = new WebSocket(this.url);\n\t\t\tthis.ws.binaryType = 'arraybuffer';\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tif (!this.ws) return;\n\t\t\t\tthis.ws.onopen = null as any;\n\t\t\t\tthis.ws.onclose = null as any;\n\t\t\t\tthis.ws.onerror = null as any;\n\t\t\t\tthis.ws.onmessage = null as any;\n\t\t\t};\n\n\t\t\tconst onOpen = () => {\n\t\t\t\t// Clear connect timeout\n\t\t\t\tclearTimeout(to);\n\t\t\t\t// Success: reset attempts and flags\n\t\t\t\tthis.status = ConnectionStatus.Ready;\n\t\t\t\tthis.attempts = 0;\n\t\t\t\tthis.givenUp = false;\n\t\t\t\tthis.lastActivity = Date.now();\n\t\t\t\tthis.stats.lastActivity = this.lastActivity;\n\t\t\t\tthis.resolveReady(true);\n\t\t\t};\n\n\t\t\tconst onClose = (ev: CloseEvent) => {\n\t\t\t\t// Clear connect timeout\n\t\t\t\tclearTimeout(to);\n\t\t\t\tcleanup();\n\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\tthis.resolveReady(false);\n\t\t\t\tif (ev.code !== 1000) {\n\t\t\t\t\tthis.scheduleReconnect();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst onError = (_ev: Event) => {\n\t\t\t\t// Clear connect timeout\n\t\t\t\tclearTimeout(to);\n\t\t\t\t// Treat like close; don’t throw\n\t\t\t\tcleanup();\n\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\tthis.resolveReady(false);\n\t\t\t\tthis.scheduleReconnect();\n\t\t\t};\n\n\t\t\tconst onMessage = (event: MessageEvent) => {\n\t\t\t\tthis.lastActivity = Date.now();\n\t\t\t\tthis.stats.lastActivity = this.lastActivity;\n\t\t\t\tthis.stats.received++;\n\n\t\t\t\tif (typeof event.data === 'string') {\n\t\t\t\t\tconst rawText = event.data;\n\t\t\t\t\tconst subId = this.extractSubId(rawText);\n\t\t\t\t\tif (this.messageHandler) {\n\t\t\t\t\t\tthis.messageHandler(this.url, subId, rawText);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Attach\n\t\t\tthis.ws.onopen = onOpen;\n\t\t\tthis.ws.onclose = onClose;\n\t\t\tthis.ws.onerror = onError;\n\t\t\tthis.ws.onmessage = onMessage;\n\n\t\t\t// Timeout and abort\n\t\t\tconst to = setTimeout(() => {\n\t\t\t\tif (this.status === ConnectionStatus.Connecting) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.ws?.close();\n\t\t\t\t\t} catch {}\n\t\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\t\tthis.resolveReady(false);\n\t\t\t\t\tthis.scheduleReconnect();\n\t\t\t\t}\n\t\t\t}, this.config.connectTimeoutMs);\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tclearTimeout(to);\n\t\t\t\tcleanup();\n\t\t\t\ttry {\n\t\t\t\t\tthis.ws?.close();\n\t\t\t\t} catch {}\n\t\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\t\tthis.resolveReady(false);\n\t\t\t\tif (this.wantReconnect) this.scheduleReconnect(); // guard here\n\t\t\t};\n\n\t\t\tsignal.addEventListener('abort', onAbort);\n\t\t} catch {\n\t\t\tthis.status = ConnectionStatus.Closed;\n\t\t\tthis.resolveReady(false);\n\t\t\tthis.scheduleReconnect();\n\t\t}\n\t}\n\n\tasync sendMessage(frame: string): Promise<void> {\n\t\tif (this.status !== ConnectionStatus.Ready || !this.ws) {\n\t\t\tthrow new Error('Connection not ready');\n\t\t}\n\t\tthis.ws.send(frame);\n\t\tthis.stats.sent++;\n\t\tthis.lastActivity = Date.now();\n\t\tthis.stats.lastActivity = this.lastActivity;\n\t}\n\n\tasync waitForReady(timeoutMs: number = this.config.connectTimeoutMs ?? 5_000): Promise<void> {\n\t\tif (this.status === ConnectionStatus.Ready) return;\n\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.removeReadyResolver(resolver);\n\t\t\t\treject(new Error('Timeout waiting for ready'));\n\t\t\t}, timeoutMs);\n\n\t\t\tconst resolver = (ok: boolean) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tif (ok) resolve();\n\t\t\t\telse reject(new Error('Connection closed'));\n\t\t\t};\n\t\t\tthis.readyWaiters.push(resolver);\n\t\t});\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.wantReconnect = false;\n\t\tif (this.abortController) this.abortController.abort();\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\t\tthis.closeWebSocket();\n\t\tthis.status = ConnectionStatus.Closed;\n\t\tthis.resolveReady(false);\n\t}\n\n\tshouldCloseDueToInactivity(): boolean {\n\t\treturn Date.now() - this.lastActivity > (this.config.idleTimeoutMs ?? 300_000);\n\t}\n\n\t// Internals\n\n\tprivate resolveReady(ok: boolean) {\n\t\tif (this.readyWaiters.length === 0) return;\n\t\tconst waiters = this.readyWaiters.slice();\n\t\tthis.readyWaiters.length = 0;\n\t\tfor (const fn of waiters) {\n\t\t\ttry {\n\t\t\t\tfn(ok);\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\tprivate removeReadyResolver(fn: (ok: boolean) => void) {\n\t\tconst idx = this.readyWaiters.indexOf(fn);\n\t\tif (idx >= 0) this.readyWaiters.splice(idx, 1);\n\t}\n\n\tprivate closeWebSocket() {\n\t\tif (this.ws) {\n\t\t\ttry {\n\t\t\t\tthis.ws.close(1000, 'Normal closure');\n\t\t\t} catch {}\n\t\t\tthis.ws = null;\n\t\t}\n\t}\n\n\tprivate scheduleReconnect(): void {\n\t\tif (!this.wantReconnect) return;\n\t\tif (this.status !== ConnectionStatus.Closed || !this.config.retry?.baseMs) return;\n\n\t\tconst cap = this.config.maxReconnectAttempts ?? 2;\n\t\tif (cap > 0 && this.attempts >= cap) {\n\t\t\tthis.givenUp = true;\n\t\t\treturn;\n\t\t}\n\n\t\tconst base = this.config.retry.baseMs;\n\t\tconst max = this.config.retry.maxMs ?? 10_000;\n\t\tconst mult = this.config.retry.multiplier ?? 1.6;\n\t\tconst jitter = this.config.retry.jitter ?? 0.1;\n\n\t\t// Calculate delay using current attempts\n\t\tconst delay =\n\t\t\tMath.min(base * Math.pow(mult, this.attempts), max) *\n\t\t\t(1 + (Math.random() - 0.5) * jitter * 2);\n\n\t\tthis.reconnectTimer = setTimeout(() => {\n\t\t\t// Increment attempts when actually trying again\n\t\t\tthis.attempts++;\n\t\t\tthis.stats.reconnects = this.attempts;\n\t\t\tthis.connect();\n\t\t}, delay) as unknown as number;\n\t}\n\n\tprivate extractSubId(s: string): string | null {\n\t\tlet i = 0;\n\t\tconst n = s.length;\n\n\t\t// Skip leading whitespace\n\t\twhile (i < n && s.charCodeAt(i) <= 32) i++;\n\t\tif (i >= n || s[i] !== '[') return null;\n\t\ti++;\n\n\t\t// Skip whitespace before first element\n\t\twhile (i < n && s.charCodeAt(i) <= 32) i++;\n\t\tif (i >= n) return null;\n\n\t\t// Skip first element (kind or similar)\n\t\tif (s[i] === '\"') {\n\t\t\t// Fast-skip a quoted string (no escape handling, consistent with existing code)\n\t\t\ti++;\n\t\t\twhile (i < n && s[i] !== '\"') i++;\n\t\t\tif (i >= n) return null;\n\t\t\ti++; // past closing quote\n\t\t} else {\n\t\t\t// Non-quoted first element; skip until comma or end of array\n\t\t\twhile (i < n && s[i] !== ',' && s[i] !== ']') i++;\n\t\t}\n\n\t\t// Move to the comma after first element\n\t\twhile (i < n && s[i] !== ',') i++;\n\t\tif (i >= n || s[i] !== ',') return null;\n\t\ti++; // skip comma\n\n\t\t// Skip whitespace before second element\n\t\twhile (i < n && s.charCodeAt(i) <= 32) i++;\n\t\tif (i >= n) return null;\n\n\t\t// Extract second element only if it's a quoted string\n\t\tif (s[i] === '\"') {\n\t\t\ti++;\n\t\t\tconst start = i;\n\t\t\twhile (i < n && s[i] !== '\"') i++;\n\t\t\tif (i > n) return null;\n\t\t\treturn s.slice(start, i);\n\t\t}\n\t\treturn null;\n\t}\n}\n","import { RelayConfig, ConnectionStatus } from './types';\nimport { RelayConnection } from './connection';\n\nexport class ConnectionRegistry {\n\tprivate connections = new Map<string, RelayConnection>();\n\tprivate disabledRelays = new Set<string>();\n\tprivate nextAllowed = new Map<string, number>(); // cooldown timestamps per URL (ms)\n\tprivate subCounts = new Map<string, number>(); // active subscription count per URL\n\tprivate config: RelayConfig;\n\n\tprivate readonly cooldownMs = 60_000; // after failures\n\n\tconstructor(config: RelayConfig) {\n\t\tthis.config = { maxReconnectAttempts: 2, ...config };\n\t}\n\n\tprivate now(): number {\n\t\treturn Date.now();\n\t}\n\n\tprivate detectKind(frame: string): 'REQ' | 'CLOSE' | 'OTHER' {\n\t\tconst m = frame.match(/^\\s*\\[\\s*\"([^\"]+)\"/);\n\t\tif (!m) return 'OTHER';\n\t\tconst k = m[1].toUpperCase();\n\t\tif (k === 'REQ') return 'REQ';\n\t\tif (k === 'CLOSE') return 'CLOSE';\n\t\treturn 'OTHER';\n\t}\n\n\tprivate getCount(url: string): number {\n\t\treturn this.subCounts.get(url) ?? 0;\n\t}\n\tprivate setCount(url: string, value: number): void {\n\t\tthis.subCounts.set(url, Math.max(0, value));\n\t}\n\n\t// Replace bumpCount with a pure counter that returns the new value.\n\tprivate bumpCount(url: string, delta: number): number {\n\t\tconst next = Math.max(0, this.getCount(url) + delta);\n\t\tthis.subCounts.set(url, next);\n\t\treturn next;\n\t}\n\n\tprivate giveUpOrCooldown(url: string, conn?: RelayConnection) {\n\t\tif (conn?.hasGivenUp()) {\n\t\t\tif (!this.disabledRelays.has(url)) {\n\t\t\t\tconsole.warn(`[registry] disabling relay ${url}: max attempts reached`);\n\t\t\t}\n\t\t\tthis.disabledRelays.add(url);\n\t\t\tthis.nextAllowed.set(url, this.now() + this.cooldownMs);\n\t\t} else {\n\t\t\tthis.nextAllowed.set(url, this.now() + Math.min(this.cooldownMs, 10_000));\n\t\t}\n\t}\n\n\tprivate isCoolingDown(url: string): boolean {\n\t\tconst at = this.nextAllowed.get(url) ?? 0;\n\t\treturn this.now() < at;\n\t}\n\n\tasync ensureConnection(url: string): Promise<RelayConnection> {\n\t\tif (this.disabledRelays.has(url)) {\n\t\t\tthrow new Error(`Relay disabled: ${url}`);\n\t\t}\n\t\tif (this.isCoolingDown(url)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Relay ${url} cooling down until ${new Date(this.nextAllowed.get(url)!).toISOString()}`\n\t\t\t);\n\t\t}\n\n\t\tlet conn = this.connections.get(url);\n\t\tif (!conn) {\n\t\t\tconn = new RelayConnection(url, this.config);\n\t\t\tthis.connections.set(url, conn);\n\t\t\tconn.connect(); // fire-and-forget\n\t\t} else if (conn.getStatus() !== ConnectionStatus.Ready) {\n\t\t\t// nudge reconnect on existing connection\n\t\t\tconn.connect();\n\t\t}\n\n\t\tif (conn.getStatus() !== ConnectionStatus.Ready) {\n\t\t\ttry {\n\t\t\t\tawait conn.waitForReady(this.config.connectTimeoutMs ?? 5_000);\n\t\t\t} catch (e) {\n\t\t\t\tthis.giveUpOrCooldown(url, conn);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\n\t\tif (conn.getStatus() !== ConnectionStatus.Ready) {\n\t\t\tthis.giveUpOrCooldown(url, conn);\n\t\t\tthrow new Error(`Relay ${url} not ready`);\n\t\t}\n\n\t\treturn conn;\n\t}\n\n\t// Update sendFrame to disconnect immediately when count becomes 0 after a CLOSE.\n\tasync sendFrame(url: string, frame: string): Promise<void> {\n\t\tif (this.disabledRelays.has(url) || this.isCoolingDown(url)) return;\n\n\t\tconst kind = this.detectKind(frame);\n\t\tconst conn = await this.ensureConnection(url);\n\n\t\ttry {\n\t\t\tawait conn.sendMessage(frame);\n\t\t} catch (e) {\n\t\t\tthis.giveUpOrCooldown(url, conn);\n\t\t\tawait this.disconnect(url);\n\t\t\tthrow e;\n\t\t}\n\n\t\t// Track the sub count for visibility and lifecycle decisions\n\t\tif (kind === 'REQ') {\n\t\t\tthis.bumpCount(url, +1);\n\t\t} else if (kind === 'CLOSE') {\n\t\t\tconst newCount = this.bumpCount(url, -1);\n\t\t\tif (newCount === 0) {\n\t\t\t\tconsole.log('disconnecting', url);\n\t\t\t\t// Immediately disconnect when no more active REQ for this relay\n\t\t\t\tawait this.disconnect(url);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async sendAllFramesToRelay(url: string, frames: string[]): Promise<void> {\n\t\tfor (const frame of frames) {\n\t\t\tawait this.sendFrame(url, frame);\n\t\t}\n\t}\n\n\tasync sendToRelays(\n\t\trelays: string[],\n\t\tframes: string[],\n\t\tmaxSuccesses = 5,\n\t\tmaxConcurrency = 5\n\t): Promise<void> {\n\t\tconst available = relays.filter(\n\t\t\t(url) => !this.disabledRelays.has(url) && !this.isCoolingDown(url)\n\t\t);\n\n\t\tconst target = Math.min(maxSuccesses, available.length);\n\t\tif (target === 0) return;\n\n\t\tlet index = 0;\n\t\tlet successes = 0;\n\t\tlet inFlight = 0;\n\n\t\tlet resolveDone!: () => void;\n\t\tconst done = new Promise<void>((resolve) => {\n\t\t\tresolveDone = resolve;\n\t\t});\n\n\t\tconst maybeFinish = () => {\n\t\t\tif (successes >= target || (index >= available.length && inFlight === 0)) {\n\t\t\t\tresolveDone();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t\tconst launch = () => {\n\t\t\t// Fill available “slots” up to maxConcurrency.\n\t\t\twhile (inFlight < maxConcurrency && index < available.length && successes < target) {\n\t\t\t\tconst url = available[index++];\n\t\t\t\tinFlight++;\n\n\t\t\t\tthis.sendAllFramesToRelay(url, frames)\n\t\t\t\t\t.then(() => {\n\t\t\t\t\t\tsuccesses++;\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t// ignore failures; just move on\n\t\t\t\t\t})\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tinFlight--;\n\t\t\t\t\t\tif (!maybeFinish()) {\n\t\t\t\t\t\t\tlaunch(); // backfill the freed slot\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tlaunch();\n\t\tawait done;\n\t}\n\n\tasync disconnect(url: string): Promise<void> {\n\t\tconst connection = this.connections.get(url);\n\t\tif (connection) {\n\t\t\tawait connection.close();\n\t\t\tthis.connections.delete(url);\n\t\t}\n\t\t// No pending-disconnects to cancel anymore\n\t\tthis.subCounts.delete(url);\n\t}\n\n\tasync disconnectAll(): Promise<void> {\n\t\tfor (const [url] of this.connections) {\n\t\t\tawait this.disconnect(url);\n\t\t}\n\t}\n\n\tenableRelay(url: string): void {\n\t\tthis.disabledRelays.delete(url);\n\t\tthis.nextAllowed.delete(url);\n\t}\n\n\tisRelayDisabled(url: string): boolean {\n\t\treturn this.disabledRelays.has(url);\n\t}\n\n\tgetActiveReqCount(url: string): number {\n\t\treturn this.getCount(url);\n\t}\n\n\tgetConnectionStatus(url: string): ConnectionStatus | undefined {\n\t\tconst connection = this.connections.get(url);\n\t\treturn connection ? connection.getStatus() : undefined;\n\t}\n\n\tgetAllStatuses(): Map<string, ConnectionStatus> {\n\t\treturn new Map(\n\t\t\tArray.from(this.connections.entries()).map(([url, conn]) => [url, conn.getStatus()])\n\t\t);\n\t}\n}\n"],"names":["ByteRingBuffer","buffer","value","head","tail","used","p","payload","N","len","totalSize","droppedThisWrite","mySeq","writePos","readPos","trailerPos","variable","advance","targetPos","source","sourceOffset","length","remaining","srcOffset","tgt","spaceToEnd","chunkSize","tgtAbs","srcChunk","sourcePos","target","targetOffset","tgtOffset","src","srcAbs","ConnectionStatus","RelayConnection","url","config","handler","signal","cleanup","onOpen","to","onClose","ev","onError","_ev","onMessage","event","rawText","subId","onAbort","frame","timeoutMs","resolve","reject","timer","resolver","ok","waiters","fn","idx","cap","base","max","mult","jitter","delay","s","i","n","start","ConnectionRegistry","m","k","delta","next","conn","at","e","kind","frames","relays","maxSuccesses","maxConcurrency","available","index","successes","inFlight","resolveDone","done","maybeFinish","launch","connection"],"mappings":"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"}
@@ -19,7 +19,7 @@ export declare class ConnectionRegistry {
19
19
  ensureConnection(url: string): Promise<RelayConnection>;
20
20
  sendFrame(url: string, frame: string): Promise<void>;
21
21
  private sendAllFramesToRelay;
22
- sendToRelays(relays: string[], frames: string[]): Promise<void>;
22
+ sendToRelays(relays: string[], frames: string[], maxSuccesses?: number, maxConcurrency?: number): Promise<void>;
23
23
  disconnect(url: string): Promise<void>;
24
24
  disconnectAll(): Promise<void>;
25
25
  enableRelay(url: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/ws/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,MAAM,CAAc;IAE5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;gBAEzB,MAAM,EAAE,WAAW;IAI/B,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,QAAQ;IAGhB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,aAAa;IAKf,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAsCvD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YA2B5C,oBAAoB;IAM5B,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK9B,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIrC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAItC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAK9D,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;CAK/C"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/ws/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,MAAM,CAAc;IAE5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;gBAEzB,MAAM,EAAE,WAAW;IAI/B,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,QAAQ;IAGhB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,aAAa;IAKf,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAsCvD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YA2B5C,oBAAoB;IAM5B,YAAY,CACjB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,YAAY,SAAI,EAChB,cAAc,SAAI,GAChB,OAAO,CAAC,IAAI,CAAC;IAmDV,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK9B,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIrC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAItC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAK9D,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;CAK/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candypoets/nipworker",
3
- "version": "0.0.58",
3
+ "version": "0.0.59",
4
4
  "description": "Nostr client library with worker-based architecture using Rust WASM",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -63,7 +63,7 @@
63
63
  "vite-plugin-wasm": "^3.0.0"
64
64
  },
65
65
  "dependencies": {
66
- "@candypoets/rust-worker": "0.0.58",
66
+ "@candypoets/rust-worker": "0.0.59",
67
67
  "nostr-tools": "^2.0.0"
68
68
  },
69
69
  "engines": {