@candypoets/nipworker 0.90.0 → 0.90.1

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.
@@ -21,15 +21,15 @@ export interface InitOutput {
21
21
  readonly wsrust_close: (a: number, b: number, c: number) => void;
22
22
  readonly wsrust_new: (a: any, b: any, c: any, d: any, e: any) => [number, number, number];
23
23
  readonly init_tracing: (a: number, b: number) => void;
24
- readonly wasm_bindgen__convert__closures_____invoke__hbfefea57f0386d0d: (a: number, b: number) => void;
25
- readonly wasm_bindgen__closure__destroy__h8f3ce6e75229a5a8: (a: number, b: number) => void;
26
24
  readonly wasm_bindgen__convert__closures_____invoke__h911638d95040c9f2: (a: number, b: number, c: any) => void;
27
25
  readonly wasm_bindgen__closure__destroy__h063e4a13178b0c12: (a: number, b: number) => void;
26
+ readonly wasm_bindgen__convert__closures_____invoke__hbfefea57f0386d0d: (a: number, b: number) => void;
27
+ readonly wasm_bindgen__closure__destroy__h8f3ce6e75229a5a8: (a: number, b: number) => void;
28
28
  readonly wasm_bindgen__convert__closures_____invoke__h9f24d583ae866fa6: (a: number, b: number, c: any) => void;
29
29
  readonly wasm_bindgen__closure__destroy__h8c04b6ea827d767b: (a: number, b: number) => void;
30
- readonly wasm_bindgen__convert__closures_____invoke__hd724dca759e5a136: (a: number, b: number) => void;
31
30
  readonly wasm_bindgen__convert__closures_____invoke__h6e43682478e95d85: (a: number, b: number, c: any) => void;
32
31
  readonly wasm_bindgen__closure__destroy__h2312464f5f444124: (a: number, b: number) => void;
32
+ readonly wasm_bindgen__convert__closures_____invoke__hd724dca759e5a136: (a: number, b: number) => void;
33
33
  readonly __wbindgen_malloc: (a: number, b: number) => number;
34
34
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
35
35
  readonly __wbindgen_exn_store: (a: number) => void;
@@ -5,15 +5,15 @@ export const __wbg_wsrust_free: (a: number, b: number) => void;
5
5
  export const wsrust_close: (a: number, b: number, c: number) => void;
6
6
  export const wsrust_new: (a: any, b: any, c: any, d: any, e: any) => [number, number, number];
7
7
  export const init_tracing: (a: number, b: number) => void;
8
- export const wasm_bindgen__convert__closures_____invoke__hbfefea57f0386d0d: (a: number, b: number) => void;
9
- export const wasm_bindgen__closure__destroy__h8f3ce6e75229a5a8: (a: number, b: number) => void;
10
8
  export const wasm_bindgen__convert__closures_____invoke__h911638d95040c9f2: (a: number, b: number, c: any) => void;
11
9
  export const wasm_bindgen__closure__destroy__h063e4a13178b0c12: (a: number, b: number) => void;
10
+ export const wasm_bindgen__convert__closures_____invoke__hbfefea57f0386d0d: (a: number, b: number) => void;
11
+ export const wasm_bindgen__closure__destroy__h8f3ce6e75229a5a8: (a: number, b: number) => void;
12
12
  export const wasm_bindgen__convert__closures_____invoke__h9f24d583ae866fa6: (a: number, b: number, c: any) => void;
13
13
  export const wasm_bindgen__closure__destroy__h8c04b6ea827d767b: (a: number, b: number) => void;
14
- export const wasm_bindgen__convert__closures_____invoke__hd724dca759e5a136: (a: number, b: number) => void;
15
14
  export const wasm_bindgen__convert__closures_____invoke__h6e43682478e95d85: (a: number, b: number, c: any) => void;
16
15
  export const wasm_bindgen__closure__destroy__h2312464f5f444124: (a: number, b: number) => void;
16
+ export const wasm_bindgen__convert__closures_____invoke__hd724dca759e5a136: (a: number, b: number) => void;
17
17
  export const __wbindgen_malloc: (a: number, b: number) => number;
18
18
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
19
19
  export const __wbindgen_exn_store: (a: number) => void;
@@ -1,2 +1,2 @@
1
- export {};
1
+ export { createRelayProxyServer, createRelayProxyWebSocketServer, attachRelayProxyToServer, createExpressRelayProxyMiddleware, type RelayProxyServerOptions, type RelayProxyServer, type AttachRelayProxyOptions, type AttachedRelayProxy, type WebSocketRelayProxy } from './relayProxyServer';
2
2
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,sBAAsB,EACtB,+BAA+B,EAC/B,wBAAwB,EACxB,iCAAiC,EACjC,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,MAAM,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- import{createRelayProxyServer as R}from"../relayProxyServer.js";const o=process.env.NIPWORKER_PROXY_HOST||"127.0.0.1",e=process.env.NIPWORKER_PROXY_PORT||"7777",s=process.env.NIPWORKER_PROXY_PATH||"/",r=Number.parseInt(e,10);if(!Number.isFinite(r)||r<=0)throw new Error(`Invalid NIPWORKER_PROXY_PORT: ${e}`);R({host:o,port:r,path:s});
1
+ import{attachRelayProxyToServer as e,createExpressRelayProxyMiddleware as r,createRelayProxyServer as a,createRelayProxyWebSocketServer as y}from"../relayProxyServer.js";export{e as attachRelayProxyToServer,r as createExpressRelayProxyMiddleware,a as createRelayProxyServer,y as createRelayProxyWebSocketServer};
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../src/proxy/server.ts"],"sourcesContent":["/// <reference types=\"node\" />\n\nimport { createRelayProxyServer } from './relayProxyServer';\n\nconst host = process.env.NIPWORKER_PROXY_HOST || '127.0.0.1';\nconst portRaw = process.env.NIPWORKER_PROXY_PORT || '7777';\nconst path = process.env.NIPWORKER_PROXY_PATH || '/';\n\nconst port = Number.parseInt(portRaw, 10);\nif (!Number.isFinite(port) || port <= 0) {\n\tthrow new Error(`Invalid NIPWORKER_PROXY_PORT: ${portRaw}`);\n}\n\ncreateRelayProxyServer({ host, port, path });\n"],"names":["host","portRaw","path","port","createRelayProxyServer"],"mappings":";AAIA,MAAMA,IAAO,QAAQ,IAAI,wBAAwB,aAC3CC,IAAU,QAAQ,IAAI,wBAAwB,QAC9CC,IAAO,QAAQ,IAAI,wBAAwB,KAE3CC,IAAO,OAAO,SAASF,GAAS,EAAE;AACxC,IAAI,CAAC,OAAO,SAASE,CAAI,KAAKA,KAAQ;AACrC,QAAM,IAAI,MAAM,iCAAiCF,CAAO,EAAE;AAG3DG,EAAuB,EAAE,MAAAJ,GAAM,MAAAG,GAAM,MAAAD,GAAM;"}
1
+ {"version":3,"file":"server.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1 +1 @@
1
- import*as b from"flatbuffers";import{WebSocketServer as v,WebSocket as l}from"ws";import{R as C,W as m,M as h,e as E,N as B,d as R,S as N}from"./worker-message.js";function F(e={}){const r=e.host??"127.0.0.1",n=e.port??7777,o=e.path??"/",t=e.logger??console;let s;try{s=new v({host:r,port:n,path:o})}catch(c){throw t.error(`[relay-proxy] failed to create WebSocketServer: ${String(c)}`),c}let i=n;const a=s.address();return a&&typeof a=="object"&&(i=a.port),s.on("error",c=>{t.error(`[relay-proxy] WebSocketServer error: ${String(c)}`)}),s.on("connection",c=>{const f={relaySockets:new Map,pendingFrames:new Map,dedupBySubId:new Map,lastSubIdByRelay:new Map};c.on("message",(u,S)=>{if(S){const d=L(u);if(!d)return;T(f,c,d,t);return}const g=A(u);g&&_(f,g,t)}),c.on("close",()=>{I(f)}),c.on("error",()=>{I(f)})}),t.info(`[relay-proxy] listening on ws://${r}:${i}${o}`),{port:i,close:()=>new Promise((c,f)=>{s.close(u=>{if(u){f(u);return}c()})})}}function _(e,r,n){let o;try{o=JSON.parse(r)}catch{return}if(P(o)){const t=JSON.stringify(["AUTH",o.event]);O(e,o.relay,t,n);return}if(V(o)){e.dedupBySubId.delete(o.subscription_id);for(const[t,s]of e.relaySockets.entries())s.readyState===l.OPEN&&s.send(JSON.stringify(["CLOSE",o.subscription_id])),e.lastSubIdByRelay.set(t,o.subscription_id)}}function T(e,r,n,o){const t=n.frames.map(s=>({frame:s,state:W(s)}));for(const s of n.relays){M(e,r,s,o);for(const i of t){U(e,s,i.state);const a=i.frame;O(e,s,a,o)}}}function M(e,r,n,o){const t=e.relaySockets.get(n);if(t&&t.readyState!==l.CLOSED)return;const s=new l(n);e.relaySockets.set(n,s),e.pendingFrames.set(n,[]),s.on("open",()=>{const i=e.pendingFrames.get(n);if(i){for(const a of i)s.send(a);e.pendingFrames.set(n,[])}}),s.on("message",i=>{const a=A(i);if(!a||r.readyState!==l.OPEN)return;const c=e.lastSubIdByRelay.get(n),f=$(e,n,a,c);f&&r.send(f,{binary:!0})}),s.on("close",()=>{e.pendingFrames.delete(n)}),s.on("error",i=>{if(!e.pendingFrames.has(n))return;const a=String(i);!a.includes("ECONNREFUSED")&&!a.includes("ENOTFOUND")&&o.warn(`[relay-proxy] relay socket error for ${n}: ${a.slice(0,100)}`),e.pendingFrames.delete(n)})}function U(e,r,n){if(n){if(e.lastSubIdByRelay.set(r,n.subId),n.type==="REQ"){const o=n.subId;e.dedupBySubId.has(o)||e.dedupBySubId.set(o,new Set);return}e.dedupBySubId.delete(n.subId)}}function W(e){const r=w(e);if(!r)return null;const n=r[0];if(n!=="REQ"&&n!=="CLOSE")return null;const o=typeof r[1]=="string"?r[1]:null;return o?{type:n,subId:o}:null}function O(e,r,n,o){const t=e.relaySockets.get(r);if(t){if(t.readyState===l.OPEN){t.send(n);return}if(t.readyState===l.CONNECTING){const s=e.pendingFrames.get(r)??[];s.push(n),e.pendingFrames.set(r,s);return}o.warn(`[relay-proxy] dropping frame for closed relay socket ${r}`)}}function $(e,r,n,o){const t=w(n);if(!t||t.length<1||typeof t[0]!="string")return k(o??"",r,n);const s=t[0];if(s==="EVENT"){const i=typeof t[1]=="string"?t[1]:"",a=D(t[2]);if(!i||!a)return k(i,r,n);const c=e.dedupBySubId.get(i);return c&&c.has(a.id)?null:(c&&c.add(a.id),x(i,r,a))}if(s==="NOTICE"){const i=t[1]===void 0?null:String(t[1]);return y("",r,"NOTICE",i)}if(s==="AUTH"){const i=t[1]===void 0?null:String(t[1]);return y(o??"",r,"AUTH",i)}if(s==="CLOSED"){const i=typeof t[1]=="string"?t[1]:"",a=t[2]===void 0?null:String(t[2]);return y(i,r,"CLOSED",a)}if(s==="OK"){const i=typeof t[1]=="string"?t[1]:"",a=t[2]===void 0?"false":String(t[2]),c=t[3]===void 0?null:String(t[3]);return y(i,r,a,c)}if(s==="EOSE"){const i=typeof t[1]=="string"?t[1]:"";return y(i,r,"EOSE",null)}return k(o??"",r,n)}function x(e,r,n){const o=new b.Builder(1024),t=e?o.createString(e):0,s=o.createString(r),i=o.createString(n.id),a=o.createString(n.pubkey),c=o.createString(n.content),f=o.createString(n.sig),u=new Array(n.tags.length);for(let p=0;p<n.tags.length;p++)u[p]=J(o,n.tags[p]);const S=B.createTagsVector(o,u),g=B.createNostrEvent(o,i,a,n.kind,c,S,n.created_at,f),d=m.createWorkerMessage(o,t,s,h.NostrEvent,E.NostrEvent,g);return o.finish(d),o.asUint8Array()}function y(e,r,n,o){const t=new b.Builder(256),s=e?t.createString(e):0,i=t.createString(r),a=t.createString(n),c=o===null?0:t.createString(o),f=R.createConnectionStatus(t,i,a,c),u=m.createWorkerMessage(t,s,i,h.ConnectionStatus,E.ConnectionStatus,f);return t.finish(u),t.asUint8Array()}function k(e,r,n){const o=new b.Builder(256),t=e?o.createString(e):0,s=o.createString(r),i=o.createString(n),a=C.createRaw(o,i),c=m.createWorkerMessage(o,t,s,h.Raw,E.Raw,a);return o.finish(c),o.asUint8Array()}function D(e){if(!e||typeof e!="object")return null;const r=e;if(typeof r.id!="string"||typeof r.pubkey!="string"||typeof r.kind!="number"||typeof r.content!="string"||typeof r.created_at!="number"||typeof r.sig!="string"||!Array.isArray(r.tags))return null;const n=r.tags;let o=!1;for(const s of n){if(!Array.isArray(s)||s.length===0){o=!0;continue}for(const i of s)if(typeof i!="string"){o=!0;break}}let t;if(!o)t=n;else{t=[];for(const s of n){if(!Array.isArray(s))continue;const i=[];for(const a of s)typeof a=="string"&&i.push(a);i.length>0&&t.push(i)}}return{id:r.id,pubkey:r.pubkey,kind:r.kind,content:r.content,created_at:r.created_at,sig:r.sig,tags:t}}function J(e,r){const n=new Array(r.length);for(let t=0;t<r.length;t++)n[t]=e.createString(r[t]);const o=N.createItemsVector(e,n);return N.createStringVec(e,o)}function w(e){try{const r=JSON.parse(e);return Array.isArray(r)?r:null}catch{return null}}function L(e){const r=A(e);if(!r)return null;try{const n=JSON.parse(r);if(!Array.isArray(n.relays)||!Array.isArray(n.frames))return null;const o=n.relays.filter(s=>typeof s=="string"),t=n.frames.filter(s=>typeof s=="string");return o.length===0||t.length===0?null:{relays:o,frames:t}}catch{return null}}function A(e){return typeof e=="string"?e:e instanceof ArrayBuffer?Buffer.from(e).toString("utf8"):Buffer.isBuffer(e)?e.toString("utf8"):e instanceof Uint8Array?Buffer.from(e).toString("utf8"):Array.isArray(e)?Buffer.concat(e.filter(r=>Buffer.isBuffer(r))).toString("utf8"):""}function P(e){if(!e||typeof e!="object")return!1;const r=e;return r.type==="auth_response"&&typeof r.relay=="string"&&r.event!==void 0}function V(e){if(!e||typeof e!="object")return!1;const r=e;return r.type==="close_sub"&&typeof r.subscription_id=="string"}function I(e){e.dedupBySubId.clear(),e.lastSubIdByRelay.clear();for(const r of e.relaySockets.values())try{r.close()}catch{}e.relaySockets.clear(),e.pendingFrames.clear()}export{F as createRelayProxyServer};
1
+ import*as B from"flatbuffers";import{WebSocketServer as w,WebSocket as S}from"ws";import{R as C,W as I,M as v,e as A,N as O,d as P,S as R}from"./worker-message.js";function T(e={}){const n=e.host??"127.0.0.1",s=e.port??7777,o=e.path??"/",t=e.logger??console;let r;try{r=new w({host:n,port:s,path:o})}catch(c){throw t.error(`[relay-proxy] failed to create WebSocketServer: ${String(c)}`),c}let i=s;const a=r.address();return a&&typeof a=="object"&&(i=a.port),r.on("error",c=>{t.error(`[relay-proxy] WebSocketServer error: ${String(c)}`)}),r.on("connection",c=>{const u={relaySockets:new Map,pendingFrames:new Map,dedupBySubId:new Map,lastSubIdByRelay:new Map};c.on("message",(f,l)=>{if(l){const d=k(f);if(!d)return;E(u,c,d,t);return}const g=b(f);g&&m(u,g,t)}),c.on("close",()=>{y(u)}),c.on("error",()=>{y(u)})}),t.info(`[relay-proxy] listening on ws://${n}:${i}${o}`),{port:i,close:()=>new Promise((c,u)=>{r.close(f=>{if(f){u(f);return}c()})})}}function U(e){const n=e.path??"/",s=e.logger??console,o=new w({noServer:!0}),t=new Map;return o.on("connection",r=>{const i={relaySockets:new Map,pendingFrames:new Map,dedupBySubId:new Map,lastSubIdByRelay:new Map};t.set(r,i),r.on("message",(a,c)=>{const u=t.get(r);if(!u)return;if(c){const l=k(a);if(!l)return;E(u,r,l,s);return}const f=b(a);f&&m(u,f,s)}),r.on("close",()=>{const a=t.get(r);a&&(y(a),t.delete(r))}),r.on("error",()=>{const a=t.get(r);a&&(y(a),t.delete(r))})}),{wss:o,close:()=>new Promise((r,i)=>{t.forEach(a=>y(a)),t.clear(),o.close(a=>{if(a){i(a);return}r()})}),handleUpgrade:(r,i,a)=>{(r.url??"").startsWith(n)&&o.handleUpgrade(r,i,a,c=>{o.emit("connection",c,r)})}}}function W(e){const{server:n,path:s="/",logger:o=console}=e,t=new w({server:n,path:s});return t.on("connection",r=>{const i={relaySockets:new Map,pendingFrames:new Map,dedupBySubId:new Map,lastSubIdByRelay:new Map};r.on("message",(a,c)=>{if(c){const f=k(a);if(!f)return;E(i,r,f,o);return}const u=b(a);u&&m(i,u,o)}),r.on("close",()=>{y(i)}),r.on("error",()=>{y(i)})}),o.info(`[relay-proxy] attached to server at path: ${s}`),{close:()=>new Promise((r,i)=>{t.close(a=>{if(a){i(a);return}r()})})}}function _(e,n){const s=n.path??"/ws-proxy",o=n.logger??console;let t=null;const r=new Map,i=e.listen.bind(e);return e.listen=(...a)=>{const c=i(...a);return t=new w({server:c,path:s}),t.on("connection",u=>{const f={relaySockets:new Map,pendingFrames:new Map,dedupBySubId:new Map,lastSubIdByRelay:new Map};r.set(u,f),u.on("message",(l,g)=>{const d=r.get(u);if(!d)return;if(g){const N=k(l);if(!N)return;E(d,u,N,o);return}const p=b(l);p&&m(d,p,o)}),u.on("close",()=>{const l=r.get(u);l&&(y(l),r.delete(u))}),u.on("error",()=>{const l=r.get(u);l&&(y(l),r.delete(u))})}),o.info(`[relay-proxy] attached to Express server at path: ${s}`),c},{close:()=>new Promise((a,c)=>{if(!t){a();return}r.forEach(u=>y(u)),r.clear(),t.close(u=>{if(u){c(u);return}a()})})}}function m(e,n,s){let o;try{o=JSON.parse(n)}catch{return}if(Q(o)){const t=JSON.stringify(["AUTH",o.event]);x(e,o.relay,t,s);return}if(G(o)){e.dedupBySubId.delete(o.subscription_id);for(const[t,r]of e.relaySockets.entries())r.readyState===S.OPEN&&r.send(JSON.stringify(["CLOSE",o.subscription_id])),e.lastSubIdByRelay.set(t,o.subscription_id)}}function E(e,n,s,o){const t=s.frames.map(r=>({frame:r,state:J(r)}));for(const r of s.relays){$(e,n,r,o);for(const i of t){D(e,r,i.state);const a=i.frame;x(e,r,a,o)}}}function $(e,n,s,o){const t=e.relaySockets.get(s);if(t&&t.readyState!==S.CLOSED)return;const r=new S(s);e.relaySockets.set(s,r),e.pendingFrames.set(s,[]),r.on("open",()=>{const i=e.pendingFrames.get(s);if(i){for(const a of i)r.send(a);e.pendingFrames.set(s,[])}}),r.on("message",i=>{const a=b(i);if(!a||n.readyState!==S.OPEN)return;const c=e.lastSubIdByRelay.get(s),u=L(e,s,a,c);u&&n.send(u,{binary:!0})}),r.on("close",()=>{e.pendingFrames.delete(s)}),r.on("error",i=>{if(!e.pendingFrames.has(s))return;const a=String(i);!a.includes("ECONNREFUSED")&&!a.includes("ENOTFOUND")&&o.warn(`[relay-proxy] relay socket error for ${s}: ${a.slice(0,100)}`),e.pendingFrames.delete(s)})}function D(e,n,s){if(s){if(e.lastSubIdByRelay.set(n,s.subId),s.type==="REQ"){const o=s.subId;e.dedupBySubId.has(o)||e.dedupBySubId.set(o,new Set);return}e.dedupBySubId.delete(s.subId)}}function J(e){const n=F(e);if(!n)return null;const s=n[0];if(s!=="REQ"&&s!=="CLOSE")return null;const o=typeof n[1]=="string"?n[1]:null;return o?{type:s,subId:o}:null}function x(e,n,s,o){const t=e.relaySockets.get(n);if(t){if(t.readyState===S.OPEN){t.send(s);return}if(t.readyState===S.CONNECTING){const r=e.pendingFrames.get(n)??[];r.push(s),e.pendingFrames.set(n,r);return}o.warn(`[relay-proxy] dropping frame for closed relay socket ${n}`)}}function L(e,n,s,o){const t=F(s);if(!t||t.length<1||typeof t[0]!="string")return M(o??"",n,s);const r=t[0];if(r==="EVENT"){const i=typeof t[1]=="string"?t[1]:"",a=V(t[2]);if(!i||!a)return M(i,n,s);const c=e.dedupBySubId.get(i);return c&&c.has(a.id)?null:(c&&c.add(a.id),j(i,n,a))}if(r==="NOTICE"){const i=t[1]===void 0?null:String(t[1]);return h("",n,"NOTICE",i)}if(r==="AUTH"){const i=t[1]===void 0?null:String(t[1]);return h(o??"",n,"AUTH",i)}if(r==="CLOSED"){const i=typeof t[1]=="string"?t[1]:"",a=t[2]===void 0?null:String(t[2]);return h(i,n,"CLOSED",a)}if(r==="OK"){const i=typeof t[1]=="string"?t[1]:"",a=t[2]===void 0?"false":String(t[2]),c=t[3]===void 0?null:String(t[3]);return h(i,n,a,c)}if(r==="EOSE"){const i=typeof t[1]=="string"?t[1]:"";return h(i,n,"EOSE",null)}return M(o??"",n,s)}function j(e,n,s){const o=new B.Builder(1024),t=e?o.createString(e):0,r=o.createString(n),i=o.createString(s.id),a=o.createString(s.pubkey),c=o.createString(s.content),u=o.createString(s.sig),f=new Array(s.tags.length);for(let p=0;p<s.tags.length;p++)f[p]=H(o,s.tags[p]);const l=O.createTagsVector(o,f),g=O.createNostrEvent(o,i,a,s.kind,c,l,s.created_at,u),d=I.createWorkerMessage(o,t,r,v.NostrEvent,A.NostrEvent,g);return o.finish(d),o.asUint8Array()}function h(e,n,s,o){const t=new B.Builder(256),r=e?t.createString(e):0,i=t.createString(n),a=t.createString(s),c=o===null?0:t.createString(o),u=P.createConnectionStatus(t,i,a,c),f=I.createWorkerMessage(t,r,i,v.ConnectionStatus,A.ConnectionStatus,u);return t.finish(f),t.asUint8Array()}function M(e,n,s){const o=new B.Builder(256),t=e?o.createString(e):0,r=o.createString(n),i=o.createString(s),a=C.createRaw(o,i),c=I.createWorkerMessage(o,t,r,v.Raw,A.Raw,a);return o.finish(c),o.asUint8Array()}function V(e){if(!e||typeof e!="object")return null;const n=e;if(typeof n.id!="string"||typeof n.pubkey!="string"||typeof n.kind!="number"||typeof n.content!="string"||typeof n.created_at!="number"||typeof n.sig!="string"||!Array.isArray(n.tags))return null;const s=n.tags;let o=!1;for(const r of s){if(!Array.isArray(r)||r.length===0){o=!0;continue}for(const i of r)if(typeof i!="string"){o=!0;break}}let t;if(!o)t=s;else{t=[];for(const r of s){if(!Array.isArray(r))continue;const i=[];for(const a of r)typeof a=="string"&&i.push(a);i.length>0&&t.push(i)}}return{id:n.id,pubkey:n.pubkey,kind:n.kind,content:n.content,created_at:n.created_at,sig:n.sig,tags:t}}function H(e,n){const s=new Array(n.length);for(let t=0;t<n.length;t++)s[t]=e.createString(n[t]);const o=R.createItemsVector(e,s);return R.createStringVec(e,o)}function F(e){try{const n=JSON.parse(e);return Array.isArray(n)?n:null}catch{return null}}function k(e){const n=b(e);if(!n)return null;try{const s=JSON.parse(n);if(!Array.isArray(s.relays)||!Array.isArray(s.frames))return null;const o=s.relays.filter(r=>typeof r=="string"),t=s.frames.filter(r=>typeof r=="string");return o.length===0||t.length===0?null:{relays:o,frames:t}}catch{return null}}function b(e){return typeof e=="string"?e:e instanceof ArrayBuffer?Buffer.from(e).toString("utf8"):Buffer.isBuffer(e)?e.toString("utf8"):e instanceof Uint8Array?Buffer.from(e).toString("utf8"):Array.isArray(e)?Buffer.concat(e.filter(n=>Buffer.isBuffer(n))).toString("utf8"):""}function Q(e){if(!e||typeof e!="object")return!1;const n=e;return n.type==="auth_response"&&typeof n.relay=="string"&&n.event!==void 0}function G(e){if(!e||typeof e!="object")return!1;const n=e;return n.type==="close_sub"&&typeof n.subscription_id=="string"}function y(e){e.dedupBySubId.clear(),e.lastSubIdByRelay.clear();for(const n of e.relaySockets.values())try{n.close()}catch{}e.relaySockets.clear(),e.pendingFrames.clear()}export{W as attachRelayProxyToServer,_ as createExpressRelayProxyMiddleware,T as createRelayProxyServer,U as createRelayProxyWebSocketServer};
@@ -1 +1 @@
1
- {"version":3,"file":"relayProxyServer.js","sources":["../src/proxy/relayProxyServer.ts"],"sourcesContent":["/// <reference types=\"node\" />\n\nimport type { Server as HttpServer } from 'http';\nimport type { Server as HttpsServer } from 'https';\nimport type { IncomingMessage } from 'http';\nimport type { Duplex } from 'stream';\nimport * as flatbuffers from 'flatbuffers';\nimport { WebSocket, WebSocketServer } from 'ws';\nimport {\n\tConnectionStatus,\n\tMessage,\n\tMessageType,\n\tNostrEvent,\n\tRaw,\n\tStringVec,\n\tWorkerMessage\n} from '../generated/nostr/fb';\n\ntype RelayProxyServerLogger = Pick<Console, 'info' | 'warn' | 'error'>;\n\nexport type RelayProxyServerOptions = {\n\thost?: string;\n\tport?: number;\n\tpath?: string;\n\tlogger?: RelayProxyServerLogger;\n};\n\nexport type RelayProxyServer = {\n\tport: number;\n\tclose: () => Promise<void>;\n};\n\nexport type AttachRelayProxyOptions = {\n\t/** The HTTP/HTTPS server to attach to */\n\tserver: HttpServer | HttpsServer;\n\t/** The path to mount the WebSocket endpoint on (e.g., '/ws-proxy') */\n\tpath?: string;\n\tlogger?: RelayProxyServerLogger;\n};\n\nexport type AttachedRelayProxy = {\n\t/** Stop accepting new connections and close all existing sessions */\n\tclose: () => Promise<void>;\n};\n\nexport type WebSocketRelayProxy = {\n\twss: WebSocketServer;\n\tclose: () => Promise<void>;\n\t/** Manually handle a WebSocket upgrade request */\n\thandleUpgrade: (request: IncomingMessage, socket: Duplex, head: Buffer) => void;\n};\n\ntype Envelope = {\n\trelays: string[];\n\tframes: string[];\n};\n\ntype AuthResponseCommand = {\n\ttype: 'auth_response';\n\trelay: string;\n\tevent: unknown;\n};\n\ntype CloseSubCommand = {\n\ttype: 'close_sub';\n\tsubscription_id: string;\n};\n\ntype NostrEventJson = {\n\tid: string;\n\tpubkey: string;\n\tkind: number;\n\tcontent: string;\n\ttags: string[][];\n\tcreated_at: number;\n\tsig: string;\n};\n\ntype Session = {\n\trelaySockets: Map<string, WebSocket>;\n\tpendingFrames: Map<string, string[]>;\n\tdedupBySubId: Map<string, Set<string>>;\n\tlastSubIdByRelay: Map<string, string>;\n};\n\ntype SubscriptionFrameState = {\n\ttype: 'REQ' | 'CLOSE';\n\tsubId: string;\n} | null;\n\n/**\n * Create a standalone relay proxy server on its own port.\n * Use this for simple deployments or when you don't have an existing HTTP server.\n */\nexport function createRelayProxyServer(options: RelayProxyServerOptions = {}): RelayProxyServer {\n\tconst host = options.host ?? '127.0.0.1';\n\tconst port = options.port ?? 7777;\n\tconst path = options.path ?? '/';\n\tconst logger = options.logger ?? console;\n\n\tlet wss: WebSocketServer;\n\ttry {\n\t\twss = new WebSocketServer({\n\t\t\thost,\n\t\t\tport,\n\t\t\tpath\n\t\t});\n\t} catch (err) {\n\t\tlogger.error(`[relay-proxy] failed to create WebSocketServer: ${String(err)}`);\n\t\tthrow err;\n\t}\n\n\t// Get the actual port (in case port 0 was passed)\n\t// If the server is not yet listening, wss.address() returns null\n\t// In that case, wait for the 'listening' event\n\tlet actualPort = port;\n\tconst address = wss.address();\n\tif (address && typeof address === 'object') {\n\t\tactualPort = address.port;\n\t}\n\n\twss.on('error', (err) => {\n\t\tlogger.error(`[relay-proxy] WebSocketServer error: ${String(err)}`);\n\t});\n\n\twss.on('connection', (clientSocket) => {\n\t\tconst session: Session = {\n\t\t\trelaySockets: new Map(),\n\t\t\tpendingFrames: new Map(),\n\t\t\tdedupBySubId: new Map(),\n\t\t\tlastSubIdByRelay: new Map()\n\t\t};\n\n\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\tif (isBinary) {\n\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\tif (!envelope) return;\n\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst text = toUtf8(data);\n\t\t\tif (!text) return;\n\t\t\thandleClientCommand(session, text, logger);\n\t\t});\n\n\t\tclientSocket.on('close', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\n\t\tclientSocket.on('error', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\t});\n\n\tlogger.info(`[relay-proxy] listening on ws://${host}:${actualPort}${path}`);\n\n\treturn {\n\t\tport: actualPort,\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t})\n\t};\n}\n\n/**\n * Create a WebSocket relay proxy that supports manual upgrade handling.\n * Use this for Vite development servers where SvelteKit middleware might interfere\n * with the standard WebSocket upgrade mechanism.\n *\n * @example\n * // In a Vite plugin\n * const relayProxy = createRelayProxyWebSocketServer({ path: '/ws-proxy' });\n *\n * // In configureServer middleware\n * server.middlewares.use((req, res, next) => {\n * if (req.url?.startsWith('/ws-proxy') && req.headers.upgrade === 'websocket') {\n * server.httpServer.once('upgrade', (request, socket, head) => {\n * if (request.url?.startsWith('/ws-proxy')) {\n * relayProxy.handleUpgrade(request, socket, head);\n * }\n * });\n * }\n * next();\n * });\n */\nexport function createRelayProxyWebSocketServer(\n\toptions: Omit<AttachRelayProxyOptions, 'server'> & { server?: HttpServer | HttpsServer }\n): WebSocketRelayProxy {\n\tconst path = options.path ?? '/';\n\tconst logger = options.logger ?? console;\n\n\t// Create WebSocket server with noServer mode to handle upgrades manually\n\tconst wss = new WebSocketServer({\n\t\tnoServer: true\n\t});\n\n\tconst sessions = new Map<WebSocket, Session>();\n\n\twss.on('connection', (clientSocket) => {\n\t\tconst session: Session = {\n\t\t\trelaySockets: new Map(),\n\t\t\tpendingFrames: new Map(),\n\t\t\tdedupBySubId: new Map(),\n\t\t\tlastSubIdByRelay: new Map()\n\t\t};\n\t\tsessions.set(clientSocket, session);\n\n\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\tconst session = sessions.get(clientSocket);\n\t\t\tif (!session) return;\n\n\t\t\tif (isBinary) {\n\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\tif (!envelope) return;\n\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst text = toUtf8(data);\n\t\t\tif (!text) return;\n\t\t\thandleClientCommand(session, text, logger);\n\t\t});\n\n\t\tclientSocket.on('close', () => {\n\t\t\tconst session = sessions.get(clientSocket);\n\t\t\tif (session) {\n\t\t\t\tcloseSession(session);\n\t\t\t\tsessions.delete(clientSocket);\n\t\t\t}\n\t\t});\n\n\t\tclientSocket.on('error', () => {\n\t\t\tconst session = sessions.get(clientSocket);\n\t\t\tif (session) {\n\t\t\t\tcloseSession(session);\n\t\t\t\tsessions.delete(clientSocket);\n\t\t\t}\n\t\t});\n\t});\n\n\tconst handleUpgrade = (request: IncomingMessage, socket: Duplex, head: Buffer) => {\n\t\t// Verify path matches\n\t\tconst url = request.url ?? '';\n\t\tif (!url.startsWith(path)) {\n\t\t\treturn;\n\t\t}\n\n\t\twss.handleUpgrade(request, socket, head, (ws) => {\n\t\t\twss.emit('connection', ws, request);\n\t\t});\n\t};\n\n\treturn {\n\t\twss,\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\t// Close all sessions first\n\t\t\t\tsessions.forEach((session) => closeSession(session));\n\t\t\t\tsessions.clear();\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t}),\n\t\thandleUpgrade\n\t};\n}\n\n/**\n * Attach the relay proxy to an existing HTTP/HTTPS server.\n * Use this for embedding in SvelteKit (adapter-node), Express, or any Node.js server.\n *\n * @example\n * // SvelteKit with adapter-node\n * import { createServer } from 'http';\n * import { handler } from './build/handler.js';\n * import { attachRelayProxyToServer } from '@candypoets/nipworker/proxy/server';\n *\n * const server = createServer(handler);\n * attachRelayProxyToServer({ server, path: '/ws-proxy' });\n * server.listen(3000);\n */\nexport function attachRelayProxyToServer(options: AttachRelayProxyOptions): AttachedRelayProxy {\n\tconst { server, path = '/', logger = console } = options;\n\n\tconst wss = new WebSocketServer({\n\t\tserver,\n\t\tpath\n\t});\n\n\twss.on('connection', (clientSocket) => {\n\t\tconst session: Session = {\n\t\t\trelaySockets: new Map(),\n\t\t\tpendingFrames: new Map(),\n\t\t\tdedupBySubId: new Map(),\n\t\t\tlastSubIdByRelay: new Map()\n\t\t};\n\n\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\tif (isBinary) {\n\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\tif (!envelope) return;\n\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst text = toUtf8(data);\n\t\t\tif (!text) return;\n\t\t\thandleClientCommand(session, text, logger);\n\t\t});\n\n\t\tclientSocket.on('close', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\n\t\tclientSocket.on('error', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\t});\n\n\tlogger.info(`[relay-proxy] attached to server at path: ${path}`);\n\n\treturn {\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t})\n\t};\n}\n\n/**\n * Create an Express middleware that attaches the relay proxy to the Express server's underlying HTTP server.\n * Call this after setting up your Express app but before calling app.listen().\n *\n * @example\n * import express from 'express';\n * import { createExpressRelayProxyMiddleware } from '@candypoets/nipworker/proxy/server';\n *\n * const app = express();\n *\n * // Your Express routes...\n * app.get('/api/health', (req, res) => res.json({ ok: true }));\n *\n * // Attach relay proxy\n * const relayProxy = createExpressRelayProxyMiddleware(app, { path: '/ws-proxy' });\n *\n * const server = app.listen(3000, () => {\n * console.log('Server with relay proxy running on port 3000');\n * });\n *\n * // Cleanup on shutdown\n * process.on('SIGTERM', async () => {\n * await relayProxy.close();\n * server.close();\n * });\n */\nexport function createExpressRelayProxyMiddleware<\n\tT extends { listen: (...args: any[]) => HttpServer | HttpsServer }\n>(app: T, options: Omit<AttachRelayProxyOptions, 'server'>): AttachedRelayProxy {\n\tconst path = options.path ?? '/ws-proxy';\n\tconst logger = options.logger ?? console;\n\n\t// Store reference to the server once it's created\n\tlet wss: WebSocketServer | null = null;\n\tconst sessions = new Map<WebSocket, Session>();\n\n\t// Monkey-patch app.listen to capture the server instance\n\tconst originalListen = app.listen.bind(app);\n\t(app as any).listen = (...args: any[]) => {\n\t\tconst server = originalListen(...args);\n\n\t\twss = new WebSocketServer({\n\t\t\tserver,\n\t\t\tpath\n\t\t});\n\n\t\twss.on('connection', (clientSocket) => {\n\t\t\tconst session: Session = {\n\t\t\t\trelaySockets: new Map(),\n\t\t\t\tpendingFrames: new Map(),\n\t\t\t\tdedupBySubId: new Map(),\n\t\t\t\tlastSubIdByRelay: new Map()\n\t\t\t};\n\t\t\tsessions.set(clientSocket, session);\n\n\t\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\t\tconst session = sessions.get(clientSocket);\n\t\t\t\tif (!session) return;\n\n\t\t\t\tif (isBinary) {\n\t\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\t\tif (!envelope) return;\n\t\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst text = toUtf8(data);\n\t\t\t\tif (!text) return;\n\t\t\t\thandleClientCommand(session, text, logger);\n\t\t\t});\n\n\t\t\tclientSocket.on('close', () => {\n\t\t\t\tconst session = sessions.get(clientSocket);\n\t\t\t\tif (session) {\n\t\t\t\t\tcloseSession(session);\n\t\t\t\t\tsessions.delete(clientSocket);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tclientSocket.on('error', () => {\n\t\t\t\tconst session = sessions.get(clientSocket);\n\t\t\t\tif (session) {\n\t\t\t\t\tcloseSession(session);\n\t\t\t\t\tsessions.delete(clientSocket);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tlogger.info(`[relay-proxy] attached to Express server at path: ${path}`);\n\n\t\treturn server;\n\t};\n\n\treturn {\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\tif (!wss) {\n\t\t\t\t\tresolve();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Close all sessions first\n\t\t\t\tsessions.forEach((session) => closeSession(session));\n\t\t\t\tsessions.clear();\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t})\n\t};\n}\n\nfunction handleClientCommand(session: Session, text: string, logger: RelayProxyServerLogger) {\n\tlet command: unknown;\n\ttry {\n\t\tcommand = JSON.parse(text);\n\t} catch {\n\t\treturn;\n\t}\n\n\tif (isAuthResponseCommand(command)) {\n\t\tconst frame = JSON.stringify(['AUTH', command.event]);\n\t\tsendFrameToRelay(session, command.relay, frame, logger);\n\t\treturn;\n\t}\n\n\tif (isCloseSubCommand(command)) {\n\t\tsession.dedupBySubId.delete(command.subscription_id);\n\t\tfor (const [relayUrl, relaySocket] of session.relaySockets.entries()) {\n\t\t\tif (relaySocket.readyState === WebSocket.OPEN) {\n\t\t\t\trelaySocket.send(JSON.stringify(['CLOSE', command.subscription_id]));\n\t\t\t}\n\t\t\tsession.lastSubIdByRelay.set(relayUrl, command.subscription_id);\n\t\t}\n\t}\n}\n\nfunction handleEnvelope(\n\tsession: Session,\n\tclientSocket: WebSocket,\n\tenvelope: Envelope,\n\tlogger: RelayProxyServerLogger\n) {\n\tconst trackedFrames = envelope.frames.map((frame) => ({\n\t\tframe,\n\t\tstate: parseSubscriptionFrameState(frame)\n\t}));\n\n\tfor (const relay of envelope.relays) {\n\t\tensureRelaySocket(session, clientSocket, relay, logger);\n\t\tfor (const tracked of trackedFrames) {\n\t\t\ttrackSubscriptionState(session, relay, tracked.state);\n\t\t\tconst frame = tracked.frame;\n\t\t\tsendFrameToRelay(session, relay, frame, logger);\n\t\t}\n\t}\n}\n\nfunction ensureRelaySocket(\n\tsession: Session,\n\tclientSocket: WebSocket,\n\trelayUrl: string,\n\tlogger: RelayProxyServerLogger\n) {\n\tconst existing = session.relaySockets.get(relayUrl);\n\tif (existing && existing.readyState !== WebSocket.CLOSED) return;\n\n\tconst upstream = new WebSocket(relayUrl);\n\tsession.relaySockets.set(relayUrl, upstream);\n\tsession.pendingFrames.set(relayUrl, []);\n\n\tupstream.on('open', () => {\n\t\tconst pending = session.pendingFrames.get(relayUrl);\n\t\tif (!pending) return;\n\t\tfor (const frame of pending) {\n\t\t\tupstream.send(frame);\n\t\t}\n\t\tsession.pendingFrames.set(relayUrl, []);\n\t});\n\n\tupstream.on('message', (data) => {\n\t\tconst raw = toUtf8(data);\n\t\tif (!raw || clientSocket.readyState !== WebSocket.OPEN) return;\n\t\tconst subIdHint = session.lastSubIdByRelay.get(relayUrl);\n\t\tconst workerMessage = relayFrameToWorkerMessage(session, relayUrl, raw, subIdHint);\n\t\tif (!workerMessage) return;\n\t\tclientSocket.send(workerMessage, { binary: true });\n\t});\n\n\tupstream.on('close', () => {\n\t\t// Mark as closed but don't delete - ensureRelaySocket will reconnect on next use\n\t\t// This keeps subscriptions alive across reconnects\n\t\tsession.pendingFrames.delete(relayUrl);\n\t});\n\n\tupstream.on('error', (err) => {\n\t\t// Only log the first error per relay to reduce spam\n\t\tif (!session.pendingFrames.has(relayUrl)) return;\n\t\tconst errorMsg = String(err);\n\t\t// Skip common repetitive errors\n\t\tif (!errorMsg.includes('ECONNREFUSED') && !errorMsg.includes('ENOTFOUND')) {\n\t\t\tlogger.warn(`[relay-proxy] relay socket error for ${relayUrl}: ${errorMsg.slice(0, 100)}`);\n\t\t}\n\t\tsession.pendingFrames.delete(relayUrl);\n\t});\n}\n\nfunction trackSubscriptionState(session: Session, relayUrl: string, state: SubscriptionFrameState) {\n\tif (!state) return;\n\tsession.lastSubIdByRelay.set(relayUrl, state.subId);\n\n\tif (state.type === 'REQ') {\n\t\tconst subId = state.subId;\n\t\tif (!session.dedupBySubId.has(subId)) {\n\t\t\tsession.dedupBySubId.set(subId, new Set());\n\t\t}\n\t\treturn;\n\t}\n\n\tsession.dedupBySubId.delete(state.subId);\n}\n\nfunction parseSubscriptionFrameState(frame: string): SubscriptionFrameState {\n\tconst parsed = parseRelayFrame(frame);\n\tif (!parsed) return null;\n\tconst type = parsed[0];\n\tif (type !== 'REQ' && type !== 'CLOSE') return null;\n\n\tconst subId = typeof parsed[1] === 'string' ? parsed[1] : null;\n\tif (!subId) return null;\n\treturn { type, subId };\n}\n\nfunction sendFrameToRelay(\n\tsession: Session,\n\trelayUrl: string,\n\tframe: string,\n\tlogger: RelayProxyServerLogger\n) {\n\tconst relaySocket = session.relaySockets.get(relayUrl);\n\tif (!relaySocket) return;\n\n\tif (relaySocket.readyState === WebSocket.OPEN) {\n\t\trelaySocket.send(frame);\n\t\treturn;\n\t}\n\n\tif (relaySocket.readyState === WebSocket.CONNECTING) {\n\t\tconst pending = session.pendingFrames.get(relayUrl) ?? [];\n\t\tpending.push(frame);\n\t\tsession.pendingFrames.set(relayUrl, pending);\n\t\treturn;\n\t}\n\n\tlogger.warn(`[relay-proxy] dropping frame for closed relay socket ${relayUrl}`);\n}\n\nfunction relayFrameToWorkerMessage(\n\tsession: Session,\n\trelayUrl: string,\n\trawFrame: string,\n\tsubIdHint?: string\n): Uint8Array | null {\n\tconst frame = parseRelayFrame(rawFrame);\n\tif (!frame || frame.length < 1 || typeof frame[0] !== 'string') {\n\t\treturn buildRawWorkerMessage(subIdHint ?? '', relayUrl, rawFrame);\n\t}\n\n\tconst kind = frame[0];\n\tif (kind === 'EVENT') {\n\t\tconst subId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\tconst event = asNostrEvent(frame[2]);\n\t\tif (!subId || !event) {\n\t\t\treturn buildRawWorkerMessage(subId, relayUrl, rawFrame);\n\t\t}\n\n\t\tconst dedupSet = session.dedupBySubId.get(subId);\n\t\tif (dedupSet && dedupSet.has(event.id)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (dedupSet) dedupSet.add(event.id);\n\n\t\treturn buildNostrEventWorkerMessage(subId, relayUrl, event);\n\t}\n\n\tif (kind === 'NOTICE') {\n\t\tconst message = frame[1] === undefined ? null : String(frame[1]);\n\t\treturn buildConnectionStatusWorkerMessage('', relayUrl, 'NOTICE', message);\n\t}\n\n\tif (kind === 'AUTH') {\n\t\tconst challenge = frame[1] === undefined ? null : String(frame[1]);\n\t\treturn buildConnectionStatusWorkerMessage(subIdHint ?? '', relayUrl, 'AUTH', challenge);\n\t}\n\n\tif (kind === 'CLOSED') {\n\t\tconst subId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\tconst message = frame[2] === undefined ? null : String(frame[2]);\n\t\treturn buildConnectionStatusWorkerMessage(subId, relayUrl, 'CLOSED', message);\n\t}\n\n\tif (kind === 'OK') {\n\t\tconst eventId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\tconst accepted = frame[2] === undefined ? 'false' : String(frame[2]);\n\t\tconst reason = frame[3] === undefined ? null : String(frame[3]);\n\t\treturn buildConnectionStatusWorkerMessage(eventId, relayUrl, accepted, reason);\n\t}\n\n\tif (kind === 'EOSE') {\n\t\tconst subId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\treturn buildConnectionStatusWorkerMessage(subId, relayUrl, 'EOSE', null);\n\t}\n\n\treturn buildRawWorkerMessage(subIdHint ?? '', relayUrl, rawFrame);\n}\n\nfunction buildNostrEventWorkerMessage(subId: string, relayUrl: string, event: NostrEventJson): Uint8Array {\n\tconst builder = new flatbuffers.Builder(1024);\n\n\tconst subIdOffset = subId ? builder.createString(subId) : 0;\n\tconst relayUrlOffset = builder.createString(relayUrl);\n\tconst idOffset = builder.createString(event.id);\n\tconst pubkeyOffset = builder.createString(event.pubkey);\n\tconst contentOffset = builder.createString(event.content);\n\tconst sigOffset = builder.createString(event.sig);\n\n\tconst tagOffsets = new Array<flatbuffers.Offset>(event.tags.length);\n\tfor (let i = 0; i < event.tags.length; i++) {\n\t\ttagOffsets[i] = createStringVecOffset(builder, event.tags[i]!);\n\t}\n\tconst tagsOffset = NostrEvent.createTagsVector(builder, tagOffsets);\n\n\tconst eventOffset = NostrEvent.createNostrEvent(\n\t\tbuilder,\n\t\tidOffset,\n\t\tpubkeyOffset,\n\t\tevent.kind,\n\t\tcontentOffset,\n\t\ttagsOffset,\n\t\tevent.created_at,\n\t\tsigOffset\n\t);\n\n\tconst workerMessageOffset = WorkerMessage.createWorkerMessage(\n\t\tbuilder,\n\t\tsubIdOffset,\n\t\trelayUrlOffset,\n\t\tMessageType.NostrEvent,\n\t\tMessage.NostrEvent,\n\t\teventOffset\n\t);\n\n\tbuilder.finish(workerMessageOffset);\n\treturn builder.asUint8Array();\n}\n\nfunction buildConnectionStatusWorkerMessage(\n\tsubId: string,\n\trelayUrl: string,\n\tstatus: string,\n\tmessage: string | null\n): Uint8Array {\n\tconst builder = new flatbuffers.Builder(256);\n\n\tconst subIdOffset = subId ? builder.createString(subId) : 0;\n\tconst relayUrlOffset = builder.createString(relayUrl);\n\tconst statusOffset = builder.createString(status);\n\tconst messageOffset = message === null ? 0 : builder.createString(message);\n\n\tconst contentOffset = ConnectionStatus.createConnectionStatus(\n\t\tbuilder,\n\t\trelayUrlOffset,\n\t\tstatusOffset,\n\t\tmessageOffset\n\t);\n\n\tconst workerMessageOffset = WorkerMessage.createWorkerMessage(\n\t\tbuilder,\n\t\tsubIdOffset,\n\t\trelayUrlOffset,\n\t\tMessageType.ConnectionStatus,\n\t\tMessage.ConnectionStatus,\n\t\tcontentOffset\n\t);\n\n\tbuilder.finish(workerMessageOffset);\n\treturn builder.asUint8Array();\n}\n\nfunction buildRawWorkerMessage(subId: string, relayUrl: string, rawFrame: string): Uint8Array {\n\tconst builder = new flatbuffers.Builder(256);\n\n\tconst subIdOffset = subId ? builder.createString(subId) : 0;\n\tconst relayUrlOffset = builder.createString(relayUrl);\n\tconst rawOffset = builder.createString(rawFrame);\n\tconst contentOffset = Raw.createRaw(builder, rawOffset);\n\n\tconst workerMessageOffset = WorkerMessage.createWorkerMessage(\n\t\tbuilder,\n\t\tsubIdOffset,\n\t\trelayUrlOffset,\n\t\tMessageType.Raw,\n\t\tMessage.Raw,\n\t\tcontentOffset\n\t);\n\n\tbuilder.finish(workerMessageOffset);\n\treturn builder.asUint8Array();\n}\n\nfunction asNostrEvent(value: unknown): NostrEventJson | null {\n\tif (!value || typeof value !== 'object') return null;\n\tconst candidate = value as Partial<NostrEventJson>;\n\tif (\n\t\ttypeof candidate.id !== 'string' ||\n\t\ttypeof candidate.pubkey !== 'string' ||\n\t\ttypeof candidate.kind !== 'number' ||\n\t\ttypeof candidate.content !== 'string' ||\n\t\ttypeof candidate.created_at !== 'number' ||\n\t\ttypeof candidate.sig !== 'string' ||\n\t\t!Array.isArray(candidate.tags)\n\t) {\n\t\treturn null;\n\t}\n\n\tconst rawTags = candidate.tags;\n\tlet needsSanitization = false;\n\tfor (const tag of rawTags) {\n\t\tif (!Array.isArray(tag) || tag.length === 0) {\n\t\t\tneedsSanitization = true;\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const item of tag) {\n\t\t\tif (typeof item !== 'string') {\n\t\t\t\tneedsSanitization = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tlet tags: string[][];\n\tif (!needsSanitization) {\n\t\ttags = rawTags as string[][];\n\t} else {\n\t\ttags = [];\n\t\tfor (const tag of rawTags) {\n\t\t\tif (!Array.isArray(tag)) continue;\n\t\t\tconst sanitizedTag: string[] = [];\n\t\t\tfor (const item of tag) {\n\t\t\t\tif (typeof item === 'string') {\n\t\t\t\t\tsanitizedTag.push(item);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (sanitizedTag.length > 0) {\n\t\t\t\ttags.push(sanitizedTag);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tid: candidate.id,\n\t\tpubkey: candidate.pubkey,\n\t\tkind: candidate.kind,\n\t\tcontent: candidate.content,\n\t\tcreated_at: candidate.created_at,\n\t\tsig: candidate.sig,\n\t\ttags\n\t};\n}\n\nfunction createStringVecOffset(builder: flatbuffers.Builder, values: string[]): flatbuffers.Offset {\n\tconst itemOffsets = new Array<flatbuffers.Offset>(values.length);\n\tfor (let i = 0; i < values.length; i++) {\n\t\titemOffsets[i] = builder.createString(values[i]!);\n\t}\n\tconst itemsOffset = StringVec.createItemsVector(builder, itemOffsets);\n\treturn StringVec.createStringVec(builder, itemsOffset);\n}\n\nfunction parseRelayFrame(rawFrame: string): unknown[] | null {\n\ttry {\n\t\tconst parsed = JSON.parse(rawFrame);\n\t\tif (!Array.isArray(parsed)) return null;\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction parseEnvelope(data: unknown): Envelope | null {\n\tconst text = toUtf8(data);\n\tif (!text) return null;\n\n\ttry {\n\t\tconst parsed = JSON.parse(text) as Partial<Envelope>;\n\t\tif (!Array.isArray(parsed.relays) || !Array.isArray(parsed.frames)) return null;\n\t\tconst relays = parsed.relays.filter((relay): relay is string => typeof relay === 'string');\n\t\tconst frames = parsed.frames.filter((frame): frame is string => typeof frame === 'string');\n\t\tif (relays.length === 0 || frames.length === 0) return null;\n\t\treturn { relays, frames };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction toUtf8(data: unknown): string {\n\tif (typeof data === 'string') return data;\n\tif (data instanceof ArrayBuffer) return Buffer.from(data).toString('utf8');\n\tif (Buffer.isBuffer(data)) return data.toString('utf8');\n\tif (data instanceof Uint8Array) return Buffer.from(data).toString('utf8');\n\tif (Array.isArray(data)) {\n\t\treturn Buffer.concat(data.filter((item): item is Buffer => Buffer.isBuffer(item))).toString('utf8');\n\t}\n\treturn '';\n}\n\nfunction isAuthResponseCommand(value: unknown): value is AuthResponseCommand {\n\tif (!value || typeof value !== 'object') return false;\n\tconst candidate = value as Partial<AuthResponseCommand>;\n\treturn (\n\t\tcandidate.type === 'auth_response' &&\n\t\ttypeof candidate.relay === 'string' &&\n\t\tcandidate.event !== undefined\n\t);\n}\n\nfunction isCloseSubCommand(value: unknown): value is CloseSubCommand {\n\tif (!value || typeof value !== 'object') return false;\n\tconst candidate = value as Partial<CloseSubCommand>;\n\treturn candidate.type === 'close_sub' && typeof candidate.subscription_id === 'string';\n}\n\nfunction closeSession(session: Session) {\n\tsession.dedupBySubId.clear();\n\tsession.lastSubIdByRelay.clear();\n\tfor (const socket of session.relaySockets.values()) {\n\t\ttry {\n\t\t\tsocket.close();\n\t\t} catch {\n\t\t\t// Best effort.\n\t\t}\n\t}\n\tsession.relaySockets.clear();\n\tsession.pendingFrames.clear();\n}\n"],"names":["createRelayProxyServer","options","host","port","path","logger","wss","WebSocketServer","err","actualPort","address","clientSocket","session","data","isBinary","envelope","parseEnvelope","handleEnvelope","text","toUtf8","handleClientCommand","closeSession","resolve","reject","command","isAuthResponseCommand","frame","sendFrameToRelay","isCloseSubCommand","relayUrl","relaySocket","WebSocket","trackedFrames","parseSubscriptionFrameState","relay","ensureRelaySocket","tracked","trackSubscriptionState","existing","upstream","pending","raw","subIdHint","workerMessage","relayFrameToWorkerMessage","errorMsg","state","subId","parsed","parseRelayFrame","type","rawFrame","buildRawWorkerMessage","kind","event","asNostrEvent","dedupSet","buildNostrEventWorkerMessage","message","buildConnectionStatusWorkerMessage","challenge","eventId","accepted","reason","builder","flatbuffers","subIdOffset","relayUrlOffset","idOffset","pubkeyOffset","contentOffset","sigOffset","tagOffsets","i","createStringVecOffset","tagsOffset","NostrEvent","eventOffset","workerMessageOffset","WorkerMessage","MessageType","Message","status","statusOffset","messageOffset","ConnectionStatus","rawOffset","Raw","value","candidate","rawTags","needsSanitization","tag","item","tags","sanitizedTag","values","itemOffsets","itemsOffset","StringVec","relays","frames","socket"],"mappings":"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"}
1
+ {"version":3,"file":"relayProxyServer.js","sources":["../src/proxy/relayProxyServer.ts"],"sourcesContent":["/// <reference types=\"node\" />\n\nimport type { Server as HttpServer } from 'http';\nimport type { Server as HttpsServer } from 'https';\nimport type { IncomingMessage } from 'http';\nimport type { Duplex } from 'stream';\nimport * as flatbuffers from 'flatbuffers';\nimport { WebSocket, WebSocketServer } from 'ws';\nimport {\n\tConnectionStatus,\n\tMessage,\n\tMessageType,\n\tNostrEvent,\n\tRaw,\n\tStringVec,\n\tWorkerMessage\n} from '../generated/nostr/fb';\n\ntype RelayProxyServerLogger = Pick<Console, 'info' | 'warn' | 'error'>;\n\nexport type RelayProxyServerOptions = {\n\thost?: string;\n\tport?: number;\n\tpath?: string;\n\tlogger?: RelayProxyServerLogger;\n};\n\nexport type RelayProxyServer = {\n\tport: number;\n\tclose: () => Promise<void>;\n};\n\nexport type AttachRelayProxyOptions = {\n\t/** The HTTP/HTTPS server to attach to */\n\tserver: HttpServer | HttpsServer;\n\t/** The path to mount the WebSocket endpoint on (e.g., '/ws-proxy') */\n\tpath?: string;\n\tlogger?: RelayProxyServerLogger;\n};\n\nexport type AttachedRelayProxy = {\n\t/** Stop accepting new connections and close all existing sessions */\n\tclose: () => Promise<void>;\n};\n\nexport type WebSocketRelayProxy = {\n\twss: WebSocketServer;\n\tclose: () => Promise<void>;\n\t/** Manually handle a WebSocket upgrade request */\n\thandleUpgrade: (request: IncomingMessage, socket: Duplex, head: Buffer) => void;\n};\n\ntype Envelope = {\n\trelays: string[];\n\tframes: string[];\n};\n\ntype AuthResponseCommand = {\n\ttype: 'auth_response';\n\trelay: string;\n\tevent: unknown;\n};\n\ntype CloseSubCommand = {\n\ttype: 'close_sub';\n\tsubscription_id: string;\n};\n\ntype NostrEventJson = {\n\tid: string;\n\tpubkey: string;\n\tkind: number;\n\tcontent: string;\n\ttags: string[][];\n\tcreated_at: number;\n\tsig: string;\n};\n\ntype Session = {\n\trelaySockets: Map<string, WebSocket>;\n\tpendingFrames: Map<string, string[]>;\n\tdedupBySubId: Map<string, Set<string>>;\n\tlastSubIdByRelay: Map<string, string>;\n};\n\ntype SubscriptionFrameState = {\n\ttype: 'REQ' | 'CLOSE';\n\tsubId: string;\n} | null;\n\n/**\n * Create a standalone relay proxy server on its own port.\n * Use this for simple deployments or when you don't have an existing HTTP server.\n */\nexport function createRelayProxyServer(options: RelayProxyServerOptions = {}): RelayProxyServer {\n\tconst host = options.host ?? '127.0.0.1';\n\tconst port = options.port ?? 7777;\n\tconst path = options.path ?? '/';\n\tconst logger = options.logger ?? console;\n\n\tlet wss: WebSocketServer;\n\ttry {\n\t\twss = new WebSocketServer({\n\t\t\thost,\n\t\t\tport,\n\t\t\tpath\n\t\t});\n\t} catch (err) {\n\t\tlogger.error(`[relay-proxy] failed to create WebSocketServer: ${String(err)}`);\n\t\tthrow err;\n\t}\n\n\t// Get the actual port (in case port 0 was passed)\n\t// If the server is not yet listening, wss.address() returns null\n\t// In that case, wait for the 'listening' event\n\tlet actualPort = port;\n\tconst address = wss.address();\n\tif (address && typeof address === 'object') {\n\t\tactualPort = address.port;\n\t}\n\n\twss.on('error', (err) => {\n\t\tlogger.error(`[relay-proxy] WebSocketServer error: ${String(err)}`);\n\t});\n\n\twss.on('connection', (clientSocket) => {\n\t\tconst session: Session = {\n\t\t\trelaySockets: new Map(),\n\t\t\tpendingFrames: new Map(),\n\t\t\tdedupBySubId: new Map(),\n\t\t\tlastSubIdByRelay: new Map()\n\t\t};\n\n\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\tif (isBinary) {\n\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\tif (!envelope) return;\n\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst text = toUtf8(data);\n\t\t\tif (!text) return;\n\t\t\thandleClientCommand(session, text, logger);\n\t\t});\n\n\t\tclientSocket.on('close', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\n\t\tclientSocket.on('error', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\t});\n\n\tlogger.info(`[relay-proxy] listening on ws://${host}:${actualPort}${path}`);\n\n\treturn {\n\t\tport: actualPort,\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t})\n\t};\n}\n\n/**\n * Create a WebSocket relay proxy that supports manual upgrade handling.\n * Use this for Vite development servers where SvelteKit middleware might interfere\n * with the standard WebSocket upgrade mechanism.\n *\n * @example\n * // In a Vite plugin\n * const relayProxy = createRelayProxyWebSocketServer({ path: '/ws-proxy' });\n *\n * // In configureServer middleware\n * server.middlewares.use((req, res, next) => {\n * if (req.url?.startsWith('/ws-proxy') && req.headers.upgrade === 'websocket') {\n * server.httpServer.once('upgrade', (request, socket, head) => {\n * if (request.url?.startsWith('/ws-proxy')) {\n * relayProxy.handleUpgrade(request, socket, head);\n * }\n * });\n * }\n * next();\n * });\n */\nexport function createRelayProxyWebSocketServer(\n\toptions: Omit<AttachRelayProxyOptions, 'server'> & { server?: HttpServer | HttpsServer }\n): WebSocketRelayProxy {\n\tconst path = options.path ?? '/';\n\tconst logger = options.logger ?? console;\n\n\t// Create WebSocket server with noServer mode to handle upgrades manually\n\tconst wss = new WebSocketServer({\n\t\tnoServer: true\n\t});\n\n\tconst sessions = new Map<WebSocket, Session>();\n\n\twss.on('connection', (clientSocket) => {\n\t\tconst session: Session = {\n\t\t\trelaySockets: new Map(),\n\t\t\tpendingFrames: new Map(),\n\t\t\tdedupBySubId: new Map(),\n\t\t\tlastSubIdByRelay: new Map()\n\t\t};\n\t\tsessions.set(clientSocket, session);\n\n\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\tconst session = sessions.get(clientSocket);\n\t\t\tif (!session) return;\n\n\t\t\tif (isBinary) {\n\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\tif (!envelope) return;\n\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst text = toUtf8(data);\n\t\t\tif (!text) return;\n\t\t\thandleClientCommand(session, text, logger);\n\t\t});\n\n\t\tclientSocket.on('close', () => {\n\t\t\tconst session = sessions.get(clientSocket);\n\t\t\tif (session) {\n\t\t\t\tcloseSession(session);\n\t\t\t\tsessions.delete(clientSocket);\n\t\t\t}\n\t\t});\n\n\t\tclientSocket.on('error', () => {\n\t\t\tconst session = sessions.get(clientSocket);\n\t\t\tif (session) {\n\t\t\t\tcloseSession(session);\n\t\t\t\tsessions.delete(clientSocket);\n\t\t\t}\n\t\t});\n\t});\n\n\tconst handleUpgrade = (request: IncomingMessage, socket: Duplex, head: Buffer) => {\n\t\t// Verify path matches\n\t\tconst url = request.url ?? '';\n\t\tif (!url.startsWith(path)) {\n\t\t\treturn;\n\t\t}\n\n\t\twss.handleUpgrade(request, socket, head, (ws) => {\n\t\t\twss.emit('connection', ws, request);\n\t\t});\n\t};\n\n\treturn {\n\t\twss,\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\t// Close all sessions first\n\t\t\t\tsessions.forEach((session) => closeSession(session));\n\t\t\t\tsessions.clear();\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t}),\n\t\thandleUpgrade\n\t};\n}\n\n/**\n * Attach the relay proxy to an existing HTTP/HTTPS server.\n * Use this for embedding in SvelteKit (adapter-node), Express, or any Node.js server.\n *\n * @example\n * // SvelteKit with adapter-node\n * import { createServer } from 'http';\n * import { handler } from './build/handler.js';\n * import { attachRelayProxyToServer } from '@candypoets/nipworker/proxy/server';\n *\n * const server = createServer(handler);\n * attachRelayProxyToServer({ server, path: '/ws-proxy' });\n * server.listen(3000);\n */\nexport function attachRelayProxyToServer(options: AttachRelayProxyOptions): AttachedRelayProxy {\n\tconst { server, path = '/', logger = console } = options;\n\n\tconst wss = new WebSocketServer({\n\t\tserver,\n\t\tpath\n\t});\n\n\twss.on('connection', (clientSocket) => {\n\t\tconst session: Session = {\n\t\t\trelaySockets: new Map(),\n\t\t\tpendingFrames: new Map(),\n\t\t\tdedupBySubId: new Map(),\n\t\t\tlastSubIdByRelay: new Map()\n\t\t};\n\n\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\tif (isBinary) {\n\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\tif (!envelope) return;\n\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst text = toUtf8(data);\n\t\t\tif (!text) return;\n\t\t\thandleClientCommand(session, text, logger);\n\t\t});\n\n\t\tclientSocket.on('close', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\n\t\tclientSocket.on('error', () => {\n\t\t\tcloseSession(session);\n\t\t});\n\t});\n\n\tlogger.info(`[relay-proxy] attached to server at path: ${path}`);\n\n\treturn {\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t})\n\t};\n}\n\n/**\n * Create an Express middleware that attaches the relay proxy to the Express server's underlying HTTP server.\n * Call this after setting up your Express app but before calling app.listen().\n *\n * @example\n * import express from 'express';\n * import { createExpressRelayProxyMiddleware } from '@candypoets/nipworker/proxy/server';\n *\n * const app = express();\n *\n * // Your Express routes...\n * app.get('/api/health', (req, res) => res.json({ ok: true }));\n *\n * // Attach relay proxy\n * const relayProxy = createExpressRelayProxyMiddleware(app, { path: '/ws-proxy' });\n *\n * const server = app.listen(3000, () => {\n * console.log('Server with relay proxy running on port 3000');\n * });\n *\n * // Cleanup on shutdown\n * process.on('SIGTERM', async () => {\n * await relayProxy.close();\n * server.close();\n * });\n */\nexport function createExpressRelayProxyMiddleware<\n\tT extends { listen: (...args: any[]) => HttpServer | HttpsServer }\n>(app: T, options: Omit<AttachRelayProxyOptions, 'server'>): AttachedRelayProxy {\n\tconst path = options.path ?? '/ws-proxy';\n\tconst logger = options.logger ?? console;\n\n\t// Store reference to the server once it's created\n\tlet wss: WebSocketServer | null = null;\n\tconst sessions = new Map<WebSocket, Session>();\n\n\t// Monkey-patch app.listen to capture the server instance\n\tconst originalListen = app.listen.bind(app);\n\t(app as any).listen = (...args: any[]) => {\n\t\tconst server = originalListen(...args);\n\n\t\twss = new WebSocketServer({\n\t\t\tserver,\n\t\t\tpath\n\t\t});\n\n\t\twss.on('connection', (clientSocket) => {\n\t\t\tconst session: Session = {\n\t\t\t\trelaySockets: new Map(),\n\t\t\t\tpendingFrames: new Map(),\n\t\t\t\tdedupBySubId: new Map(),\n\t\t\t\tlastSubIdByRelay: new Map()\n\t\t\t};\n\t\t\tsessions.set(clientSocket, session);\n\n\t\t\tclientSocket.on('message', (data, isBinary) => {\n\t\t\t\tconst session = sessions.get(clientSocket);\n\t\t\t\tif (!session) return;\n\n\t\t\t\tif (isBinary) {\n\t\t\t\t\tconst envelope = parseEnvelope(data);\n\t\t\t\t\tif (!envelope) return;\n\t\t\t\t\thandleEnvelope(session, clientSocket, envelope, logger);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst text = toUtf8(data);\n\t\t\t\tif (!text) return;\n\t\t\t\thandleClientCommand(session, text, logger);\n\t\t\t});\n\n\t\t\tclientSocket.on('close', () => {\n\t\t\t\tconst session = sessions.get(clientSocket);\n\t\t\t\tif (session) {\n\t\t\t\t\tcloseSession(session);\n\t\t\t\t\tsessions.delete(clientSocket);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tclientSocket.on('error', () => {\n\t\t\t\tconst session = sessions.get(clientSocket);\n\t\t\t\tif (session) {\n\t\t\t\t\tcloseSession(session);\n\t\t\t\t\tsessions.delete(clientSocket);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tlogger.info(`[relay-proxy] attached to Express server at path: ${path}`);\n\n\t\treturn server;\n\t};\n\n\treturn {\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\tif (!wss) {\n\t\t\t\t\tresolve();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Close all sessions first\n\t\t\t\tsessions.forEach((session) => closeSession(session));\n\t\t\t\tsessions.clear();\n\t\t\t\twss.close((err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t})\n\t};\n}\n\nfunction handleClientCommand(session: Session, text: string, logger: RelayProxyServerLogger) {\n\tlet command: unknown;\n\ttry {\n\t\tcommand = JSON.parse(text);\n\t} catch {\n\t\treturn;\n\t}\n\n\tif (isAuthResponseCommand(command)) {\n\t\tconst frame = JSON.stringify(['AUTH', command.event]);\n\t\tsendFrameToRelay(session, command.relay, frame, logger);\n\t\treturn;\n\t}\n\n\tif (isCloseSubCommand(command)) {\n\t\tsession.dedupBySubId.delete(command.subscription_id);\n\t\tfor (const [relayUrl, relaySocket] of session.relaySockets.entries()) {\n\t\t\tif (relaySocket.readyState === WebSocket.OPEN) {\n\t\t\t\trelaySocket.send(JSON.stringify(['CLOSE', command.subscription_id]));\n\t\t\t}\n\t\t\tsession.lastSubIdByRelay.set(relayUrl, command.subscription_id);\n\t\t}\n\t}\n}\n\nfunction handleEnvelope(\n\tsession: Session,\n\tclientSocket: WebSocket,\n\tenvelope: Envelope,\n\tlogger: RelayProxyServerLogger\n) {\n\tconst trackedFrames = envelope.frames.map((frame) => ({\n\t\tframe,\n\t\tstate: parseSubscriptionFrameState(frame)\n\t}));\n\n\tfor (const relay of envelope.relays) {\n\t\tensureRelaySocket(session, clientSocket, relay, logger);\n\t\tfor (const tracked of trackedFrames) {\n\t\t\ttrackSubscriptionState(session, relay, tracked.state);\n\t\t\tconst frame = tracked.frame;\n\t\t\tsendFrameToRelay(session, relay, frame, logger);\n\t\t}\n\t}\n}\n\nfunction ensureRelaySocket(\n\tsession: Session,\n\tclientSocket: WebSocket,\n\trelayUrl: string,\n\tlogger: RelayProxyServerLogger\n) {\n\tconst existing = session.relaySockets.get(relayUrl);\n\tif (existing && existing.readyState !== WebSocket.CLOSED) return;\n\n\tconst upstream = new WebSocket(relayUrl);\n\tsession.relaySockets.set(relayUrl, upstream);\n\tsession.pendingFrames.set(relayUrl, []);\n\n\tupstream.on('open', () => {\n\t\tconst pending = session.pendingFrames.get(relayUrl);\n\t\tif (!pending) return;\n\t\tfor (const frame of pending) {\n\t\t\tupstream.send(frame);\n\t\t}\n\t\tsession.pendingFrames.set(relayUrl, []);\n\t});\n\n\tupstream.on('message', (data) => {\n\t\tconst raw = toUtf8(data);\n\t\tif (!raw || clientSocket.readyState !== WebSocket.OPEN) return;\n\t\tconst subIdHint = session.lastSubIdByRelay.get(relayUrl);\n\t\tconst workerMessage = relayFrameToWorkerMessage(session, relayUrl, raw, subIdHint);\n\t\tif (!workerMessage) return;\n\t\tclientSocket.send(workerMessage, { binary: true });\n\t});\n\n\tupstream.on('close', () => {\n\t\t// Mark as closed but don't delete - ensureRelaySocket will reconnect on next use\n\t\t// This keeps subscriptions alive across reconnects\n\t\tsession.pendingFrames.delete(relayUrl);\n\t});\n\n\tupstream.on('error', (err) => {\n\t\t// Only log the first error per relay to reduce spam\n\t\tif (!session.pendingFrames.has(relayUrl)) return;\n\t\tconst errorMsg = String(err);\n\t\t// Skip common repetitive errors\n\t\tif (!errorMsg.includes('ECONNREFUSED') && !errorMsg.includes('ENOTFOUND')) {\n\t\t\tlogger.warn(`[relay-proxy] relay socket error for ${relayUrl}: ${errorMsg.slice(0, 100)}`);\n\t\t}\n\t\tsession.pendingFrames.delete(relayUrl);\n\t});\n}\n\nfunction trackSubscriptionState(session: Session, relayUrl: string, state: SubscriptionFrameState) {\n\tif (!state) return;\n\tsession.lastSubIdByRelay.set(relayUrl, state.subId);\n\n\tif (state.type === 'REQ') {\n\t\tconst subId = state.subId;\n\t\tif (!session.dedupBySubId.has(subId)) {\n\t\t\tsession.dedupBySubId.set(subId, new Set());\n\t\t}\n\t\treturn;\n\t}\n\n\tsession.dedupBySubId.delete(state.subId);\n}\n\nfunction parseSubscriptionFrameState(frame: string): SubscriptionFrameState {\n\tconst parsed = parseRelayFrame(frame);\n\tif (!parsed) return null;\n\tconst type = parsed[0];\n\tif (type !== 'REQ' && type !== 'CLOSE') return null;\n\n\tconst subId = typeof parsed[1] === 'string' ? parsed[1] : null;\n\tif (!subId) return null;\n\treturn { type, subId };\n}\n\nfunction sendFrameToRelay(\n\tsession: Session,\n\trelayUrl: string,\n\tframe: string,\n\tlogger: RelayProxyServerLogger\n) {\n\tconst relaySocket = session.relaySockets.get(relayUrl);\n\tif (!relaySocket) return;\n\n\tif (relaySocket.readyState === WebSocket.OPEN) {\n\t\trelaySocket.send(frame);\n\t\treturn;\n\t}\n\n\tif (relaySocket.readyState === WebSocket.CONNECTING) {\n\t\tconst pending = session.pendingFrames.get(relayUrl) ?? [];\n\t\tpending.push(frame);\n\t\tsession.pendingFrames.set(relayUrl, pending);\n\t\treturn;\n\t}\n\n\tlogger.warn(`[relay-proxy] dropping frame for closed relay socket ${relayUrl}`);\n}\n\nfunction relayFrameToWorkerMessage(\n\tsession: Session,\n\trelayUrl: string,\n\trawFrame: string,\n\tsubIdHint?: string\n): Uint8Array | null {\n\tconst frame = parseRelayFrame(rawFrame);\n\tif (!frame || frame.length < 1 || typeof frame[0] !== 'string') {\n\t\treturn buildRawWorkerMessage(subIdHint ?? '', relayUrl, rawFrame);\n\t}\n\n\tconst kind = frame[0];\n\tif (kind === 'EVENT') {\n\t\tconst subId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\tconst event = asNostrEvent(frame[2]);\n\t\tif (!subId || !event) {\n\t\t\treturn buildRawWorkerMessage(subId, relayUrl, rawFrame);\n\t\t}\n\n\t\tconst dedupSet = session.dedupBySubId.get(subId);\n\t\tif (dedupSet && dedupSet.has(event.id)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (dedupSet) dedupSet.add(event.id);\n\n\t\treturn buildNostrEventWorkerMessage(subId, relayUrl, event);\n\t}\n\n\tif (kind === 'NOTICE') {\n\t\tconst message = frame[1] === undefined ? null : String(frame[1]);\n\t\treturn buildConnectionStatusWorkerMessage('', relayUrl, 'NOTICE', message);\n\t}\n\n\tif (kind === 'AUTH') {\n\t\tconst challenge = frame[1] === undefined ? null : String(frame[1]);\n\t\treturn buildConnectionStatusWorkerMessage(subIdHint ?? '', relayUrl, 'AUTH', challenge);\n\t}\n\n\tif (kind === 'CLOSED') {\n\t\tconst subId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\tconst message = frame[2] === undefined ? null : String(frame[2]);\n\t\treturn buildConnectionStatusWorkerMessage(subId, relayUrl, 'CLOSED', message);\n\t}\n\n\tif (kind === 'OK') {\n\t\tconst eventId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\tconst accepted = frame[2] === undefined ? 'false' : String(frame[2]);\n\t\tconst reason = frame[3] === undefined ? null : String(frame[3]);\n\t\treturn buildConnectionStatusWorkerMessage(eventId, relayUrl, accepted, reason);\n\t}\n\n\tif (kind === 'EOSE') {\n\t\tconst subId = typeof frame[1] === 'string' ? frame[1] : '';\n\t\treturn buildConnectionStatusWorkerMessage(subId, relayUrl, 'EOSE', null);\n\t}\n\n\treturn buildRawWorkerMessage(subIdHint ?? '', relayUrl, rawFrame);\n}\n\nfunction buildNostrEventWorkerMessage(subId: string, relayUrl: string, event: NostrEventJson): Uint8Array {\n\tconst builder = new flatbuffers.Builder(1024);\n\n\tconst subIdOffset = subId ? builder.createString(subId) : 0;\n\tconst relayUrlOffset = builder.createString(relayUrl);\n\tconst idOffset = builder.createString(event.id);\n\tconst pubkeyOffset = builder.createString(event.pubkey);\n\tconst contentOffset = builder.createString(event.content);\n\tconst sigOffset = builder.createString(event.sig);\n\n\tconst tagOffsets = new Array<flatbuffers.Offset>(event.tags.length);\n\tfor (let i = 0; i < event.tags.length; i++) {\n\t\ttagOffsets[i] = createStringVecOffset(builder, event.tags[i]!);\n\t}\n\tconst tagsOffset = NostrEvent.createTagsVector(builder, tagOffsets);\n\n\tconst eventOffset = NostrEvent.createNostrEvent(\n\t\tbuilder,\n\t\tidOffset,\n\t\tpubkeyOffset,\n\t\tevent.kind,\n\t\tcontentOffset,\n\t\ttagsOffset,\n\t\tevent.created_at,\n\t\tsigOffset\n\t);\n\n\tconst workerMessageOffset = WorkerMessage.createWorkerMessage(\n\t\tbuilder,\n\t\tsubIdOffset,\n\t\trelayUrlOffset,\n\t\tMessageType.NostrEvent,\n\t\tMessage.NostrEvent,\n\t\teventOffset\n\t);\n\n\tbuilder.finish(workerMessageOffset);\n\treturn builder.asUint8Array();\n}\n\nfunction buildConnectionStatusWorkerMessage(\n\tsubId: string,\n\trelayUrl: string,\n\tstatus: string,\n\tmessage: string | null\n): Uint8Array {\n\tconst builder = new flatbuffers.Builder(256);\n\n\tconst subIdOffset = subId ? builder.createString(subId) : 0;\n\tconst relayUrlOffset = builder.createString(relayUrl);\n\tconst statusOffset = builder.createString(status);\n\tconst messageOffset = message === null ? 0 : builder.createString(message);\n\n\tconst contentOffset = ConnectionStatus.createConnectionStatus(\n\t\tbuilder,\n\t\trelayUrlOffset,\n\t\tstatusOffset,\n\t\tmessageOffset\n\t);\n\n\tconst workerMessageOffset = WorkerMessage.createWorkerMessage(\n\t\tbuilder,\n\t\tsubIdOffset,\n\t\trelayUrlOffset,\n\t\tMessageType.ConnectionStatus,\n\t\tMessage.ConnectionStatus,\n\t\tcontentOffset\n\t);\n\n\tbuilder.finish(workerMessageOffset);\n\treturn builder.asUint8Array();\n}\n\nfunction buildRawWorkerMessage(subId: string, relayUrl: string, rawFrame: string): Uint8Array {\n\tconst builder = new flatbuffers.Builder(256);\n\n\tconst subIdOffset = subId ? builder.createString(subId) : 0;\n\tconst relayUrlOffset = builder.createString(relayUrl);\n\tconst rawOffset = builder.createString(rawFrame);\n\tconst contentOffset = Raw.createRaw(builder, rawOffset);\n\n\tconst workerMessageOffset = WorkerMessage.createWorkerMessage(\n\t\tbuilder,\n\t\tsubIdOffset,\n\t\trelayUrlOffset,\n\t\tMessageType.Raw,\n\t\tMessage.Raw,\n\t\tcontentOffset\n\t);\n\n\tbuilder.finish(workerMessageOffset);\n\treturn builder.asUint8Array();\n}\n\nfunction asNostrEvent(value: unknown): NostrEventJson | null {\n\tif (!value || typeof value !== 'object') return null;\n\tconst candidate = value as Partial<NostrEventJson>;\n\tif (\n\t\ttypeof candidate.id !== 'string' ||\n\t\ttypeof candidate.pubkey !== 'string' ||\n\t\ttypeof candidate.kind !== 'number' ||\n\t\ttypeof candidate.content !== 'string' ||\n\t\ttypeof candidate.created_at !== 'number' ||\n\t\ttypeof candidate.sig !== 'string' ||\n\t\t!Array.isArray(candidate.tags)\n\t) {\n\t\treturn null;\n\t}\n\n\tconst rawTags = candidate.tags;\n\tlet needsSanitization = false;\n\tfor (const tag of rawTags) {\n\t\tif (!Array.isArray(tag) || tag.length === 0) {\n\t\t\tneedsSanitization = true;\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const item of tag) {\n\t\t\tif (typeof item !== 'string') {\n\t\t\t\tneedsSanitization = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tlet tags: string[][];\n\tif (!needsSanitization) {\n\t\ttags = rawTags as string[][];\n\t} else {\n\t\ttags = [];\n\t\tfor (const tag of rawTags) {\n\t\t\tif (!Array.isArray(tag)) continue;\n\t\t\tconst sanitizedTag: string[] = [];\n\t\t\tfor (const item of tag) {\n\t\t\t\tif (typeof item === 'string') {\n\t\t\t\t\tsanitizedTag.push(item);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (sanitizedTag.length > 0) {\n\t\t\t\ttags.push(sanitizedTag);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tid: candidate.id,\n\t\tpubkey: candidate.pubkey,\n\t\tkind: candidate.kind,\n\t\tcontent: candidate.content,\n\t\tcreated_at: candidate.created_at,\n\t\tsig: candidate.sig,\n\t\ttags\n\t};\n}\n\nfunction createStringVecOffset(builder: flatbuffers.Builder, values: string[]): flatbuffers.Offset {\n\tconst itemOffsets = new Array<flatbuffers.Offset>(values.length);\n\tfor (let i = 0; i < values.length; i++) {\n\t\titemOffsets[i] = builder.createString(values[i]!);\n\t}\n\tconst itemsOffset = StringVec.createItemsVector(builder, itemOffsets);\n\treturn StringVec.createStringVec(builder, itemsOffset);\n}\n\nfunction parseRelayFrame(rawFrame: string): unknown[] | null {\n\ttry {\n\t\tconst parsed = JSON.parse(rawFrame);\n\t\tif (!Array.isArray(parsed)) return null;\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction parseEnvelope(data: unknown): Envelope | null {\n\tconst text = toUtf8(data);\n\tif (!text) return null;\n\n\ttry {\n\t\tconst parsed = JSON.parse(text) as Partial<Envelope>;\n\t\tif (!Array.isArray(parsed.relays) || !Array.isArray(parsed.frames)) return null;\n\t\tconst relays = parsed.relays.filter((relay): relay is string => typeof relay === 'string');\n\t\tconst frames = parsed.frames.filter((frame): frame is string => typeof frame === 'string');\n\t\tif (relays.length === 0 || frames.length === 0) return null;\n\t\treturn { relays, frames };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction toUtf8(data: unknown): string {\n\tif (typeof data === 'string') return data;\n\tif (data instanceof ArrayBuffer) return Buffer.from(data).toString('utf8');\n\tif (Buffer.isBuffer(data)) return data.toString('utf8');\n\tif (data instanceof Uint8Array) return Buffer.from(data).toString('utf8');\n\tif (Array.isArray(data)) {\n\t\treturn Buffer.concat(data.filter((item): item is Buffer => Buffer.isBuffer(item))).toString('utf8');\n\t}\n\treturn '';\n}\n\nfunction isAuthResponseCommand(value: unknown): value is AuthResponseCommand {\n\tif (!value || typeof value !== 'object') return false;\n\tconst candidate = value as Partial<AuthResponseCommand>;\n\treturn (\n\t\tcandidate.type === 'auth_response' &&\n\t\ttypeof candidate.relay === 'string' &&\n\t\tcandidate.event !== undefined\n\t);\n}\n\nfunction isCloseSubCommand(value: unknown): value is CloseSubCommand {\n\tif (!value || typeof value !== 'object') return false;\n\tconst candidate = value as Partial<CloseSubCommand>;\n\treturn candidate.type === 'close_sub' && typeof candidate.subscription_id === 'string';\n}\n\nfunction closeSession(session: Session) {\n\tsession.dedupBySubId.clear();\n\tsession.lastSubIdByRelay.clear();\n\tfor (const socket of session.relaySockets.values()) {\n\t\ttry {\n\t\t\tsocket.close();\n\t\t} catch {\n\t\t\t// Best effort.\n\t\t}\n\t}\n\tsession.relaySockets.clear();\n\tsession.pendingFrames.clear();\n}\n"],"names":["createRelayProxyServer","options","host","port","path","logger","wss","WebSocketServer","err","actualPort","address","clientSocket","session","data","isBinary","envelope","parseEnvelope","handleEnvelope","text","toUtf8","handleClientCommand","closeSession","resolve","reject","createRelayProxyWebSocketServer","sessions","request","socket","head","ws","attachRelayProxyToServer","server","createExpressRelayProxyMiddleware","app","originalListen","args","command","isAuthResponseCommand","frame","sendFrameToRelay","isCloseSubCommand","relayUrl","relaySocket","WebSocket","trackedFrames","parseSubscriptionFrameState","relay","ensureRelaySocket","tracked","trackSubscriptionState","existing","upstream","pending","raw","subIdHint","workerMessage","relayFrameToWorkerMessage","errorMsg","state","subId","parsed","parseRelayFrame","type","rawFrame","buildRawWorkerMessage","kind","event","asNostrEvent","dedupSet","buildNostrEventWorkerMessage","message","buildConnectionStatusWorkerMessage","challenge","eventId","accepted","reason","builder","flatbuffers","subIdOffset","relayUrlOffset","idOffset","pubkeyOffset","contentOffset","sigOffset","tagOffsets","i","createStringVecOffset","tagsOffset","NostrEvent","eventOffset","workerMessageOffset","WorkerMessage","MessageType","Message","status","statusOffset","messageOffset","ConnectionStatus","rawOffset","Raw","value","candidate","rawTags","needsSanitization","tag","item","tags","sanitizedTag","values","itemOffsets","itemsOffset","StringVec","relays","frames"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candypoets/nipworker",
3
- "version": "0.90.0",
3
+ "version": "0.90.1",
4
4
  "description": "Nostr client library with worker-based architecture using Rust WASM",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -52,7 +52,6 @@
52
52
  "flatc:rust": "flatc --rust --gen-object-api --gen-all --filename-suffix '' -o ./src/shared/src/generated ./schemas/message.fbs",
53
53
  "flatc:ts": "flatc --ts --gen-object-api --gen-all --filename-suffix '' -o ./src/generated ./schemas/message.fbs",
54
54
  "build:types": "tsc --emitDeclarationOnly --outDir dist",
55
- "proxy:start": "node ./dist/proxy/server.js",
56
55
  "prepublishOnly": "npm run build",
57
56
  "test": "vitest run",
58
57
  "test:watch": "vitest",