···229229const firehose = new Firehose({
230230 ws: WebSocket,
231231});`);const a=e.ws??globalThis.WebSocket;this.ws=new a(`${this.relay}/xrpc/com.atproto.sync.subscribeRepos${r}`)}start(){this.ws.addEventListener("open",()=>{this.emitter.emit("open")}),this.ws.addEventListener("message",async({data:e})=>{try{const r=await this.parseMessage(e);switch("seq"in r&&r.seq&&!isNaN(r.seq)&&(this.cursor=`${r.seq}`),r.$type){case"com.atproto.sync.subscribeRepos#identity":this.emitter.emit("identity",r);break;case"com.atproto.sync.subscribeRepos#account":this.emitter.emit("account",r);break;case"com.atproto.sync.subscribeRepos#info":this.emitter.emit("info",r);break;case"com.atproto.sync.subscribeRepos#sync":this.emitter.emit("sync",r);break;case"com.atproto.sync.subscribeRepos#commit":this.emitter.emit("commit",r);break;default:this.emitter.emit("unknown",r);break}}catch(r){this.emitter.emit("error",{cursor:this.cursor,error:r})}finally{this.autoReconnect&&this.preventReconnect()}}),this.ws.addEventListener("close",()=>{this.emitter.emit("close",this.cursor)}),this.ws.addEventListener("error",e=>{this.emitter.emit("websocketError",{cursor:this.cursor,error:e})})}close(){var e;(e=this.ws)==null||e.close()}on(e,r){return this.emitter.on(e,r)}async parseMessage(e){var d,m;const r=new Uint8Array(await new Blob(Array.isArray(e)?e:[e]).arrayBuffer()),[a,o]=Kp(r),[l,u]=Kp(o);if(u.length>0)throw new Error("Excess bytes in message");const{t:f,op:h}=LL(a);if(h===-1)throw new Error(`Error: ${l.message}
232232-Error code: ${l.error}`);if(f==="#commit"){const{seq:y,repo:v,commit:b,rev:x,since:S,blocks:w,ops:E,prevData:O,time:k}=l;if(!((d=w==null?void 0:w.$bytes)!=null&&d.length))return{$type:"com.atproto.sync.subscribeRepos#commit",seq:y,repo:v,commit:b.$link,rev:x,since:S,blocks:new Uint8Array,ops:[],...O?{prevData:O.$link}:{},time:k};const R=Dx(w),A=UL(R),_=[];for(const $ of E){const I=$.action;if(I==="create"){if(!$.cid)continue;const L=A.get($.cid.$link);if(!L)continue;_.push({action:I,path:$.path,cid:$.cid.$link,record:L})}else if(I==="update"){if(!$.cid)continue;const L=A.get($.cid.$link);if(!L)continue;_.push({action:I,path:$.path,cid:$.cid.$link,...$.prev?{prev:$.prev.$link}:{},record:L})}else if(I==="delete")_.push({action:I,path:$.path,...$.prev?{prev:$.prev.$link}:{}});else throw new Error(`Unknown action: ${I}`)}return{$type:"com.atproto.sync.subscribeRepos#commit",seq:y,repo:v,commit:b.$link,rev:x,since:S,blocks:R,ops:_,...O?{prevData:O.$link}:{},time:k}}else if(f==="#sync"){const{seq:y,did:v,blocks:b,rev:x,time:S}=l,w=(m=b==null?void 0:b.$bytes)!=null&&m.length?Dx(b):new Uint8Array;return{$type:"com.atproto.sync.subscribeRepos#sync",seq:y,did:v,blocks:w,rev:x,time:S}}return{$type:`com.atproto.sync.subscribeRepos${f}`,...l}}preventReconnect(){this.reconnectTimeout&&clearTimeout(this.reconnectTimeout),this.reconnectTimeout=setTimeout(()=>{this.reconnect()},5e3)}reconnect(){var e;(e=this.ws)==null||e.close(),this.start(),this.emitter.emit("reconnect")}}function LL(t){if(!t||typeof t!="object"||!t.t||typeof t.t!="string"||!t.op||typeof t.op!="number")throw new Error("Invalid header received");return{t:t.t,op:t.op}}function UL(t){const e=new Map;for(const{cid:r,bytes:a}of $L(t).iterate())e.set(vL(r).$link,VT(a));return e}const BL="234567abcdefghijklmnopqrstuvwxyz",$x=t=>{let e=0;for(const r of t)e=e*32+BL.indexOf(r);return e},IL=/^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/,HL=t=>{if(!PL(t))throw new Error("invalid TID");const e=$x(t.slice(0,11)),r=$x(t.slice(11,13));return{timestamp:e,clockid:r}},PL=t=>t.length===13&&IL.test(t),GL="app.bsky.feed.like",Ys=32,Gi=32,eu=Ys*Gi;function YL({url:t,desc:e,includeEvents:r,onRecieveEvent:a}){const[o,l]=j.useState("connecting"),[u,f]=j.useState(0),[h,d]=j.useState(0),[m,y]=j.useState({idx:Array.from({length:Gi+2}).map(()=>0),recv:Array.from({length:Gi+2}).map(()=>0)});return j.useEffect(()=>{const v=(x,S)=>{if(r.has(x)&&(a(x,S),f(w=>w+1),x==="commit"&&S.ops.length===1)){const w=S.ops[0];try{const[E,O]=w.path.split("/");if(E===GL){const k=HL(O).timestamp/1e3,R=Date.parse(S.time),A=R-k,_=+new Date-R;let $,I;A<0?$=-1:A>=eu?$=Gi:$=Math.min(Math.floor(A/Ys),eu),_<0?I=-1:_>=eu?I=Gi:I=Math.min(Math.floor(_/Ys),eu),y(({idx:L,recv:C})=>(L=L.slice(),C=C.slice(),L[$+1]+=1,C[I+1]+=1,{idx:L,recv:C}))}}catch{}}},b=new jL({relay:t});return b.on("open",()=>l("connected")),b.on("close",()=>l("closed")),b.on("reconnect",()=>d(x=>x+1)),b.on("error",x=>{console.error("oops",x),l("errored")}),b.on("websocketError",()=>l("errored")),b.on("commit",x=>v("commit",x)),b.on("sync",x=>v("sync",x)),b.on("account",x=>v("account",x)),b.on("identity",x=>v("identity",x)),b.on("info",(...x)=>console.info("info event",...x)),b.on("unknown",x=>console.warn(`unknown event from ${t}`,x)),b.start(),()=>{b.close()}},[t,r]),D.jsxs("div",{className:"relay",children:[D.jsx("h2",{children:e}),D.jsx("p",{children:D.jsx("code",{children:t})}),D.jsxs("p",{children:["[",D.jsx("code",{children:o}),"] (",D.jsx("code",{children:u.toLocaleString()})," events)"]}),h>0&&D.jsxs("p",{children:["reconnects: ",D.jsx("code",{children:h})]}),D.jsx(Xp,{height:180,width:420,yAxis:[{label:"events",scaleType:"symlog"}],skipAnimation:!0,xAxis:[{data:[-1].concat(Array.from({length:Gi}).map((v,b)=>b*Ys)).concat(["+"]),label:"index latency (ms)"}],series:[{data:m.idx}]}),D.jsx(Xp,{height:180,width:420,yAxis:[{label:"events",scaleType:"symlog"}],skipAnimation:!0,xAxis:[{data:[-1].concat(Array.from({length:Gi}).map((v,b)=>b*Ys)).concat(["+"]),label:"receive latency (ms)"}],series:[{data:m.recv}]})]})}const Nx=[{url:"wss://atproto.africa",desc:"Blacksky"},{url:"wss://bsky.network",desc:"Bluesky primary (+Rainbow)"},{url:"wss://relay1.us-east.bsky.network",desc:"Bluesky sync1.1 east (+Rainbow)"},{url:"wss://relay1.us-west.bsky.network",desc:"Bluesky sync1.1 west (+Rainbow)"},{url:"wss://relay.fire.hose.cam",desc:"microcosm Montreal"},{url:"wss://relay3.fr.hose.cam",desc:"microcosm France"},{url:"wss://relay.upcloud.world",desc:"UpCloud relay"},{url:"wss://relay.hayescmd.net",desc:"@edavis.dev's relay"},{url:"wss://relay.xero.systems",desc:"@dane.is.extraordinarily.cool's relay"}],jx=1600,qL=6,Lx=10*60*1e3,Ux=(t,e,r)=>{};function VL(){const[t,e]=j.useState([]),[r,a]=j.useState(["commit","sync","account","identity","unknown"]),[o,l]=j.useState(new Set(r)),[u,f]=j.useState(()=>Ux),[h,d]=j.useState(()=>()=>{}),[m,y]=j.useState({series:[]}),[v,b]=j.useState(!1),[x,S]=j.useState("");j.useEffect(()=>{let O=performance.now(),k={},R=[],A=requestAnimationFrame(L),_=setTimeout($,Lx);f(()=>(C,z,H)=>{k[C]||(k[C]=0),k[C]+=1}),d(()=>()=>{clearTimeout(_),_=setTimeout($,Lx),b(!1),console.info("keepalive: disconnection timer reset")});function $(){console.info("disconnecting due to inactivity"),e([]),b(!0)}const I=setInterval(()=>{let C=performance.now(),z=C-O;R.length>=qL-1&&R.shift(),R.push({t:C,dt:z,counts:k}),O=C,k={}},jx);function L(){var H;let C=performance.now();const z=Object.keys(((H=R.at(-1))==null?void 0:H.counts)||{}).toSorted();y({xAxis:[{data:R.map(({t:q})=>(-(C-q)/1e3).toFixed(1)).concat(["now"]),label:"bucket (seconds ago)"}],series:z.map(q=>({label:q,data:R.map(({dt:it,counts:lt})=>lt[q]?(lt[q]/(it/1e3)).toFixed(1):null).concat([k[q]?(k[q]/(jx/1e3)).toFixed(1):null])}))}),A=requestAnimationFrame(L)}return()=>{f(()=>Ux),d(()=>()=>null),clearInterval(I),cancelAnimationFrame(A)}},[]);function w(O,k){b(!1),e(k?R=>R.includes(O)?R:[...R,O]:R=>R.includes(O)?R.filter(A=>A!==O):R),h()}function E(){if(!x)return"";try{let O;return x.includes("://")?O=new URL(x):O=new URL("https://"+x),O.protocol==="https:"?O.protocol="wss:":O.protocol==="http:"&&(O.protocol="ws:"),O.origin}catch{return""}}return D.jsxs(D.Fragment,{children:[D.jsx("h1",{children:"compare hoses"}),D.jsx("p",{children:D.jsxs("em",{children:["warning: enabling many relay connections requires a ",D.jsx("strong",{children:"lot"})," of bandwidth"]})}),D.jsxs("form",{style:{display:"block",textAlign:"left"},children:[Nx.map(({url:O,desc:k})=>D.jsx("p",{style:{margin:0},children:D.jsxs("label",{children:[D.jsx("input",{type:"checkbox",onChange:R=>w(O,R.target.checked),checked:t.includes(O)}),` ${k} `,"(",D.jsx("code",{children:O.slice(6)}),")"]})},O)),D.jsx("p",{style:{margin:0},children:D.jsxs("label",{children:[D.jsx("input",{type:"checkbox",onChange:O=>{const k=E();k&&w(k,O.target.checked)},checked:t.includes(E())})," ",D.jsx("input",{type:"text",placeholder:"wss://…",value:x,onChange:O=>{const k=E();e(R=>R.includes(k)?R.filter(A=>A!==k):R),S(O.target.value)},onKeyDown:O=>{if(O.key!=="Enter")return;O.preventDefault();const k=E();k&&w(k,!0)}})," ",x&&D.jsx("code",{children:E()})]})})]}),D.jsxs("form",{style:{display:"block",margin:"1rem 0"},children:[D.jsx("span",{children:"events: "}),r.map(O=>D.jsxs("label",{children:[D.jsx("input",{type:"checkbox",checked:o.has(O),onChange:k=>{l(R=>{const A=new Set(R);return k.target.checked?A.add(O):A.delete(O),A})}})," ",O]},O))]}),D.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:"2em",textAlign:"left"},children:t.map(O=>{const{desc:k}=Nx.find(R=>R.url===O)??{desc:"custom relay"};return D.jsx("div",{children:D.jsx(YL,{url:O,desc:k,includeEvents:o,onRecieveEvent:(R,A)=>u(O,R,A)})},O)})}),v&&D.jsx("p",{children:D.jsx("em",{children:"disconnected to save bandwidth due to inactivity"})}),D.jsx("div",{className:"throughputs",children:D.jsx(Xp,{height:300,yAxis:[{label:"events / sec"}],skipAnimation:!0,...m})})]})}class vf{constructor(e){Ct(this,"val");this.val=e}}class XL extends vf{}class KL extends vf{}class KT extends vf{}class ZL extends vf{}class QL{constructor(e,r,a){Ct(this,"did");Ct(this,"handle");Ct(this,"pds");this.did=e,this.handle=r,this.pds=a}}const ZT="https://slingshot.microcosm.blue";async function QT(t){const e=new URLSearchParams;e.set("identifier",t);const r=await fetch(`${ZT}/xrpc/com.bad-example.identity.resolveMiniDoc?${e}`);if(!r.ok)return r.text().then(h=>console.warn(`slingshot failed to resolve ${t} (${r.status})`,h)),null;const a=await r.json(),o=new XL(a.did),l=new KL(a.handle),u=new URL(a.pds);return new QL(o,l,u)}async function FT(t,e){const r=new URLSearchParams;r.set("did",e.val);const a=await fetch(`${t}xrpc/com.atproto.sync.getRepoStatus?${r}`);if(a.status===404)try{if((await a.json()).error==="RepoNotFound")return"notfound"}catch{}return a.ok?await a.json():(a.text().then(o=>console.warn(`slingshot failed to getRepoStatus ${t} / ${e} (${a.status})`,o)),null)}async function FL(t,e,r){const a=new URLSearchParams;a.set("repo",t.val),a.set("collection",e.val),a.set("rkey",r.val);const o=await fetch(`${ZT}/xrpc/com.atproto.repo.getRecord?${a}`);return o.ok?(await o.json()).value:(o.text().then(u=>console.warn(`slingshot failed to getRecord ${t}/${e}/${r} (${o.status})`,u)),null)}async function*WL(t,e,r){let a=null;do{const o=new URLSearchParams;o.set("repo",e.val),o.set("collection",r.val),o.set("limit","100"),a&&o.set("cursor",a);const l=await fetch(`${t}xrpc/com.atproto.repo.listRecords?${o}`);if(!l.ok)return l.text().then(f=>console.warn(`slingshot failed to listRecords ${e} / ${r} (${l.status})`,f)),null;const u=await l.json();for(const{value:f}of u.records)yield f;a=u.cursor}while(a)}function JL({did:t,link:e}){const a=`https://cdn.bsky.app/img/avatar_thumbnail/plain/${t.val}/${e}@jpeg`;return D.jsx("img",{alt:"avatar",src:a,style:{display:"block",width:"100%",height:"100%"}})}function bg({doc:t,children:e}){const[r,a]=j.useState(null),[o,l]=j.useState(null);return j.useEffect(()=>{let u=!1;return(async()=>{var m,y;`${t.did.val}`;const f=await FL(t.did,new KT("app.bsky.actor.profile"),new ZL("self")),h=(y=(m=f==null?void 0:f.avatar)==null?void 0:m.ref)==null?void 0:y.$link;h&&!u&&a(h);const d=f==null?void 0:f.displayName;d&&!u&&l(d)})(),()=>u=!0},[t.did.val]),D.jsxs("div",{style:{display:"flex",textAlign:"left",alignItems:"center",background:"#333",padding:"0.5em 0.6em",gap:"0.6em",borderRadius:"0.3em"},children:[D.jsx("div",{style:{background:"#000",height:"42px",width:"42px",display:"flex",justifyContent:"center",alignItems:"center",color:"#858",fontSize:"0.8em",borderRadius:"100%",flexShrink:"0",overflow:"hidden"},children:r?D.jsx(JL,{did:t.did,link:r}):D.jsx(D.Fragment,{children:"…"})}),D.jsxs("div",{style:{flexGrow:"1"},children:[D.jsx("h3",{style:{margin:0,fontSize:"1em"},children:o||t.handle.val}),D.jsxs("p",{style:{fontSize:"1em",margin:0,lineHeight:"1",opacity:"0.8"},children:[o&&D.jsxs(D.Fragment,{children:[D.jsx("code",{children:t.handle.val}),D.jsx("br",{})]}),D.jsx("code",{children:t.did.val})]})]}),e&&D.jsx("div",{children:e})]})}function t8(t,e,r){let a=null,o=null;function l(...u){o=u,a===null&&(a=setTimeout(async()=>{r(await e(...o)),a=null},t))}return l}function e8({onSet:t,children:e}){const[r,a]=j.useState(""),[o,l]=j.useState(null),[u,f]=j.useState(null),h=j.useCallback(t8(300,QT,l),[]),d=j.useCallback(y=>{a(y.target.value),h(y.target.value)}),m=j.useCallback(y=>{y.preventDefault(),o?t(o):f(+new Date)});return j.useEffect(()=>{u&&o&&(new Date-u>500||t(o))},[o,u]),D.jsxs("form",{style:{maxWidth:"420px",margin:"0 auto",display:"block"},onSubmit:m,children:[D.jsxs("label",{style:{display:"block"},children:["Your handle or DID:"," ",D.jsx("input",{style:{margin:"0.5em 0",padding:"0.3em 0.5em",font:"inherit",borderRadius:"0.3em",border:"1px solid #444"},placeholder:"bad-example.com",value:r,onChange:d})]}),o?D.jsx(bg,{doc:o,children:D.jsx("button",{onClick:()=>t(o),children:"check"})}):D.jsx("br",{})]})}const WT=["wss://relay.xero.systems","wss://relay1.us-east.bsky.network","wss://relay1.us-west.bsky.network"];async function n8(t){const e=[],r=[],a=[];for(const o of WT){const l=new URL(o);l.protocol=l.protocol.replace("ws","http");let u;try{u=await FT(l,t)}catch{}if(u==="notfound"){r.push(l.hostname);continue}if(!u){a.push(l.hostname);continue}u.active||(console.log("rs",u),e.push(l.hostname))}return{deactivateds:e,missings:r,fails:a}}function r8({oof:t,children:e}){const r={},{deactivateds:a,missings:o,fails:l}=t;return a.forEach(u=>r[u]="deactivated"),o.forEach(u=>r[u]="not crawling"),l.forEach(u=>r[u]="check failed"),D.jsxs("p",{style:{fontSize:"0.8em",textAlign:"right",margin:"0"},children:[Object.keys(r).map(u=>D.jsxs(D.Fragment,{children:[D.jsx("code",{children:u}),": ",D.jsx("span",{style:{color:"#f64"},children:r[u]}),D.jsx("br",{})]})),D.jsx("strong",{children:"pds:"})," ",D.jsx("code",{children:t.doc.pds.hostname})," (",D.jsx("span",{style:{color:"#7f6"},children:"active"}),")",D.jsx("br",{})]})}function i8({actives:t}){const e=[];let r=0;return t.forEach(a=>{a.deactivateds.length>0||a.missings.length>0||a.fails.length>0?e.push(a):r+=1}),D.jsxs(D.Fragment,{children:[D.jsxs("p",{children:[r," account",r!==1&&"s"," on alternative PDSs checked out ok."]}),e.length>0&&D.jsxs(D.Fragment,{children:[D.jsxs("h3",{children:[e.length," account",e.length!==1&&"s"," found with relay problems"]}),e.map(a=>D.jsx("div",{style:{margin:"0.5rem 0"},children:D.jsx(bg,{doc:a.doc,children:D.jsx(r8,{oof:a})})},a.doc.did.val))]})]})}function a8({doc:t}){const[e,r]=j.useState({}),[a,o]=j.useState([]),[l,u]=j.useState([]),[f,h]=j.useState([]),[d,m]=j.useState([]),y=j.useCallback(async v=>{if(e[v])return;r(O=>({...O,[v]:!0}));let b;try{b=await QT(v)}catch{}if(!b){m(O=>[...O,{subject:v,reason:"resolution"}]);return}if(b.pds.hostname.endsWith(".host.bsky.network")){h(O=>[...O,b]);return}let x;try{x=await FT(b.pds,b.did)}catch{}if(x==="notfound"){m(O=>[...O,{subject:v,reason:"notfound"}]);return}if(!x){m(O=>[...O,{subject:v,reason:"pds getRepoStatus"}]);return}if(!x.active){u(O=>[...O,b]);return}const{deactivateds:S,missings:w,fails:E}=await n8(b.did);o(O=>[...O,{doc:b,deactivateds:S,missings:w,fails:E}])},[]);return j.useEffect(()=>{let v=!1;return(async()=>{y(t.did.val);const b=WL(t.pds,t.did,new KT("app.bsky.graph.follow"));for await(const x of b){if(v)break;y(x.subject)}})(),()=>v=!0},[t.did.val,t.pds]),D.jsxs("div",{style:{marginBottom:"4em"},children:[D.jsxs("h2",{children:["Checking following (",Object.keys(e).length,")…"]}),D.jsxs("p",{children:["Of your follows, ",d.length," failed resolution, ",f.length," are on bsky mushroom PDSs, and ",l.length," are actually deactivated."]}),D.jsx(i8,{actives:a}),D.jsxs("div",{style:{textAlign:"left"},children:[D.jsx("h3",{style:{margin:"3em 0 0"},children:"What these results mean"}),D.jsx("h4",{style:{marginBottom:"0",color:"#f64"},children:"Deactivated"}),D.jsxs("p",{children:["The relay has become desynchronized with this account, incorrectly marking it as not ",D.jsx("code",{children:"active"}),". All commits from this account will be blocked by the relay; none will be broadcast to relay consumers."]}),D.jsx("h4",{style:{marginBottom:"0",color:"#f64"},children:"Not crawling"}),D.jsx("p",{children:"The relay doesn't know about this account—perhaps it as never crawled its PDS. No content from this account will be discovered by the relay, so relay consumers won't see it."}),D.jsx("h4",{style:{marginBottom:"0",color:"#f64"},children:"Check failed"}),D.jsx("p",{children:"This account seems active, but something went wrong when checking its status with the relay. It might be fine!"}),D.jsx("h3",{style:{margin:"3em 0 0"},children:"Which relays are checked?"}),D.jsx("ul",{children:WT.map(v=>D.jsx("li",{children:D.jsx("code",{children:new URL(v).hostname})},v))}),D.jsx("h4",{style:{marginBottom:"0"},children:"Excluded relays"}),D.jsxs("ul",{children:[D.jsxs("li",{children:[D.jsx("code",{children:"atproto.africa"})," does not store repo status, so it can't get desynchronized, and won't drop commits."]}),D.jsxs("li",{children:[D.jsx("code",{children:"bsky.network"}),", running the old BGS code, does not implement ",D.jsx("code",{children:"com.atproto.sync.getRepoStatus"}),"."]}),D.jsx("li",{children:"All other known relays do not allow CORS XRPC requests, so we can't check from your browser."})]}),D.jsx("p",{children:"Accounts on Bluesky's mushroom PDSs are not checked because accounts seem to mainly desynchronize when migrating PDSs. Since accounts can now be migrated into the mushrooms, perhaps they should be checked too?"})]})]})}function o8(){const[t,e]=j.useState(null);return D.jsxs("div",{style:{maxWidth:"800px"},children:[D.jsx("h1",{children:"Oops deactivated checker"}),D.jsxs("p",{children:["This is a relay debugging tool to check if relays are blocking accounts you follow due to desynchronized ",D.jsx("code",{children:"active"})," state. This can happen when accounts migrate to an alternative PDS host."]}),t?D.jsx(bg,{doc:t,children:D.jsx("button",{style:{color:"#f90"},title:"clear",onClick:()=>e(null),children:"×"})}):D.jsx(e8,{onSet:e}),t&&D.jsx(a8,{doc:t}),D.jsx("p",{children:D.jsxs("small",{children:["False positive note: it's possible for a relay to set an account as ",D.jsx("code",{children:"deactivated"})," on purpose, but this moderation action is extremely rare."]})})]})}const s8=ym({colorSchemes:{dark:!0}}),JT=location.search.includes("deactivated");JT&&(document.title="Oops deactivated checker");wA.createRoot(document.getElementById("root")).render(D.jsx(CE,{injectFirst:!0,children:D.jsx(Jk,{theme:s8,children:JT?D.jsx(o8,{}):D.jsx(VL,{})})}));
232232+Error code: ${l.error}`);if(f==="#commit"){const{seq:y,repo:v,commit:b,rev:x,since:S,blocks:w,ops:E,prevData:O,time:k}=l;if(!((d=w==null?void 0:w.$bytes)!=null&&d.length))return{$type:"com.atproto.sync.subscribeRepos#commit",seq:y,repo:v,commit:b.$link,rev:x,since:S,blocks:new Uint8Array,ops:[],...O?{prevData:O.$link}:{},time:k};const R=Dx(w),A=UL(R),_=[];for(const $ of E){const I=$.action;if(I==="create"){if(!$.cid)continue;const L=A.get($.cid.$link);if(!L)continue;_.push({action:I,path:$.path,cid:$.cid.$link,record:L})}else if(I==="update"){if(!$.cid)continue;const L=A.get($.cid.$link);if(!L)continue;_.push({action:I,path:$.path,cid:$.cid.$link,...$.prev?{prev:$.prev.$link}:{},record:L})}else if(I==="delete")_.push({action:I,path:$.path,...$.prev?{prev:$.prev.$link}:{}});else throw new Error(`Unknown action: ${I}`)}return{$type:"com.atproto.sync.subscribeRepos#commit",seq:y,repo:v,commit:b.$link,rev:x,since:S,blocks:R,ops:_,...O?{prevData:O.$link}:{},time:k}}else if(f==="#sync"){const{seq:y,did:v,blocks:b,rev:x,time:S}=l,w=(m=b==null?void 0:b.$bytes)!=null&&m.length?Dx(b):new Uint8Array;return{$type:"com.atproto.sync.subscribeRepos#sync",seq:y,did:v,blocks:w,rev:x,time:S}}return{$type:`com.atproto.sync.subscribeRepos${f}`,...l}}preventReconnect(){this.reconnectTimeout&&clearTimeout(this.reconnectTimeout),this.reconnectTimeout=setTimeout(()=>{this.reconnect()},5e3)}reconnect(){var e;(e=this.ws)==null||e.close(),this.start(),this.emitter.emit("reconnect")}}function LL(t){if(!t||typeof t!="object"||!t.t||typeof t.t!="string"||!t.op||typeof t.op!="number")throw new Error("Invalid header received");return{t:t.t,op:t.op}}function UL(t){const e=new Map;for(const{cid:r,bytes:a}of $L(t).iterate())e.set(vL(r).$link,VT(a));return e}const BL="234567abcdefghijklmnopqrstuvwxyz",$x=t=>{let e=0;for(const r of t)e=e*32+BL.indexOf(r);return e},IL=/^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/,HL=t=>{if(!PL(t))throw new Error("invalid TID");const e=$x(t.slice(0,11)),r=$x(t.slice(11,13));return{timestamp:e,clockid:r}},PL=t=>t.length===13&&IL.test(t),GL="app.bsky.feed.like",Ys=32,Gi=32,eu=Ys*Gi;function YL({url:t,desc:e,includeEvents:r,onRecieveEvent:a}){const[o,l]=j.useState("connecting"),[u,f]=j.useState(0),[h,d]=j.useState(0),[m,y]=j.useState({idx:Array.from({length:Gi+2}).map(()=>0),recv:Array.from({length:Gi+2}).map(()=>0)});return j.useEffect(()=>{const v=(x,S)=>{if(r.has(x)&&(a(x,S),f(w=>w+1),x==="commit"&&S.ops.length===1)){const w=S.ops[0];try{const[E,O]=w.path.split("/");if(E===GL){const k=HL(O).timestamp/1e3,R=Date.parse(S.time),A=R-k,_=+new Date-R;let $,I;A<0?$=-1:A>=eu?$=Gi:$=Math.min(Math.floor(A/Ys),eu),_<0?I=-1:_>=eu?I=Gi:I=Math.min(Math.floor(_/Ys),eu),y(({idx:L,recv:C})=>(L=L.slice(),C=C.slice(),L[$+1]+=1,C[I+1]+=1,{idx:L,recv:C}))}}catch{}}},b=new jL({relay:t});return b.on("open",()=>l("connected")),b.on("close",()=>l("closed")),b.on("reconnect",()=>d(x=>x+1)),b.on("error",x=>{console.error("oops",x),l("errored")}),b.on("websocketError",()=>l("errored")),b.on("commit",x=>v("commit",x)),b.on("sync",x=>v("sync",x)),b.on("account",x=>v("account",x)),b.on("identity",x=>v("identity",x)),b.on("info",(...x)=>console.info("info event",...x)),b.on("unknown",x=>console.warn(`unknown event from ${t}`,x)),b.start(),()=>{b.close()}},[t,r]),D.jsxs("div",{className:"relay",children:[D.jsx("h2",{children:e}),D.jsx("p",{children:D.jsx("code",{children:t})}),D.jsxs("p",{children:["[",D.jsx("code",{children:o}),"] (",D.jsx("code",{children:u.toLocaleString()})," events)"]}),h>0&&D.jsxs("p",{children:["reconnects: ",D.jsx("code",{children:h})]}),D.jsx(Xp,{height:180,width:420,yAxis:[{label:"events",scaleType:"symlog"}],skipAnimation:!0,xAxis:[{data:[-1].concat(Array.from({length:Gi}).map((v,b)=>b*Ys)).concat(["+"]),label:"index latency (ms)"}],series:[{data:m.idx}]}),D.jsx(Xp,{height:180,width:420,yAxis:[{label:"events",scaleType:"symlog"}],skipAnimation:!0,xAxis:[{data:[-1].concat(Array.from({length:Gi}).map((v,b)=>b*Ys)).concat(["+"]),label:"receive latency (ms)"}],series:[{data:m.recv}]})]})}const Nx=[{url:"wss://atproto.africa",desc:"Blacksky"},{url:"wss://bsky.network",desc:"Bluesky primary (+Rainbow)"},{url:"wss://relay1.us-east.bsky.network",desc:"Bluesky sync1.1 east (+Rainbow)"},{url:"wss://relay1.us-west.bsky.network",desc:"Bluesky sync1.1 west (+Rainbow)"},{url:"wss://relay.fire.hose.cam",desc:"microcosm Montreal"},{url:"wss://relay3.fr.hose.cam",desc:"microcosm France"},{url:"wss://relay.upcloud.world",desc:"UpCloud relay"},{url:"wss://relay.hayescmd.net",desc:"@edavis.dev's relay"},{url:"wss://relay.xero.systems",desc:"@dane.is.extraordinarily.cool's relay"},{url:"wss://relay.feeds.blue",desc:"@mackuba.eu's relay (only self-hosters)"}],jx=1600,qL=6,Lx=10*60*1e3,Ux=(t,e,r)=>{};function VL(){const[t,e]=j.useState([]),[r,a]=j.useState(["commit","sync","account","identity","unknown"]),[o,l]=j.useState(new Set(r)),[u,f]=j.useState(()=>Ux),[h,d]=j.useState(()=>()=>{}),[m,y]=j.useState({series:[]}),[v,b]=j.useState(!1),[x,S]=j.useState("");j.useEffect(()=>{let O=performance.now(),k={},R=[],A=requestAnimationFrame(L),_=setTimeout($,Lx);f(()=>(C,z,H)=>{k[C]||(k[C]=0),k[C]+=1}),d(()=>()=>{clearTimeout(_),_=setTimeout($,Lx),b(!1),console.info("keepalive: disconnection timer reset")});function $(){console.info("disconnecting due to inactivity"),e([]),b(!0)}const I=setInterval(()=>{let C=performance.now(),z=C-O;R.length>=qL-1&&R.shift(),R.push({t:C,dt:z,counts:k}),O=C,k={}},jx);function L(){var H;let C=performance.now();const z=Object.keys(((H=R.at(-1))==null?void 0:H.counts)||{}).toSorted();y({xAxis:[{data:R.map(({t:q})=>(-(C-q)/1e3).toFixed(1)).concat(["now"]),label:"bucket (seconds ago)"}],series:z.map(q=>({label:q,data:R.map(({dt:it,counts:lt})=>lt[q]?(lt[q]/(it/1e3)).toFixed(1):null).concat([k[q]?(k[q]/(jx/1e3)).toFixed(1):null])}))}),A=requestAnimationFrame(L)}return()=>{f(()=>Ux),d(()=>()=>null),clearInterval(I),cancelAnimationFrame(A)}},[]);function w(O,k){b(!1),e(k?R=>R.includes(O)?R:[...R,O]:R=>R.includes(O)?R.filter(A=>A!==O):R),h()}function E(){if(!x)return"";try{let O;return x.includes("://")?O=new URL(x):O=new URL("https://"+x),O.protocol==="https:"?O.protocol="wss:":O.protocol==="http:"&&(O.protocol="ws:"),O.origin}catch{return""}}return D.jsxs(D.Fragment,{children:[D.jsx("h1",{children:"compare hoses"}),D.jsx("p",{children:D.jsxs("em",{children:["warning: enabling many relay connections requires a ",D.jsx("strong",{children:"lot"})," of bandwidth"]})}),D.jsxs("form",{style:{display:"block",textAlign:"left"},children:[Nx.map(({url:O,desc:k})=>D.jsx("p",{style:{margin:0},children:D.jsxs("label",{children:[D.jsx("input",{type:"checkbox",onChange:R=>w(O,R.target.checked),checked:t.includes(O)}),` ${k} `,"(",D.jsx("code",{children:O.slice(6)}),")"]})},O)),D.jsx("p",{style:{margin:0},children:D.jsxs("label",{children:[D.jsx("input",{type:"checkbox",onChange:O=>{const k=E();k&&w(k,O.target.checked)},checked:t.includes(E())})," ",D.jsx("input",{type:"text",placeholder:"wss://…",value:x,onChange:O=>{const k=E();e(R=>R.includes(k)?R.filter(A=>A!==k):R),S(O.target.value)},onKeyDown:O=>{if(O.key!=="Enter")return;O.preventDefault();const k=E();k&&w(k,!0)}})," ",x&&D.jsx("code",{children:E()})]})})]}),D.jsxs("form",{style:{display:"block",margin:"1rem 0"},children:[D.jsx("span",{children:"events: "}),r.map(O=>D.jsxs("label",{children:[D.jsx("input",{type:"checkbox",checked:o.has(O),onChange:k=>{l(R=>{const A=new Set(R);return k.target.checked?A.add(O):A.delete(O),A})}})," ",O]},O))]}),D.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:"2em",textAlign:"left"},children:t.map(O=>{const{desc:k}=Nx.find(R=>R.url===O)??{desc:"custom relay"};return D.jsx("div",{children:D.jsx(YL,{url:O,desc:k,includeEvents:o,onRecieveEvent:(R,A)=>u(O,R,A)})},O)})}),v&&D.jsx("p",{children:D.jsx("em",{children:"disconnected to save bandwidth due to inactivity"})}),D.jsx("div",{className:"throughputs",children:D.jsx(Xp,{height:300,yAxis:[{label:"events / sec"}],skipAnimation:!0,...m})})]})}class vf{constructor(e){Ct(this,"val");this.val=e}}class XL extends vf{}class KL extends vf{}class KT extends vf{}class ZL extends vf{}class QL{constructor(e,r,a){Ct(this,"did");Ct(this,"handle");Ct(this,"pds");this.did=e,this.handle=r,this.pds=a}}const ZT="https://slingshot.microcosm.blue";async function QT(t){const e=new URLSearchParams;e.set("identifier",t);const r=await fetch(`${ZT}/xrpc/com.bad-example.identity.resolveMiniDoc?${e}`);if(!r.ok)return r.text().then(h=>console.warn(`slingshot failed to resolve ${t} (${r.status})`,h)),null;const a=await r.json(),o=new XL(a.did),l=new KL(a.handle),u=new URL(a.pds);return new QL(o,l,u)}async function FT(t,e){const r=new URLSearchParams;r.set("did",e.val);const a=await fetch(`${t}xrpc/com.atproto.sync.getRepoStatus?${r}`);if(a.status===404)try{if((await a.json()).error==="RepoNotFound")return"notfound"}catch{}return a.ok?await a.json():(a.text().then(o=>console.warn(`slingshot failed to getRepoStatus ${t} / ${e} (${a.status})`,o)),null)}async function FL(t,e,r){const a=new URLSearchParams;a.set("repo",t.val),a.set("collection",e.val),a.set("rkey",r.val);const o=await fetch(`${ZT}/xrpc/com.atproto.repo.getRecord?${a}`);return o.ok?(await o.json()).value:(o.text().then(u=>console.warn(`slingshot failed to getRecord ${t}/${e}/${r} (${o.status})`,u)),null)}async function*WL(t,e,r){let a=null;do{const o=new URLSearchParams;o.set("repo",e.val),o.set("collection",r.val),o.set("limit","100"),a&&o.set("cursor",a);const l=await fetch(`${t}xrpc/com.atproto.repo.listRecords?${o}`);if(!l.ok)return l.text().then(f=>console.warn(`slingshot failed to listRecords ${e} / ${r} (${l.status})`,f)),null;const u=await l.json();for(const{value:f}of u.records)yield f;a=u.cursor}while(a)}function JL({did:t,link:e}){const a=`https://cdn.bsky.app/img/avatar_thumbnail/plain/${t.val}/${e}@jpeg`;return D.jsx("img",{alt:"avatar",src:a,style:{display:"block",width:"100%",height:"100%"}})}function bg({doc:t,children:e}){const[r,a]=j.useState(null),[o,l]=j.useState(null);return j.useEffect(()=>{let u=!1;return(async()=>{var m,y;`${t.did.val}`;const f=await FL(t.did,new KT("app.bsky.actor.profile"),new ZL("self")),h=(y=(m=f==null?void 0:f.avatar)==null?void 0:m.ref)==null?void 0:y.$link;h&&!u&&a(h);const d=f==null?void 0:f.displayName;d&&!u&&l(d)})(),()=>u=!0},[t.did.val]),D.jsxs("div",{style:{display:"flex",textAlign:"left",alignItems:"center",background:"#333",padding:"0.5em 0.6em",gap:"0.6em",borderRadius:"0.3em"},children:[D.jsx("div",{style:{background:"#000",height:"42px",width:"42px",display:"flex",justifyContent:"center",alignItems:"center",color:"#858",fontSize:"0.8em",borderRadius:"100%",flexShrink:"0",overflow:"hidden"},children:r?D.jsx(JL,{did:t.did,link:r}):D.jsx(D.Fragment,{children:"…"})}),D.jsxs("div",{style:{flexGrow:"1"},children:[D.jsx("h3",{style:{margin:0,fontSize:"1em"},children:o||t.handle.val}),D.jsxs("p",{style:{fontSize:"1em",margin:0,lineHeight:"1",opacity:"0.8"},children:[o&&D.jsxs(D.Fragment,{children:[D.jsx("code",{children:t.handle.val}),D.jsx("br",{})]}),D.jsx("code",{children:t.did.val})]})]}),e&&D.jsx("div",{children:e})]})}function t8(t,e,r){let a=null,o=null;function l(...u){o=u,a===null&&(a=setTimeout(async()=>{r(await e(...o)),a=null},t))}return l}function e8({onSet:t,children:e}){const[r,a]=j.useState(""),[o,l]=j.useState(null),[u,f]=j.useState(null),h=j.useCallback(t8(300,QT,l),[]),d=j.useCallback(y=>{a(y.target.value),h(y.target.value)}),m=j.useCallback(y=>{y.preventDefault(),o?t(o):f(+new Date)});return j.useEffect(()=>{u&&o&&(new Date-u>500||t(o))},[o,u]),D.jsxs("form",{style:{maxWidth:"420px",margin:"0 auto",display:"block"},onSubmit:m,children:[D.jsxs("label",{style:{display:"block"},children:["Your handle or DID:"," ",D.jsx("input",{style:{margin:"0.5em 0",padding:"0.3em 0.5em",font:"inherit",borderRadius:"0.3em",border:"1px solid #444"},placeholder:"bad-example.com",value:r,onChange:d})]}),o?D.jsx(bg,{doc:o,children:D.jsx("button",{onClick:()=>t(o),children:"check"})}):D.jsx("br",{})]})}const WT=["wss://relay.xero.systems","wss://relay1.us-east.bsky.network","wss://relay1.us-west.bsky.network"];async function n8(t){const e=[],r=[],a=[];for(const o of WT){const l=new URL(o);l.protocol=l.protocol.replace("ws","http");let u;try{u=await FT(l,t)}catch{}if(u==="notfound"){r.push(l.hostname);continue}if(!u){a.push(l.hostname);continue}u.active||(console.log("rs",u),e.push(l.hostname))}return{deactivateds:e,missings:r,fails:a}}function r8({oof:t,children:e}){const r={},{deactivateds:a,missings:o,fails:l}=t;return a.forEach(u=>r[u]="deactivated"),o.forEach(u=>r[u]="not crawling"),l.forEach(u=>r[u]="check failed"),D.jsxs("p",{style:{fontSize:"0.8em",textAlign:"right",margin:"0"},children:[Object.keys(r).map(u=>D.jsxs(D.Fragment,{children:[D.jsx("code",{children:u}),": ",D.jsx("span",{style:{color:"#f64"},children:r[u]}),D.jsx("br",{})]})),D.jsx("strong",{children:"pds:"})," ",D.jsx("code",{children:t.doc.pds.hostname})," (",D.jsx("span",{style:{color:"#7f6"},children:"active"}),")",D.jsx("br",{})]})}function i8({actives:t}){const e=[];let r=0;return t.forEach(a=>{a.deactivateds.length>0||a.missings.length>0||a.fails.length>0?e.push(a):r+=1}),D.jsxs(D.Fragment,{children:[D.jsxs("p",{children:[r," account",r!==1&&"s"," on alternative PDSs checked out ok."]}),e.length>0&&D.jsxs(D.Fragment,{children:[D.jsxs("h3",{children:[e.length," account",e.length!==1&&"s"," found with relay problems"]}),e.map(a=>D.jsx("div",{style:{margin:"0.5rem 0"},children:D.jsx(bg,{doc:a.doc,children:D.jsx(r8,{oof:a})})},a.doc.did.val))]})]})}function a8({doc:t}){const[e,r]=j.useState({}),[a,o]=j.useState([]),[l,u]=j.useState([]),[f,h]=j.useState([]),[d,m]=j.useState([]),y=j.useCallback(async v=>{if(e[v])return;r(O=>({...O,[v]:!0}));let b;try{b=await QT(v)}catch{}if(!b){m(O=>[...O,{subject:v,reason:"resolution"}]);return}if(b.pds.hostname.endsWith(".host.bsky.network")){h(O=>[...O,b]);return}let x;try{x=await FT(b.pds,b.did)}catch{}if(x==="notfound"){m(O=>[...O,{subject:v,reason:"notfound"}]);return}if(!x){m(O=>[...O,{subject:v,reason:"pds getRepoStatus"}]);return}if(!x.active){u(O=>[...O,b]);return}const{deactivateds:S,missings:w,fails:E}=await n8(b.did);o(O=>[...O,{doc:b,deactivateds:S,missings:w,fails:E}])},[]);return j.useEffect(()=>{let v=!1;return(async()=>{y(t.did.val);const b=WL(t.pds,t.did,new KT("app.bsky.graph.follow"));for await(const x of b){if(v)break;y(x.subject)}})(),()=>v=!0},[t.did.val,t.pds]),D.jsxs("div",{style:{marginBottom:"4em"},children:[D.jsxs("h2",{children:["Checking following (",Object.keys(e).length,")…"]}),D.jsxs("p",{children:["Of your follows, ",d.length," failed resolution, ",f.length," are on bsky mushroom PDSs, and ",l.length," are actually deactivated."]}),D.jsx(i8,{actives:a}),D.jsxs("div",{style:{textAlign:"left"},children:[D.jsx("h3",{style:{margin:"3em 0 0"},children:"What these results mean"}),D.jsx("h4",{style:{marginBottom:"0",color:"#f64"},children:"Deactivated"}),D.jsxs("p",{children:["The relay has become desynchronized with this account, incorrectly marking it as not ",D.jsx("code",{children:"active"}),". All commits from this account will be blocked by the relay; none will be broadcast to relay consumers."]}),D.jsx("h4",{style:{marginBottom:"0",color:"#f64"},children:"Not crawling"}),D.jsx("p",{children:"The relay doesn't know about this account—perhaps it as never crawled its PDS. No content from this account will be discovered by the relay, so relay consumers won't see it."}),D.jsx("h4",{style:{marginBottom:"0",color:"#f64"},children:"Check failed"}),D.jsx("p",{children:"This account seems active, but something went wrong when checking its status with the relay. It might be fine!"}),D.jsx("h3",{style:{margin:"3em 0 0"},children:"Which relays are checked?"}),D.jsx("ul",{children:WT.map(v=>D.jsx("li",{children:D.jsx("code",{children:new URL(v).hostname})},v))}),D.jsx("h4",{style:{marginBottom:"0"},children:"Excluded relays"}),D.jsxs("ul",{children:[D.jsxs("li",{children:[D.jsx("code",{children:"atproto.africa"})," does not store repo status, so it can't get desynchronized, and won't drop commits."]}),D.jsxs("li",{children:[D.jsx("code",{children:"bsky.network"}),", running the old BGS code, does not implement ",D.jsx("code",{children:"com.atproto.sync.getRepoStatus"}),"."]}),D.jsx("li",{children:"All other known relays do not allow CORS XRPC requests, so we can't check from your browser."})]}),D.jsx("p",{children:"Accounts on Bluesky's mushroom PDSs are not checked because accounts seem to mainly desynchronize when migrating PDSs. Since accounts can now be migrated into the mushrooms, perhaps they should be checked too?"})]})]})}function o8(){const[t,e]=j.useState(null);return D.jsxs("div",{style:{maxWidth:"800px"},children:[D.jsx("h1",{children:"Oops deactivated checker"}),D.jsxs("p",{children:["This is a relay debugging tool to check if relays are blocking accounts you follow due to desynchronized ",D.jsx("code",{children:"active"})," state. This can happen when accounts migrate to an alternative PDS host."]}),t?D.jsx(bg,{doc:t,children:D.jsx("button",{style:{color:"#f90"},title:"clear",onClick:()=>e(null),children:"×"})}):D.jsx(e8,{onSet:e}),t&&D.jsx(a8,{doc:t}),D.jsx("p",{children:D.jsxs("small",{children:["False positive note: it's possible for a relay to set an account as ",D.jsx("code",{children:"deactivated"})," on purpose, but this moderation action is extremely rare."]})})]})}const s8=ym({colorSchemes:{dark:!0}}),JT=location.search.includes("deactivated");JT&&(document.title="Oops deactivated checker");wA.createRoot(document.getElementById("root")).render(D.jsx(CE,{injectFirst:!0,children:D.jsx(Jk,{theme:s8,children:JT?D.jsx(o8,{}):D.jsx(VL,{})})}));