@buoy-gg/network 7.0.79 → 7.0.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/lib/commonjs/index.js +1 -1
- package/lib/commonjs/index.web.js +1 -1
- package/lib/commonjs/network/components/NetworkEventDetailView.js +1 -1
- package/lib/commonjs/network/components/NetworkEventDetailView.web.js +1 -1
- package/lib/commonjs/network/components/NetworkModal.js +1 -1
- package/lib/commonjs/network/components/sockets/NetworkSocketsButton.js +1 -0
- package/lib/commonjs/network/components/sockets/NetworkSocketsScreen.js +1 -0
- package/lib/commonjs/network/components/sockets/SocketFrameView.js +1 -0
- package/lib/commonjs/network/components/sockets/SocketListView.js +1 -0
- package/lib/commonjs/network/components/sockets/SocketRows.js +1 -0
- package/lib/commonjs/network/components/sockets/SocketTricksView.js +1 -0
- package/lib/commonjs/network/conditions/NetworkConditionsControl.js +1 -1
- package/lib/commonjs/network/conditions/profiles.js +1 -1
- package/lib/commonjs/network/conditions/store.js +1 -1
- package/lib/commonjs/network/hooks/useSocketsState.js +1 -0
- package/lib/commonjs/network/overrides/engine.js +1 -1
- package/lib/commonjs/network/overrides/matchRule.js +1 -1
- package/lib/commonjs/network/overrides/overrideRulesStore.js +1 -1
- package/lib/commonjs/network/sockets/findProblems.js +1 -0
- package/lib/commonjs/network/sockets/foldFrames.js +1 -0
- package/lib/commonjs/network/sockets/format.js +1 -0
- package/lib/commonjs/network/sockets/readMessage.js +1 -0
- package/lib/commonjs/network/sockets/socketMoments.js +1 -0
- package/lib/commonjs/network/sockets/socketStore.js +1 -0
- package/lib/commonjs/network/sockets/socketsSyncAdapter.js +1 -0
- package/lib/commonjs/network/sockets/types.js +1 -0
- package/lib/commonjs/network/sockets/webSocketPatch.js +1 -0
- package/lib/commonjs/network/sync/networkSyncAdapter.js +1 -1
- package/lib/commonjs/network/utils/networkEventStore.js +1 -1
- package/lib/commonjs/network/utils/networkListener.js +4 -4
- package/lib/commonjs/network/utils/startNetworkCapture.js +1 -1
- package/lib/commonjs/preset.js +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.web.js +1 -1
- package/lib/module/network/components/NetworkEventDetailView.js +1 -1
- package/lib/module/network/components/NetworkEventDetailView.web.js +1 -1
- package/lib/module/network/components/NetworkModal.js +1 -1
- package/lib/module/network/components/sockets/NetworkSocketsButton.js +1 -0
- package/lib/module/network/components/sockets/NetworkSocketsScreen.js +1 -0
- package/lib/module/network/components/sockets/SocketFrameView.js +1 -0
- package/lib/module/network/components/sockets/SocketListView.js +1 -0
- package/lib/module/network/components/sockets/SocketRows.js +1 -0
- package/lib/module/network/components/sockets/SocketTricksView.js +1 -0
- package/lib/module/network/conditions/NetworkConditionsControl.js +1 -1
- package/lib/module/network/conditions/profiles.js +1 -1
- package/lib/module/network/conditions/store.js +1 -1
- package/lib/module/network/hooks/useSocketsState.js +1 -0
- package/lib/module/network/overrides/engine.js +1 -1
- package/lib/module/network/overrides/matchRule.js +1 -1
- package/lib/module/network/overrides/overrideRulesStore.js +1 -1
- package/lib/module/network/sockets/findProblems.js +1 -0
- package/lib/module/network/sockets/foldFrames.js +1 -0
- package/lib/module/network/sockets/format.js +1 -0
- package/lib/module/network/sockets/readMessage.js +1 -0
- package/lib/module/network/sockets/socketMoments.js +1 -0
- package/lib/module/network/sockets/socketStore.js +1 -0
- package/lib/module/network/sockets/socketsSyncAdapter.js +1 -0
- package/lib/module/network/sockets/types.js +1 -0
- package/lib/module/network/sockets/webSocketPatch.js +1 -0
- package/lib/module/network/sync/networkSyncAdapter.js +1 -1
- package/lib/module/network/utils/networkEventStore.js +1 -1
- package/lib/module/network/utils/networkListener.js +4 -4
- package/lib/module/network/utils/startNetworkCapture.js +1 -1
- package/lib/module/preset.js +1 -1
- package/lib/typescript/index.d.ts +11 -0
- package/lib/typescript/index.web.d.ts +4 -0
- package/lib/typescript/network/components/sockets/NetworkSocketsButton.d.ts +17 -0
- package/lib/typescript/network/components/sockets/NetworkSocketsScreen.d.ts +19 -0
- package/lib/typescript/network/components/sockets/SocketFrameView.d.ts +9 -0
- package/lib/typescript/network/components/sockets/SocketListView.d.ts +9 -0
- package/lib/typescript/network/components/sockets/SocketRows.d.ts +19 -0
- package/lib/typescript/network/components/sockets/SocketTricksView.d.ts +23 -0
- package/lib/typescript/network/conditions/store.d.ts +1 -1
- package/lib/typescript/network/hooks/useSocketsState.d.ts +3 -0
- package/lib/typescript/network/overrides/overrideRulesStore.d.ts +19 -0
- package/lib/typescript/network/overrides/types.d.ts +14 -0
- package/lib/typescript/network/sockets/findProblems.d.ts +31 -0
- package/lib/typescript/network/sockets/foldFrames.d.ts +50 -0
- package/lib/typescript/network/sockets/format.d.ts +19 -0
- package/lib/typescript/network/sockets/readMessage.d.ts +41 -0
- package/lib/typescript/network/sockets/socketMoments.d.ts +24 -0
- package/lib/typescript/network/sockets/socketStore.d.ts +94 -0
- package/lib/typescript/network/sockets/socketsSyncAdapter.d.ts +66 -0
- package/lib/typescript/network/sockets/types.d.ts +139 -0
- package/lib/typescript/network/sockets/webSocketPatch.d.ts +57 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts +3 -1
- package/lib/typescript/network/types/index.d.ts +6 -0
- package/lib/typescript/network/utils/networkListener.d.ts +6 -0
- package/lib/web/index.mjs +58 -58
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"use strict";import{paceByteCount as
|
|
1
|
+
"use strict";import{paceByteCount as be,paceResponse as me,uploadBodySize as ge,uploadSize as Re,utf8Length as we}from"../conditions/pacer";import{isBuoyRequest as Le}from"@buoy-gg/shared-ui";import{resolveRequestUrl as K}from"./resolveRequestUrl";import{nextRequestId as V}from"./requestId";import{runConditionXhr as He}from"../conditions/xhr";import{snapshotNetworkCondition as ue}from"../conditions/store";import{throwIfAborted as ee,waitBeforeDispatch as qe}from"../conditions/wait";import{accountCheckFor as Se,overrideForRequest as le,overrideRulesLoaded as _e,overrideRulesNeedBody as Te,overrideRulesReady as pe}from"../overrides/engine";import{runOverride as Ee,hasPendingStandardRequest as ve}from"../overrides/applyToXhr";import{canSynthesizeResponse as te,synthesizeFetchError as Oe,synthesizeResponse as Xe}from"../overrides/applyToFetch";import{markForOutcome as he}from"../overrides/types";import{httpStatusText as ke,xhrCompletionKind as Ne}from"./httpCompletion";import{realNow as X}from"./realNow";let M;function fe(g){const e=globalThis.__buoyHeaderInjectors;if(!e||e.length===0)return void 0;let t;for(const s of e){try{const l=s(g);if(l)t={...t,...l}}catch{}}return t}function xe(){if(M!==void 0)return M;M=null;try{if(typeof __DEV__!=="undefined"&&!__DEV__)return M;const g=require("react-native");const e=g?.NativeModules?.SourceCode;const t=e?.getConstants?.().scriptURL??e?.scriptURL;if(typeof t==="string"&&/^https?:\/\//.test(t)){const s=new URL(t);M=`${s.protocol}//${s.host}`}}catch{M=null}return M}let j=false;const Ce=Symbol.for("buoy.earlyNetwork");function Be(g){const e={};if(!g)return e;if(typeof Headers!=="undefined"&&g instanceof Headers)g.forEach((t,s)=>{e[s]=t});else if(Array.isArray(g))for(const[t,s]of g)e[t]=s;else Object.assign(e,g);return e}export const BUOY_FETCH_INTERCEPTOR_FLAG="__buoyNetworkFetchIntercepted";export const BUOY_XHR_INTERCEPTOR_FLAG="__buoyNetworkXhrIntercepted";const Ie=["text/event-stream","application/x-ndjson","application/stream+json"];const se="[streaming\u2026]";let ne=false;function Ae(){if(ne)return true;const g=globalThis.document;ne=!!g?.querySelector?.("[ng-version]");return ne}function ye(g){if(!g)return false;const e=g.toLowerCase();return Ie.some(t=>e.includes(t))}const Me="__BUOY_INTERNAL_HOSTS__";function je(g){const e=globalThis[Me];if(!(e instanceof Set)||e.size===0)return false;let t;try{t=new URL(g).host.toLowerCase()}catch{return false}for(const s of e){if(typeof s==="string"&&s&&t===s.toLowerCase())return true}return false}class Ue{listeners=[];isListening=false;patchedFetch=null;patchedXHROpen=null;patchedXHRSend=null;patchedXHRSetRequestHeader=null;fetchGeneration=0;xhrGeneration=0;constructor(){this.originalFetch=globalThis.fetch;this.originalXHROpen=XMLHttpRequest.prototype.open;this.originalXHRSend=XMLHttpRequest.prototype.send;this.originalXHRSetRequestHeader=XMLHttpRequest.prototype.setRequestHeader}shouldIgnoreUrl(e){if(Le(e))return true;try{const t=new URL(e);if(t.hostname==="keygen.sh"||t.hostname.endsWith(".keygen.sh"))return true;if((t.hostname==="buoy.gg"||t.hostname==="www.buoy.gg")&&(t.pathname.startsWith("/api/license")||t.pathname.startsWith("/api/settings/")))return true;const s=t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="10.0.2.2";if(s&&["8081","8802"].includes(t.port))return true;const l=xe();if(l&&`${t.protocol}//${t.host}`===l)return true}catch{return false}return je(e)}emit(e){this.listeners.forEach(t=>{try{t(e)}catch(s){}})}parseUrl(e){let t=null;const s=e.indexOf("?");if(s>-1){t={};e.substring(s+1).split("&").forEach(l=>{const[u,o]=l.split("=");if(u&&o!==void 0){t[u]=decodeURIComponent(o.replace(/\+/g," "))}})}return{url:s>-1?e.substring(0,s):e,params:t}}async readStreamedBody(e,t=256*1024){const s=e.body;if(!s||typeof s.getReader!=="function"){const f=await e.text();return f.length>t?{body:`${f.slice(0,t)}
|
|
2
2
|
|
|
3
|
-
... [truncated]`,size:f.length}:{body:f,size:f.length}}const l=s.getReader();const u=new TextDecoder;let
|
|
3
|
+
... [truncated]`,size:f.length,truncated:true}:{body:f,size:f.length}}const l=s.getReader();const u=new TextDecoder;let o="";let h=0;let m=false;try{for(;;){const{done:f,value:i}=await l.read();if(f)break;h+=i?.byteLength??0;if(!m){o+=u.decode(i,{stream:true});if(h>=t){o=`${o.slice(0,t)}
|
|
4
4
|
|
|
5
|
-
... [truncated]`;
|
|
5
|
+
... [truncated]`;m=true;break}}}if(!m)o+=u.decode()}finally{try{void Promise.resolve(l.cancel()).catch(()=>{})}catch{}try{l.releaseLock()}catch{}}return{body:o,size:h,truncated:m||void 0}}async processResponseBody(e,t=1024*1024){try{const s=e.headers.get("content-length");if(s){const o=parseInt(s,10);if(!isNaN(o)&&o>t){return{body:`[Response too large: ${this.formatBytes(o)}]`,size:o,truncated:true,json:false}}}const l=await e.text();const u=l.length;if(u>t){const o=l.substring(0,t);const h=u-t;return{body:`${o}
|
|
6
6
|
|
|
7
|
-
... [truncated, ${this.formatBytes(p)} omitted]`,size:u,truncated:true}}try{return{body:JSON.parse(l),size:u,truncated:false}}catch{return{body:l,size:u,truncated:false}}}catch(s){return{body:"~~~ unable to read body ~~~",size:0,truncated:false}}}formatBytes(e){if(e===0)return"0 B";const t=1024;const s=["B","KB","MB","GB"];const l=Math.floor(Math.log(e)/Math.log(t));return`${Math.round(e/Math.pow(t,l)*100)/100} ${s[l]}`}handleXHRResponse(e,t,s,l,u,i,p,g,f=false,a="xhr",n,y){const R=g?v()-g:0;const c=e._buoyTtfb;const L=c===void 0?void 0:{ttfb:c,download:Math.max(0,R-c)};if(f||e.status===0){this.emit({type:"error",timestamp:new Date,duration:R,request:{id:t||"unknown",url:s,method:l,headers:u,data:i,params:p||void 0,client:a},error:{message:f?"Request failed":"Network error or request aborted"},override:n,condition:y});return}let o;let h=0;try{const r=e.response;if(e.responseType==="json"&&r){o=r;h=JSON.stringify(r).length}else if(e.responseType===""||e.responseType==="text"){if(e.responseText){h=e.responseText.length;try{o=JSON.parse(e.responseText)}catch{o=e.responseText}}}else if(e.responseType==="arraybuffer"){if(r){try{const m=new TextDecoder("utf-8").decode(r);h=m.length;try{o=JSON.parse(m)}catch{o=m}}catch(m){o=`[arraybuffer response - ${r.byteLength||0} bytes]`;h=r.byteLength||0}}else{o=`[arraybuffer response - no data]`;h=0}}else if(e.responseType==="blob"){if(r instanceof Blob){o=`[blob response - ${r.size} bytes, type: ${r.type||"unknown"}]`;h=r.size}else if(r){try{o=typeof r==="string"?JSON.parse(r):r;h=JSON.stringify(o).length}catch{o=`[blob response - unable to parse]`;h=0}}else{o=`[blob response - no data]`;h=0}}else if(r){if(typeof r==="string"){o=r;h=r.length;try{o=JSON.parse(r)}catch{}}else if(typeof r==="object"){o=r;h=JSON.stringify(r).length}else{o=String(r);h=String(r).length}}}catch(r){o="~~~ unable to read body ~~~"}if(y?.downloadBytesPerSecond&&(!e.responseType||e.responseType==="text"||e.responseType==="json")){const r=e._response;if(typeof r==="string")h=fe(r)}const H={};try{const r=e.getAllResponseHeaders();if(r){r.split("\r\n").forEach(m=>{if(m){const w=m.indexOf(": ");if(w>0){const k=m.substring(0,w).toLowerCase();const q=m.substring(w+2);H[k]=q}}})}}catch{}if(Te(e.status)==="response"){this.emit({type:"response",timestamp:new Date,duration:R,timings:L,request:{id:t||"unknown",url:s,method:l,headers:u,data:i,params:p||void 0,client:a},response:{status:e.status,statusText:_e(e.status,e.statusText),headers:H,body:o,size:h},override:n,condition:y})}else{this.emit({type:"error",timestamp:new Date,duration:R,request:{id:t||"unknown",url:s,method:l,headers:u,data:i,params:p||void 0,client:a},response:{status:e.status,statusText:e.statusText,headers:H,body:o,size:h},error:{message:"Network Error"},override:n,condition:y})}}isFetchInterceptionLive(){return globalThis.fetch[BUOY_FETCH_INTERCEPTOR_FLAG]===true}isXhrInterceptionLive(){return XMLHttpRequest.prototype.send[BUOY_XHR_INTERCEPTOR_FLAG]===true}isInterceptionLive(){return this.isFetchInterceptionLive()&&this.isXhrInterceptionLive()}startListening(){const e=this.isListening&&this.isFetchInterceptionLive();const t=this.isListening&&this.isXhrInterceptionLive();if(e&&t){return}if(!e){this.installFetchInterceptor()}if(!t){this.installXhrInterceptor()}this.isListening=true;if(__DEV__){}}installFetchInterceptor(){const e=this;const t=globalThis.fetch.bind(globalThis);this.originalFetch=globalThis.fetch;const s=++this.fetchGeneration;const l=async(u,i)=>{const p=W(typeof u==="string"?u:u instanceof URL?u.href:u.url);if(s!==e.fetchGeneration){return t(u,i)}if(i?.__buoyInternal||e.shouldIgnoreUrl(p)){const d=M;M=true;try{return t(u,i)}finally{M=d}}const g=v();const f=Y("fetch");const a=typeof u==="object"&&"method"in u?u:void 0;const n=i?.method??a?.method??"GET";const y=i?.signal!==void 0?i.signal:a?.signal;V(y);const R=re(p);const c=R?.uploadBytesPerSecond?{...R,uploadCap:he(u,i)}:R;const{url:L,params:o}=e.parseUrl(p);let h={};const H=i?.headers??a?.headers;if(H){if(H instanceof Headers){H.forEach((d,_)=>{h[_]=d})}else if(Array.isArray(H)){H.forEach(([d,_])=>{h[d]=_})}else{h={...H}}}Object.assign(h,ce(p));let r;if(i?.body){if(typeof i.body==="string"){try{r=JSON.parse(i.body)}catch{r=i.body}}else{r=i.body}}const m=h["X-Request-Client"]||h["x-request-client"]||"fetch";if(c?.profile!=="offline"&&Q())await ie();let w=typeof i?.body==="string"?i.body:void 0;if(w===void 0&&i?.body===void 0&&a&&!a.bodyUsed&&n!=="GET"&&n!=="HEAD"&&we()){try{w=(await e.readStreamedBody(a.clone())).body}catch{}}const k=c?.profile!=="offline"&&Q()?ge(p,n,h,w):null;if(k)await k;const q=c?.profile!=="offline"&&Q()?oe(p,n,h,w):null;const O=q?ae(q):void 0;e.emit({type:"request",timestamp:new Date,request:{id:f||"unknown",url:L,method:n,headers:h,data:r,params:o||void 0,client:m},override:O,condition:c});const B=d=>{try{r=JSON.parse(d)}catch{r=d}e.emit({type:"request-body",timestamp:new Date,request:{id:f||"unknown",url:L,method:n,data:r,client:m}})};if(i?.body===void 0&&a&&typeof w==="string"){B(w)}else if(!c?.uploadBytesPerSecond&&i?.body===void 0&&a&&!a.bodyUsed&&n!=="GET"&&n!=="HEAD"){try{const d=a.clone();void e.readStreamedBody(d).then(({body:_})=>{if(_)B(_)}).catch(()=>{})}catch{}}try{if(c||q)await be((c?.delayMs??0)+(q?.delayMs??0),y);V(y);if(c?.profile==="offline")throw new TypeError("Network request failed (Buoy Offline)");if(q?.kind==="fail")throw qe(q);if(c?.uploadBytesPerSecond){await ue(c.uploadCap?.knownBytes??0,c.uploadBytesPerSecond,y);V(y)}let d;if(q?.kind==="respond"){d=Se(p,q)}else{const E=M;M=true;let S;try{S=t(u,i)}finally{M=E}d=await S}if(c?.downloadBytesPerSecond){const E={};d.headers.forEach((x,N)=>{E[N.toLowerCase()]=x});let S=0;let X=0;const $=[];const G=(x,N)=>{const D=new Uint8Array(X);let se=0;for(const ne of $){D.set(ne,se);se+=ne.byteLength}let K=x?new TextDecoder().decode(D):Z;if(x&&E["content-type"]?.includes("json")){try{K=JSON.parse(K)}catch{}}e.emit({type:N!==void 0?"error":"response",timestamp:new Date,duration:v()-g,request:{id:f,url:L,method:n,headers:h,data:r,params:o||void 0,client:m},response:{status:d.status,statusText:d.statusText,headers:E,body:K,size:S},...N!==void 0?{error:{message:N instanceof Error?N.message:String(N)}}:{},override:O,condition:c})};G(false);return le(d,c.downloadBytesPerSecond,y,(x,N)=>{if(x){S+=x.byteLength;const D=Math.min(x.byteLength,65536-X);if(D>0){$.push(x.slice(0,D));X+=D}}else G(true,N)},n.toUpperCase()==="HEAD")}const _=v()-g;const F=typeof d.body?.getReader==="function"||de(d.headers?.get?.("content-type"));const I=d.clone();const{body:P,size:J}=F?{body:Z,size:0}:await e.processResponseBody(I);const U=v()-g;const z=Math.max(0,U-_);const j=z>0?{ttfb:_,download:z}:void 0;const T={};d.headers.forEach((E,S)=>{T[S.toLowerCase()]=E});e.emit({type:"response",timestamp:new Date,duration:U,timings:j,request:{id:f||"unknown",url:L,method:n,headers:h,data:r,params:o||void 0,client:m},response:{status:d.status,statusText:d.statusText,headers:T,body:P,size:J},override:O,condition:c});if(F){const E=d.headers?.get?.("content-type");void e.readStreamedBody(I,de(E)?void 0:1024*1024).then(S=>{let X=S.body;if(E?.includes("json")){try{X=JSON.parse(S.body)}catch{}}e.emit({type:"response",timestamp:new Date,duration:v()-g,timings:j,request:{id:f||"unknown",url:L,method:n,headers:h,data:r,params:o||void 0,client:m},response:{status:d.status,statusText:d.statusText,headers:T,body:X,size:S.size},override:O,condition:c})}).catch(()=>{})}return d}catch(d){const _=v()-g;e.emit({type:"error",timestamp:new Date,duration:_,request:{id:f||"unknown",url:L,method:n,headers:h,data:r,params:o||void 0,client:m},error:{message:d instanceof Error?d.message:"Network error",stack:d instanceof Error?d.stack:void 0},override:O,condition:c});throw d}};l[BUOY_FETCH_INTERCEPTOR_FLAG]=true;this.patchedFetch=l;globalThis.fetch=this.patchedFetch}installXhrInterceptor(){const e=this;const t=XMLHttpRequest.prototype.open;const s=XMLHttpRequest.prototype.send;const l=XMLHttpRequest.prototype.setRequestHeader;this.originalXHROpen=t;this.originalXHRSend=s;this.originalXHRSetRequestHeader=l;const u=++this.xhrGeneration;const i=new WeakMap;const p=function(a,n,y,R,c){const L=t.call(this,a,n,y??true,R,c);if(u===e.xhrGeneration){i.get(this)?.();const o=this;o._buoyRequestId=Y("xhr");o._method=a;o._url=W(n);o._buoyStartTime=v();o._requestHeaders={};o._buoyAsync=y!==false;o._buoyRestoreAbort?.();o._buoyWaitedForRules=false;o._buoyHeldSend=false;o._buoyOpenSeq=(o._buoyOpenSeq??0)+1}return L};const g=function(a,n){const y=this;if(u===e.xhrGeneration&&y._requestHeaders){y._requestHeaders[a]=n}return l.call(this,a,n)};const f=function(a){if(u!==e.xhrGeneration||M){return s.call(this,a)}if(He(this))throw new DOMException("The request has already been sent.","InvalidStateError");if(this.readyState!==1||this._sent){return s.call(this,a)}const n=this;if(n._buoyHeldSend)throw new DOMException("The request has already been sent.","InvalidStateError");if(!Re()&&n._buoyAsync!==false&&!n._buoyWaitedForRules){n._buoyWaitedForRules=true;n._buoyHeldSend=true;const T=n._buoyOpenSeq;let E=false;const S=Object.prototype.hasOwnProperty.call(this,"abort")?this.abort:void 0;const X=()=>{if(n._buoyRestoreAbort!==X)return;n._buoyRestoreAbort=void 0;if(S)this.abort=S;else delete this.abort};n._buoyRestoreAbort=X;const $=S??Object.getPrototypeOf(this).abort;this.abort=function(){E=true;n._buoyHeldSend=false;X();return $.call(this)};const G=()=>{X();if(E||n._buoyOpenSeq!==T)return;n._buoyHeldSend=false;if(this.readyState===1&&!this._sent)f.call(this,a)};void ie().then(G,G);return}const y=n._buoyRequestId;const R=n._method||"GET";const c=n._url||"";const L=v();n._buoyStartTime=L;n._buoyTtfb=void 0;const o=n._requestHeaders||{};Object.assign(o,ce(c));if(e.shouldIgnoreUrl(c)){return s.call(this,a)}const{url:h,params:H}=e.parseUrl(c);let r;if(a){if(typeof a==="string"){try{r=JSON.parse(a)}catch{r=a}}else{r=a}}const m=o["X-Request-Client"]||o["x-request-client"];const w=o["Accept"]||o["accept"]||"";const k=w.includes("application/json")&&w.includes("text/plain")&&w.includes("*/*")&&!ke();const q=m||(k?"axios":"xhr");const O=re(c);const B=O?.uploadBytesPerSecond?{...O,uploadCap:pe(a)}:O;const d=B?.profile==="offline"?null:oe(c,R,o,typeof a==="string"?a:void 0);const _=d?ae(d):void 0;e.emit({type:"request",timestamp:new Date,request:{id:y||"unknown",url:h,method:R,headers:o,data:r,params:H||void 0,client:q},override:_,condition:B});let F=false;const I=(T=false)=>{if(F)return;F=true;e.handleXHRResponse(this,y||"unknown",h,R,o,r,H,L||0,T,q,_,B);P()};const P=()=>{i.delete(this);this.removeEventListener("load",J);this.removeEventListener("error",U);this.removeEventListener("abort",z);this.removeEventListener("readystatechange",j)};i.set(this,P);const J=()=>{I(false)};const U=()=>{I(true)};const z=()=>{I(true)};const j=()=>{if(this.readyState===2){const T=this;if(T._buoyTtfb===void 0&&T._buoyStartTime){T._buoyTtfb=v()-T._buoyStartTime}}if(this.readyState===4&&!F){I(false)}};this.addEventListener("load",J);this.addEventListener("error",U);this.addEventListener("abort",z);this.addEventListener("readystatechange",j);if(B){me(this,c,B,d,()=>s.call(this,a),a);return}if(d){const T=Le(this,c,d,()=>s.call(this,a));if(T)return}return s.call(this,a)};f[BUOY_XHR_INTERCEPTOR_FLAG]=true;this.patchedXHROpen=p;this.patchedXHRSend=f;this.patchedXHRSetRequestHeader=g;XMLHttpRequest.prototype.open=this.patchedXHROpen;XMLHttpRequest.prototype.send=this.patchedXHRSend;XMLHttpRequest.prototype.setRequestHeader=this.patchedXHRSetRequestHeader}stopListening(){if(!this.isListening){return}this.fetchGeneration++;this.xhrGeneration++;if(this.patchedFetch&&globalThis.fetch===this.patchedFetch){globalThis.fetch=this.originalFetch}if(this.patchedXHROpen&&XMLHttpRequest.prototype.open===this.patchedXHROpen){XMLHttpRequest.prototype.open=this.originalXHROpen}if(this.patchedXHRSend&&XMLHttpRequest.prototype.send===this.patchedXHRSend){XMLHttpRequest.prototype.send=this.originalXHRSend}if(this.patchedXHRSetRequestHeader&&XMLHttpRequest.prototype.setRequestHeader===this.patchedXHRSetRequestHeader){XMLHttpRequest.prototype.setRequestHeader=this.originalXHRSetRequestHeader}this.patchedFetch=null;this.patchedXHROpen=null;this.patchedXHRSend=null;this.patchedXHRSetRequestHeader=null;this.isListening=false;if(__DEV__){}}adoptEarlyRequests(){const e=globalThis;const t=e[ve]??e.__BUOY_EARLY_NETWORK__;if(t?.version!==1)return;for(const s of t.adopt()){try{if(s.kind==="fetch")this.adoptEarlyFetch(s);else this.adoptEarlyXhr(s)}catch{}}}adoptEarlyFetch(e){const{input:t,init:s}=e;const l=typeof t==="object"&&"method"in t?t:void 0;const u=W(typeof t==="string"?t:t instanceof URL?t.href:t.url);if(this.shouldIgnoreUrl(u))return;const i=Y("fetch");const p=s?.method??l?.method??"GET";const{url:g,params:f}=this.parseUrl(u);const a=Oe(s?.headers??l?.headers);let n=s?.body;if(typeof n==="string"){try{n=JSON.parse(n)}catch{}}const y=a["X-Request-Client"]||a["x-request-client"]||"fetch";const R=()=>({id:i,url:g,method:p,headers:a,data:n,params:f||void 0,client:y});this.emit({type:"request",timestamp:new Date(e.start),request:R()});if(e.requestCopy){void this.readStreamedBody(e.requestCopy).then(({body:c})=>{try{n=JSON.parse(c)}catch{n=c}this.emit({type:"request-body",timestamp:new Date,request:{id:i,url:g,method:p,data:n,client:y}})}).catch(()=>{})}void e.settled.then(async({response:c,clone:L,error:o,headersAt:h})=>{if(!c){this.emit({type:"error",timestamp:new Date,duration:h-e.start,request:R(),error:{message:o instanceof Error?o.message:"Network error",stack:o instanceof Error?o.stack:void 0}});return}const{body:H,size:r}=L?await this.processResponseBody(L):{body:Z,size:0};const m=Math.max(0,v()-e.start);const w=h-e.start;const k={};c.headers.forEach((q,O)=>{k[O.toLowerCase()]=q});this.emit({type:"response",timestamp:new Date,duration:m,timings:m>w?{ttfb:w,download:m-w}:void 0,request:R(),response:{status:c.status,statusText:c.statusText,headers:k,body:H,size:r}})})}adoptEarlyXhr(e){const t=W(e.url);if(this.shouldIgnoreUrl(t))return;const s=Y("xhr");const l=e.method.toUpperCase();const{url:u,params:i}=this.parseUrl(t);let p=e.body;if(typeof p==="string"){try{p=JSON.parse(p)}catch{}}const g=e.headers["X-Request-Client"]||e.headers["x-request-client"]||"xhr";this.emit({type:"request",timestamp:new Date(e.start),request:{id:s,url:u,method:l,headers:e.headers,data:p,params:i||void 0,client:g}});const f=e.xhr;const a=()=>{const n=(f._buoyEarlyEnd??v())-e.start;this.handleXHRResponse(f,s,u,l,e.headers,p,i,v()-n,false,g)};if(f.readyState===4)a();else f.addEventListener("loadend",a,{once:true})}addListener(e){this.listeners.push(e);return()=>{const t=this.listeners.indexOf(e);if(t>-1){this.listeners.splice(t,1)}}}removeAllListeners(){this.listeners=[]}get isActive(){return this.isListening}get listenerCount(){return this.listeners.length}}let C=null;const te=()=>{if(!C){C=new Be}return C};export const networkListener=te;export const startNetworkListener=()=>te().startListening();export const stopNetworkListener=()=>C?.stopListening();export const addNetworkListener=b=>te().addListener(b);export const removeAllNetworkListeners=()=>C?.removeAllListeners();export const isNetworkListening=()=>C?.isActive??false;export const getNetworkListenerCount=()=>C?.listenerCount??0;export const isNetworkInterceptionLive=()=>C?.isInterceptionLive()??false;
|
|
7
|
+
... [truncated, ${this.formatBytes(h)} omitted]`,size:u,truncated:true,json:false}}try{return{body:JSON.parse(l),size:u,truncated:false,json:true}}catch{return{body:l,size:u,truncated:false,json:false}}}catch(s){return{body:"~~~ unable to read body ~~~",json:false,unread:true,size:0,truncated:false}}}formatBytes(e){if(e===0)return"0 B";const t=1024;const s=["B","KB","MB","GB"];const l=Math.floor(Math.log(e)/Math.log(t));return`${Math.round(e/Math.pow(t,l)*100)/100} ${s[l]}`}handleXHRResponse(e,t,s,l,u,o,h,m,f=false,i="xhr",n,b){const L=m?X()-m:0;const c=e._buoyTtfb;const q=c===void 0?void 0:{ttfb:c,download:Math.max(0,L-c)};if(f||e.status===0){this.emit({type:"error",timestamp:new Date,duration:L,request:{id:t||"unknown",url:s,method:l,headers:u,data:o,params:h||void 0,client:i},error:{message:f?"Request failed":"Network error or request aborted"},override:n,condition:b});return}let r;let y=false;let R=false;let p=0;try{const a=e.response;if(e.responseType==="json"){const H=e.status===204||e.status===205||e.status===304||l.toUpperCase()==="HEAD"||e.getResponseHeader?.("content-length")==="0";if(a===null){r=void 0;R=!H;p=0}else{r=a;y=true;p=JSON.stringify(a).length}}else if(e.responseType===""||e.responseType==="text"){if(e.responseText){p=e.responseText.length;try{r=JSON.parse(e.responseText);y=true}catch{r=e.responseText}}}else if(e.responseType==="arraybuffer"){if(a){try{const H=new TextDecoder("utf-8").decode(a);p=H.length;try{r=JSON.parse(H);y=true}catch{r=H}}catch(H){r=`[arraybuffer response - ${a.byteLength||0} bytes]`;R=true;p=a.byteLength||0}}else{r=`[arraybuffer response - no data]`;R=true;p=0}}else if(e.responseType==="blob"){if(a instanceof Blob){r=`[blob response - ${a.size} bytes, type: ${a.type||"unknown"}]`;R=true;p=a.size}else if(a){try{r=typeof a==="string"?JSON.parse(a):a;p=JSON.stringify(r).length}catch{r=`[blob response - unable to parse]`;R=true;p=0}}else{r=`[blob response - no data]`;R=true;p=0}}else if(a){if(typeof a==="string"){r=a;p=a.length;try{r=JSON.parse(a);y=true}catch{}}else if(typeof a==="object"){r=a;p=JSON.stringify(a).length}else{r=String(a);p=String(a).length}}}catch(a){r="~~~ unable to read body ~~~";R=true}if(b?.downloadBytesPerSecond&&(!e.responseType||e.responseType==="text"||e.responseType==="json")){const a=e._response;if(typeof a==="string")p=we(a)}const E={};try{const a=e.getAllResponseHeaders();if(a){a.split("\r\n").forEach(H=>{if(H){const w=H.indexOf(": ");if(w>0){const T=H.substring(0,w).toLowerCase();const v=H.substring(w+2);E[T]=v}}})}}catch{}if(Ne(e.status)==="response"){this.emit({type:"response",timestamp:new Date,duration:L,timings:q,request:{id:t||"unknown",url:s,method:l,headers:u,data:o,params:h||void 0,client:i},response:{status:e.status,statusText:ke(e.status,e.statusText),headers:E,body:r,size:p,json:y,unread:R||void 0},override:n,condition:b})}else{this.emit({type:"error",timestamp:new Date,duration:L,request:{id:t||"unknown",url:s,method:l,headers:u,data:o,params:h||void 0,client:i},response:{status:e.status,statusText:e.statusText,headers:E,body:r,size:p},error:{message:"Network Error"},override:n,condition:b})}}isFetchInterceptionLive(){return globalThis.fetch[BUOY_FETCH_INTERCEPTOR_FLAG]===true}isXhrInterceptionLive(){return XMLHttpRequest.prototype.send[BUOY_XHR_INTERCEPTOR_FLAG]===true}isInterceptionLive(){return this.isFetchInterceptionLive()&&this.isXhrInterceptionLive()}startListening(){const e=this.isListening&&this.isFetchInterceptionLive();const t=this.isListening&&this.isXhrInterceptionLive();if(e&&t){return}if(!e){this.installFetchInterceptor()}if(!t){this.installXhrInterceptor()}this.isListening=true;if(__DEV__){}}installFetchInterceptor(){const e=this;const t=globalThis.fetch.bind(globalThis);this.originalFetch=globalThis.fetch;const s=++this.fetchGeneration;const l=async(u,o)=>{const h=K(typeof u==="string"?u:u instanceof URL?u.href:u.url);if(s!==e.fetchGeneration){return t(u,o)}if(o?.__buoyInternal||e.shouldIgnoreUrl(h)){const d=j;j=true;try{return t(u,o)}finally{j=d}}const m=X();const f=V("fetch");const i=typeof u==="object"&&"method"in u?u:void 0;const n=o?.method??i?.method??"GET";const b=o?.signal!==void 0?o.signal:i?.signal;ee(b);const L=ue(h);const c=L?.uploadBytesPerSecond?{...L,uploadCap:ge(u,o)}:L;const{url:q,params:r}=e.parseUrl(h);let y={};const R=o?.headers??i?.headers;if(R){if(R instanceof Headers){R.forEach((d,S)=>{y[S]=d})}else if(Array.isArray(R)){R.forEach(([d,S])=>{y[d]=S})}else{y={...R}}}Object.assign(y,fe(h));let p;if(o?.body){if(typeof o.body==="string"){try{p=JSON.parse(o.body)}catch{p=o.body}}else{p=o.body}}const E=y["X-Request-Client"]||y["x-request-client"]||"fetch";if(c?.profile!=="offline"&&te())await pe();let a=typeof o?.body==="string"?o.body:void 0;if(a===void 0&&o?.body===void 0&&i&&!i.bodyUsed&&n!=="GET"&&n!=="HEAD"&&Te()){try{a=(await e.readStreamedBody(i.clone())).body}catch{}}const H=c?.profile!=="offline"&&te()?Se(h,n,y,a):null;if(H)await H;const w=c?.profile!=="offline"&&te()?le(h,n,y,a):null;const T=w?he(w):void 0;e.emit({type:"request",timestamp:new Date,request:{id:f||"unknown",url:q,method:n,headers:y,data:p,params:r||void 0,client:E},override:T,condition:c});const v=d=>{try{p=JSON.parse(d)}catch{p=d}e.emit({type:"request-body",timestamp:new Date,request:{id:f||"unknown",url:q,method:n,data:p,client:E}})};if(o?.body===void 0&&i&&typeof a==="string"){v(a)}else if(!c?.uploadBytesPerSecond&&o?.body===void 0&&i&&!i.bodyUsed&&n!=="GET"&&n!=="HEAD"){try{const d=i.clone();void e.readStreamedBody(d).then(({body:S})=>{if(S)v(S)}).catch(()=>{})}catch{}}try{if(c||w)await qe((c?.delayMs??0)+(w?.delayMs??0),b);ee(b);if(c?.profile==="offline")throw new TypeError("Network request failed (Buoy Offline)");if(w?.kind==="fail")throw Oe(w);if(c?.uploadBytesPerSecond){await be(c.uploadCap?.knownBytes??0,c.uploadBytesPerSecond,b);ee(b)}let d;if(w?.kind==="respond"){d=Xe(h,w)}else{const k=j;j=true;let _;try{_=t(u,o)}finally{j=k}d=await _}if(c?.downloadBytesPerSecond){const k={};d.headers.forEach((N,x)=>{k[x.toLowerCase()]=N});let _=0;let A=0;const G=[];const oe=(N,x)=>{const F=new Uint8Array(A);let ie=0;for(const ce of G){F.set(ce,ie);ie+=ce.byteLength}let Q=N?new TextDecoder().decode(F):se;let ae=false;if(N&&k["content-type"]?.includes("json")){try{Q=JSON.parse(Q);ae=true}catch{}}const de=_>A;const Z=!N||x!==void 0;e.emit({type:x!==void 0?"error":"response",timestamp:new Date,duration:X()-m,request:{id:f,url:q,method:n,headers:y,data:p,params:r||void 0,client:E},response:{status:d.status,statusText:d.statusText,headers:k,body:Q,size:_,json:ae&&!de&&!Z,truncated:de&&!Z,...Z?{unread:true}:{}},...x!==void 0?{error:{message:x instanceof Error?x.message:String(x)}}:{},override:T,condition:c})};oe(false);return me(d,c.downloadBytesPerSecond,b,(N,x)=>{if(N){_+=N.byteLength;const F=Math.min(N.byteLength,65536-A);if(F>0){G.push(N.slice(0,F));A+=F}}else oe(true,x)},n.toUpperCase()==="HEAD")}const S=X()-m;const U=typeof d.body?.getReader==="function"||ye(d.headers?.get?.("content-type"));const I=d.clone();const{body:J,size:P,json:$,truncated:W,unread:Y}=U?{body:se,size:0,json:false,truncated:false,unread:true}:await e.processResponseBody(I);const O=X()-m;const z=Math.max(0,O-S);const D=z>0?{ttfb:S,download:z}:void 0;const C={};d.headers.forEach((k,_)=>{C[_.toLowerCase()]=k});e.emit({type:"response",timestamp:new Date,duration:O,timings:D,request:{id:f||"unknown",url:q,method:n,headers:y,data:p,params:r||void 0,client:E},response:{status:d.status,statusText:d.statusText,headers:C,body:J,size:P,json:$,truncated:W||void 0,unread:Y||void 0},override:T,condition:c});if(U){const k=d.headers?.get?.("content-type");void e.readStreamedBody(I,ye(k)?void 0:1024*1024).then(_=>{let A=_.body;let G=false;if(k?.toLowerCase().includes("json")){try{A=JSON.parse(_.body);G=true}catch{}}e.emit({type:"response",timestamp:new Date,duration:X()-m,timings:D,request:{id:f||"unknown",url:q,method:n,headers:y,data:p,params:r||void 0,client:E},response:{status:d.status,statusText:d.statusText,headers:C,body:A,size:_.size,json:G,truncated:_.truncated},override:T,condition:c})}).catch(()=>{})}return d}catch(d){const S=X()-m;e.emit({type:"error",timestamp:new Date,duration:S,request:{id:f||"unknown",url:q,method:n,headers:y,data:p,params:r||void 0,client:E},error:{message:d instanceof Error?d.message:"Network error",stack:d instanceof Error?d.stack:void 0},override:T,condition:c});throw d}};l[BUOY_FETCH_INTERCEPTOR_FLAG]=true;this.patchedFetch=l;globalThis.fetch=this.patchedFetch}installXhrInterceptor(){const e=this;const t=XMLHttpRequest.prototype.open;const s=XMLHttpRequest.prototype.send;const l=XMLHttpRequest.prototype.setRequestHeader;this.originalXHROpen=t;this.originalXHRSend=s;this.originalXHRSetRequestHeader=l;const u=++this.xhrGeneration;const o=new WeakMap;const h=function(i,n,b,L,c){const q=t.call(this,i,n,b??true,L,c);if(u===e.xhrGeneration){o.get(this)?.();const r=this;r._buoyRequestId=V("xhr");r._method=i;r._url=K(n);r._buoyStartTime=X();r._requestHeaders={};r._buoyAsync=b!==false;r._buoyRestoreAbort?.();r._buoyWaitedForRules=false;r._buoyHeldSend=false;r._buoyOpenSeq=(r._buoyOpenSeq??0)+1}return q};const m=function(i,n){const b=this;if(u===e.xhrGeneration&&b._requestHeaders){b._requestHeaders[i]=n}return l.call(this,i,n)};const f=function(i){if(u!==e.xhrGeneration||j){return s.call(this,i)}if(ve(this))throw new DOMException("The request has already been sent.","InvalidStateError");if(this.readyState!==1||this._sent){return s.call(this,i)}const n=this;if(n._buoyHeldSend)throw new DOMException("The request has already been sent.","InvalidStateError");if(!_e()&&n._buoyAsync!==false&&!n._buoyWaitedForRules){n._buoyWaitedForRules=true;n._buoyHeldSend=true;const O=n._buoyOpenSeq;let z=false;const D=Object.prototype.hasOwnProperty.call(this,"abort")?this.abort:void 0;const C=()=>{if(n._buoyRestoreAbort!==C)return;n._buoyRestoreAbort=void 0;if(D)this.abort=D;else delete this.abort};n._buoyRestoreAbort=C;const k=D??Object.getPrototypeOf(this).abort;this.abort=function(){z=true;n._buoyHeldSend=false;C();return k.call(this)};const _=()=>{C();if(z||n._buoyOpenSeq!==O)return;n._buoyHeldSend=false;if(this.readyState===1&&!this._sent)f.call(this,i)};void pe().then(_,_);return}const b=n._buoyRequestId;const L=n._method||"GET";const c=n._url||"";const q=X();n._buoyStartTime=q;n._buoyTtfb=void 0;const r=n._requestHeaders||{};Object.assign(r,fe(c));if(e.shouldIgnoreUrl(c)){return s.call(this,i)}const{url:y,params:R}=e.parseUrl(c);let p;if(i){if(typeof i==="string"){try{p=JSON.parse(i)}catch{p=i}}else{p=i}}const E=r["X-Request-Client"]||r["x-request-client"];const a=r["Accept"]||r["accept"]||"";const H=a.includes("application/json")&&a.includes("text/plain")&&a.includes("*/*")&&!Ae();const w=E||(H?"axios":"xhr");const T=ue(c);const v=T?.uploadBytesPerSecond?{...T,uploadCap:Re(i)}:T;const d=v?.profile==="offline"?null:le(c,L,r,typeof i==="string"?i:void 0);const S=d?he(d):void 0;e.emit({type:"request",timestamp:new Date,request:{id:b||"unknown",url:y,method:L,headers:r,data:p,params:R||void 0,client:w},override:S,condition:v});let U=false;const I=(O=false)=>{if(U)return;U=true;e.handleXHRResponse(this,b||"unknown",y,L,r,p,R,q||0,O,w,S,v);J()};const J=()=>{o.delete(this);this.removeEventListener("load",P);this.removeEventListener("error",$);this.removeEventListener("abort",W);this.removeEventListener("readystatechange",Y)};o.set(this,J);const P=()=>{I(false)};const $=()=>{I(true)};const W=()=>{I(true)};const Y=()=>{if(this.readyState===2){const O=this;if(O._buoyTtfb===void 0&&O._buoyStartTime){O._buoyTtfb=X()-O._buoyStartTime}}if(this.readyState===4&&!U){I(false)}};this.addEventListener("load",P);this.addEventListener("error",$);this.addEventListener("abort",W);this.addEventListener("readystatechange",Y);if(v){He(this,c,v,d,()=>s.call(this,i),i);return}if(d){const O=Ee(this,c,d,()=>s.call(this,i));if(O)return}return s.call(this,i)};f[BUOY_XHR_INTERCEPTOR_FLAG]=true;this.patchedXHROpen=h;this.patchedXHRSend=f;this.patchedXHRSetRequestHeader=m;XMLHttpRequest.prototype.open=this.patchedXHROpen;XMLHttpRequest.prototype.send=this.patchedXHRSend;XMLHttpRequest.prototype.setRequestHeader=this.patchedXHRSetRequestHeader}stopListening(){if(!this.isListening){return}this.fetchGeneration++;this.xhrGeneration++;if(this.patchedFetch&&globalThis.fetch===this.patchedFetch){globalThis.fetch=this.originalFetch}if(this.patchedXHROpen&&XMLHttpRequest.prototype.open===this.patchedXHROpen){XMLHttpRequest.prototype.open=this.originalXHROpen}if(this.patchedXHRSend&&XMLHttpRequest.prototype.send===this.patchedXHRSend){XMLHttpRequest.prototype.send=this.originalXHRSend}if(this.patchedXHRSetRequestHeader&&XMLHttpRequest.prototype.setRequestHeader===this.patchedXHRSetRequestHeader){XMLHttpRequest.prototype.setRequestHeader=this.originalXHRSetRequestHeader}this.patchedFetch=null;this.patchedXHROpen=null;this.patchedXHRSend=null;this.patchedXHRSetRequestHeader=null;this.isListening=false;if(__DEV__){}}adoptEarlyRequests(){const e=globalThis;const t=e[Ce]??e.__BUOY_EARLY_NETWORK__;if(t?.version!==1)return;for(const s of t.adopt()){try{if(s.kind==="fetch")this.adoptEarlyFetch(s);else this.adoptEarlyXhr(s)}catch{}}}adoptEarlyFetch(e){const{input:t,init:s}=e;const l=typeof t==="object"&&"method"in t?t:void 0;const u=K(typeof t==="string"?t:t instanceof URL?t.href:t.url);if(this.shouldIgnoreUrl(u))return;const o=V("fetch");const h=s?.method??l?.method??"GET";const{url:m,params:f}=this.parseUrl(u);const i=Be(s?.headers??l?.headers);let n=s?.body;if(typeof n==="string"){try{n=JSON.parse(n)}catch{}}const b=i["X-Request-Client"]||i["x-request-client"]||"fetch";const L=()=>({id:o,url:m,method:h,headers:i,data:n,params:f||void 0,client:b});this.emit({type:"request",timestamp:new Date(e.start),request:L()});if(e.requestCopy){void this.readStreamedBody(e.requestCopy).then(({body:c})=>{try{n=JSON.parse(c)}catch{n=c}this.emit({type:"request-body",timestamp:new Date,request:{id:o,url:m,method:h,data:n,client:b}})}).catch(()=>{})}void e.settled.then(async({response:c,clone:q,error:r,headersAt:y})=>{if(!c){this.emit({type:"error",timestamp:new Date,duration:y-e.start,request:L(),error:{message:r instanceof Error?r.message:"Network error",stack:r instanceof Error?r.stack:void 0}});return}const{body:R,size:p,json:E,truncated:a,unread:H}=q?await this.processResponseBody(q):{body:se,size:0,json:false,truncated:false,unread:true};const w=Math.max(0,X()-e.start);const T=y-e.start;const v={};c.headers.forEach((d,S)=>{v[S.toLowerCase()]=d});this.emit({type:"response",timestamp:new Date,duration:w,timings:w>T?{ttfb:T,download:w-T}:void 0,request:L(),response:{status:c.status,statusText:c.statusText,headers:v,body:R,size:p,json:E,truncated:a||void 0,unread:H||void 0}})})}adoptEarlyXhr(e){const t=K(e.url);if(this.shouldIgnoreUrl(t))return;const s=V("xhr");const l=e.method.toUpperCase();const{url:u,params:o}=this.parseUrl(t);let h=e.body;if(typeof h==="string"){try{h=JSON.parse(h)}catch{}}const m=e.headers["X-Request-Client"]||e.headers["x-request-client"]||"xhr";this.emit({type:"request",timestamp:new Date(e.start),request:{id:s,url:u,method:l,headers:e.headers,data:h,params:o||void 0,client:m}});const f=e.xhr;const i=()=>{const n=(f._buoyEarlyEnd??X())-e.start;this.handleXHRResponse(f,s,u,l,e.headers,h,o,X()-n,false,m)};if(f.readyState===4)i();else f.addEventListener("loadend",i,{once:true})}addListener(e){this.listeners.push(e);return()=>{const t=this.listeners.indexOf(e);if(t>-1){this.listeners.splice(t,1)}}}removeAllListeners(){this.listeners=[]}get isActive(){return this.isListening}get listenerCount(){return this.listeners.length}}let B=null;const re=()=>{if(!B){B=new Ue}return B};export const networkListener=re;export const startNetworkListener=()=>re().startListening();export const stopNetworkListener=()=>B?.stopListening();export const addNetworkListener=g=>re().addListener(g);export const removeAllNetworkListeners=()=>B?.removeAllListeners();export const isNetworkListening=()=>B?.isActive??false;export const getNetworkListenerCount=()=>B?.listenerCount??0;export const isNetworkInterceptionLive=()=>B?.isInterceptionLive()??false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{networkEventStore as n}from"./networkEventStore";import{
|
|
1
|
+
"use strict";import{networkEventStore as n}from"./networkEventStore";import{socketStore as i}from"../sockets/socketStore";import{overrideRulesStore as s}from"../overrides/overrideRulesStore";import{registerLocalNetworkFloatingTool as u}from"../conditions/NetworkFloatingTool";export function startNetworkCapture(){const t=typeof navigator!=="undefined"&&navigator.product==="ReactNative";if(!t&&(typeof window==="undefined"||typeof document==="undefined"))return()=>{};void s.ensureLoaded();i.install();const o=t?()=>{}:u();const r=n.subscribeToEvents(()=>{});let e=false;return()=>{if(e)return;e=true;o();r()}}
|
package/lib/module/preset.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{NetworkConditionsIcon as t}from"./network/conditions/NetworkConditionsIcon";import{createToolFactory as r,defineToolPreset as i}from"@buoy-gg/shared-ui";import{NetworkModal as n}from"./network/components/NetworkModal";import{overrideRulesStore as
|
|
1
|
+
"use strict";import{NetworkConditionsIcon as t}from"./network/conditions/NetworkConditionsIcon";import{createToolFactory as r,defineToolPreset as i}from"@buoy-gg/shared-ui";import{NetworkModal as n}from"./network/components/NetworkModal";import{overrideRulesStore as s}from"./network/overrides/overrideRulesStore";import{networkEventStore as a}from"./network/utils/networkEventStore";import{socketStore as d}from"./network/sockets/socketStore";import{jsx as m}from"react/jsx-runtime";const e=typeof navigator!=="undefined"&&navigator.product==="ReactNative";if(e)void s.ensureLoaded();if(e&&typeof __DEV__!=="undefined"&&__DEV__){a.startBootCapture();d.install()}export const networkToolPreset=i({id:"network",name:"NET",description:"Network request logger",slot:"both",icon:({size:o})=>m(t,{size:o}),component:n,props:{enableSharedModalDimensions:false}});export const createNetworkTool=r(networkToolPreset,o=>({enableSharedModalDimensions:o.enableSharedModalDimensions??false}));
|
|
@@ -34,6 +34,17 @@ type OverrideRulesRemote, } from "./network/overrides/overrideRulesStore";
|
|
|
34
34
|
export { draftFromEvent } from "./network/overrides/presets";
|
|
35
35
|
export { MAX_RULES as MAX_OVERRIDE_RULES, FREE_TIER_RULE_LIMIT as FREE_TIER_OVERRIDE_LIMIT, STATUS_MIN as OVERRIDE_STATUS_MIN, STATUS_MAX as OVERRIDE_STATUS_MAX, type OverrideRule, type OverrideRuleKind, type OverrideFailKind, type NetworkOverrideMark, } from "./network/overrides/types";
|
|
36
36
|
export { useIgnoredPatterns, ignoredPatternsStore, urlMatchesIgnoredPattern, isUrlIgnored, } from "@buoy-gg/shared-ui";
|
|
37
|
+
export { NetworkSocketsScreen } from "./network/components/sockets/NetworkSocketsScreen";
|
|
38
|
+
export { useSocketsState } from "./network/hooks/useSocketsState";
|
|
39
|
+
export { socketsSyncAdapter, querySockets, type SocketQuery } from "./network/sockets/socketsSyncAdapter";
|
|
40
|
+
export type { SocketAction, SocketActionResult, SocketConnection, SocketFrame, SocketProblem, SocketProblemKind, SocketsState, SocketState, } from "./network/sockets/types";
|
|
41
|
+
/**
|
|
42
|
+
* @internal — the desktop dashboard mirrors this (`applySnapshot`) and sends
|
|
43
|
+
* Tricks to the device through `setRemote`.
|
|
44
|
+
*/
|
|
45
|
+
export { socketStore, type SocketsRemote } from "./network/sockets/socketStore";
|
|
46
|
+
/** @internal — the Events tool's socket source (opened, closed, faked). */
|
|
47
|
+
export { onSocketMoment, type SocketMoment } from "./network/sockets/socketMoments";
|
|
37
48
|
export { networkSyncAdapter } from "./network/sync/networkSyncAdapter";
|
|
38
49
|
export { NetworkBodyResolverProvider, type NetworkBodyResolver, type ResolvedNetworkBody, } from "./network/sync/networkBodyResolver";
|
|
39
50
|
/** @internal */
|
|
@@ -18,6 +18,10 @@ export { networkListener, startNetworkListener, stopNetworkListener, addNetworkL
|
|
|
18
18
|
export type { NetworkingEvent, NetworkingEventListener, } from "./network/utils/networkListener";
|
|
19
19
|
export { networkEventStore } from "./network/utils/networkEventStore";
|
|
20
20
|
export { networkSavedStore, flagsForEventId, } from "./network/utils/networkSavedStore";
|
|
21
|
+
export { socketStore } from "./network/sockets/socketStore";
|
|
22
|
+
export { socketsSyncAdapter, querySockets } from "./network/sockets/socketsSyncAdapter";
|
|
23
|
+
export { onSocketMoment } from "./network/sockets/socketMoments";
|
|
24
|
+
export type { SocketConnection, SocketFrame, SocketProblem, SocketsState } from "./network/sockets/types";
|
|
21
25
|
export type { NetworkEvent, NetworkStats, NetworkFilter, NetworkTimings, } from "./network/types";
|
|
22
26
|
export { useNetworkEvents, FREE_TIER_REQUEST_LIMIT } from "./network/hooks/useNetworkEvents";
|
|
23
27
|
export { useNetworkEventList } from "./network/hooks/useNetworkEventList";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header button for the Sockets screen, and the live "a socket is open" light.
|
|
3
|
+
*
|
|
4
|
+
* A self-subscribing leaf like the Overrides button, so socket traffic never
|
|
5
|
+
* re-renders the modal shell. It stays hidden until the app opens its first
|
|
6
|
+
* socket: most apps have none, and the header is already full. The ⋯ menu
|
|
7
|
+
* always has a Sockets item.
|
|
8
|
+
*
|
|
9
|
+
* Green = a socket is open. A red count = problems Buoy found. Amber = a
|
|
10
|
+
* trick is on (frozen, blocked, offline), because a socket Buoy is breaking
|
|
11
|
+
* looks exactly like a real bug.
|
|
12
|
+
*/
|
|
13
|
+
export declare function NetworkSocketsButton({ onPress, paused }: {
|
|
14
|
+
onPress: () => void;
|
|
15
|
+
paused: boolean;
|
|
16
|
+
}): import("react").JSX.Element | null;
|
|
17
|
+
//# sourceMappingURL=NetworkSocketsButton.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type MutableRefObject } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The Sockets screen of the Network tool: the list, one message, and Tricks.
|
|
4
|
+
*
|
|
5
|
+
* A router only (rule3/rule4): it holds which of the three is showing and
|
|
6
|
+
* renders all three as stable siblings that gate themselves. Back steps out of
|
|
7
|
+
* a message or Tricks before it leaves the screen, through the same
|
|
8
|
+
* `backHandlerRef` hand-off the Overrides screen uses.
|
|
9
|
+
*/
|
|
10
|
+
export declare function NetworkSocketsScreen({ visible, paused, isPro, onUpgradePress, backHandlerRef, onTitleChange, }: {
|
|
11
|
+
visible: boolean;
|
|
12
|
+
paused: boolean;
|
|
13
|
+
isPro: boolean;
|
|
14
|
+
onUpgradePress: () => void;
|
|
15
|
+
backHandlerRef?: MutableRefObject<(() => boolean) | null>;
|
|
16
|
+
/** The header's title for what's showing: "Sockets", "Message" or "Tricks". */
|
|
17
|
+
onTitleChange?: (title: string) => void;
|
|
18
|
+
}): import("react").JSX.Element | null;
|
|
19
|
+
//# sourceMappingURL=NetworkSocketsScreen.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SocketFrame } from "../../sockets/types";
|
|
2
|
+
export declare function SocketFrameView({ visible, frameId, isPro, onUpgradePress, onEdit, }: {
|
|
3
|
+
visible: boolean;
|
|
4
|
+
frameId: string | null;
|
|
5
|
+
isPro: boolean;
|
|
6
|
+
onUpgradePress: () => void;
|
|
7
|
+
onEdit: (frame: SocketFrame) => void;
|
|
8
|
+
}): import("react").JSX.Element | null;
|
|
9
|
+
//# sourceMappingURL=SocketFrameView.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SocketFrame } from "../../sockets/types";
|
|
2
|
+
export declare function SocketListView({ visible, paused, search, onOpenFrame, onOpenTricks, }: {
|
|
3
|
+
visible: boolean;
|
|
4
|
+
paused: boolean;
|
|
5
|
+
search: string;
|
|
6
|
+
onOpenFrame: (frame: SocketFrame) => void;
|
|
7
|
+
onOpenTricks: (socketId: string | null) => void;
|
|
8
|
+
}): import("react").JSX.Element | null;
|
|
9
|
+
//# sourceMappingURL=SocketListView.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SocketRow } from "../../sockets/foldFrames";
|
|
2
|
+
import type { SocketFrame } from "../../sockets/types";
|
|
3
|
+
/**
|
|
4
|
+
* `onOpen`, not `onPress`: an agent's tap_element calls the first `onPress` it
|
|
5
|
+
* finds by name, and it must land on the touchable (which passes the frame),
|
|
6
|
+
* not on this component's prop with a touch event in place of the frame.
|
|
7
|
+
*
|
|
8
|
+
* A folded burst opens up on tap (`onExpand`) instead, one row per message.
|
|
9
|
+
*/
|
|
10
|
+
export declare function SocketMessageRow({ row, onOpen, onExpand, }: {
|
|
11
|
+
row: SocketRow;
|
|
12
|
+
onOpen: (frame: SocketFrame) => void;
|
|
13
|
+
onExpand: (foldKey: string) => void;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
export declare function SocketLifecycleRow({ row, attempt }: {
|
|
16
|
+
row: SocketRow;
|
|
17
|
+
attempt?: number;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=SocketRows.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Socket tricks: make the socket misbehave on purpose, and watch the app's own
|
|
3
|
+
* code deal with it.
|
|
4
|
+
*
|
|
5
|
+
* - Push a message into the app as if the server sent it, or send one to the
|
|
6
|
+
* server.
|
|
7
|
+
* - Drop it (1006, like lost signal), kick it out (1008, like a bad token),
|
|
8
|
+
* freeze it (looks open, says nothing), or block every new socket.
|
|
9
|
+
*
|
|
10
|
+
* Everything here is fake, so the screen says what is on, and Reset turns it
|
|
11
|
+
* all off. Pro, like response overrides.
|
|
12
|
+
*/
|
|
13
|
+
export interface TricksDraft {
|
|
14
|
+
socketId: string | null;
|
|
15
|
+
data: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function SocketTricksView({ visible, draft, isPro, onUpgradePress, }: {
|
|
18
|
+
visible: boolean;
|
|
19
|
+
draft: TricksDraft | null;
|
|
20
|
+
isPro: boolean;
|
|
21
|
+
onUpgradePress: () => void;
|
|
22
|
+
}): import("react").JSX.Element | null;
|
|
23
|
+
//# sourceMappingURL=SocketTricksView.d.ts.map
|
|
@@ -111,6 +111,10 @@ declare class OverrideRulesStore {
|
|
|
111
111
|
* needs no second check.
|
|
112
112
|
*/
|
|
113
113
|
getRules(): readonly OverrideRule[];
|
|
114
|
+
/** Another tab loaded a bug case, and this tab hasn't got its block yet. */
|
|
115
|
+
private otherTabCase;
|
|
116
|
+
/** At page load, or another tab wrote or removed its loaded case. */
|
|
117
|
+
caseLockChanged(): void;
|
|
114
118
|
private sandboxCache;
|
|
115
119
|
private sandboxOnly;
|
|
116
120
|
/**
|
|
@@ -219,7 +223,22 @@ declare class OverrideRulesStore {
|
|
|
219
223
|
* morning") and not the failure mode it was built for.
|
|
220
224
|
*/
|
|
221
225
|
noteHealthyLaunch(): void;
|
|
226
|
+
private persisted;
|
|
227
|
+
/**
|
|
228
|
+
* Writes go to disk one at a time, in order. Otherwise an older save still
|
|
229
|
+
* in flight could land after a newer one and put old rules back (a loaded
|
|
230
|
+
* case's block gone after a reload, Codex review round 10).
|
|
231
|
+
*/
|
|
232
|
+
private writes;
|
|
222
233
|
private persist;
|
|
234
|
+
/**
|
|
235
|
+
* Save the rules now and say whether they reached disk. A loaded bug case
|
|
236
|
+
* needs this: if its block rules only live in memory, a reload brings the
|
|
237
|
+
* case's state back with nothing blocking real calls (Codex review round 9).
|
|
238
|
+
* Waits for every earlier write, so none can land after it, and gives up
|
|
239
|
+
* (false) after SAVE_CHECK_MS.
|
|
240
|
+
*/
|
|
241
|
+
saveChecked(): Promise<boolean>;
|
|
223
242
|
private emit;
|
|
224
243
|
}
|
|
225
244
|
/** Singleton — one rule list per JS runtime. */
|
|
@@ -101,6 +101,20 @@ export interface OverrideRule {
|
|
|
101
101
|
* spacing and key order don't matter. Missing variables count as {}.
|
|
102
102
|
*/
|
|
103
103
|
graphqlVariables?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Only requests whose body is JSON equal to this, written with its keys
|
|
106
|
+
* sorted (see canonicalJson). Parsed, so spacing and key order don't matter.
|
|
107
|
+
* For REST calls that share one URL but differ by body, like
|
|
108
|
+
* `POST /search {"q":"fries"}` and `{"q":"pie"}`: a bug case's replay gives
|
|
109
|
+
* each body its own answer.
|
|
110
|
+
*/
|
|
111
|
+
bodyJson?: string;
|
|
112
|
+
/**
|
|
113
|
+
* With `graphqlOperation`: only a single operation, never a batch (an
|
|
114
|
+
* array body). A bug case recorded the call alone, so its answer is one
|
|
115
|
+
* result, and a batch would get the wrong shape back.
|
|
116
|
+
*/
|
|
117
|
+
graphqlSingle?: boolean;
|
|
104
118
|
/**
|
|
105
119
|
* Part of a loaded bug case's sandbox (bug links). Such a rule keeps
|
|
106
120
|
* applying while Buoy's account access is off, because the case's data is
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SocketConnection, SocketFrame, SocketProblem } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* The problems the Sockets screen turns red, and the MCP reports. Pure: the
|
|
4
|
+
* same frames give the same answer on the phone, on Desktop and in a test.
|
|
5
|
+
*
|
|
6
|
+
* Every rule here needs PROOF in the frames, because a false alarm teaches
|
|
7
|
+
* people to ignore the red. That is why "no reply" only fires for a message
|
|
8
|
+
* type that has been answered before: Haiven puts a `requestId` on typing
|
|
9
|
+
* messages too, and the server never answers those.
|
|
10
|
+
*/
|
|
11
|
+
/** A request with no reply after this long is flagged. Haiven's own timeout. */
|
|
12
|
+
export declare const REPLY_TIMEOUT_MS = 10000;
|
|
13
|
+
/** More than this many opens to one URL inside STORM_WINDOW_MS is a storm. */
|
|
14
|
+
export declare const STORM_OPENS = 3;
|
|
15
|
+
export declare const STORM_WINDOW_MS = 15000;
|
|
16
|
+
/** This many sockets in a row to one URL that never opened: the app can't connect. */
|
|
17
|
+
export declare const CANT_CONNECT_TRIES = 3;
|
|
18
|
+
/** This many sign-in failures to one URL inside AUTH_WINDOW_MS is a loop. */
|
|
19
|
+
export declare const AUTH_FAILURES = 3;
|
|
20
|
+
export declare const AUTH_WINDOW_MS = 120000;
|
|
21
|
+
/** A socket open this long without a sign-in failure signed in: it ends a run of failures. */
|
|
22
|
+
export declare const SIGNED_IN_MS = 30000;
|
|
23
|
+
/**
|
|
24
|
+
* The message types that have been answered at least once, per socket URL.
|
|
25
|
+
* Built from the frames, so it survives nothing and needs no setup.
|
|
26
|
+
*/
|
|
27
|
+
export declare function answeredTypes(frames: SocketFrame[], connections: Map<string, SocketConnection>): Map<string, Set<string>>;
|
|
28
|
+
export declare function findProblems(frames: SocketFrame[], connectionList: SocketConnection[], now: number,
|
|
29
|
+
/** Types answered before this capture's frames were cut (kept by the store). */
|
|
30
|
+
knownAnswered?: Map<string, Set<string>>): SocketProblem[];
|
|
31
|
+
//# sourceMappingURL=findProblems.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { SocketFrame, SocketProblem, SocketsState } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* What the Sockets list shows, from the store's frames. Pure.
|
|
4
|
+
*
|
|
5
|
+
* Two kinds of noise fold away so a chat socket reads like a conversation:
|
|
6
|
+
* heartbeats (counted on the socket card instead) and a burst of the same
|
|
7
|
+
* message (`chat.typing` ×18 becomes one row; a tap shows each one).
|
|
8
|
+
*/
|
|
9
|
+
/** Same-type messages closer together than this fold into one row... */
|
|
10
|
+
export declare const BURST_GAP_MS = 1500;
|
|
11
|
+
/** ...when there are at least this many. Two chats a second apart are a conversation. */
|
|
12
|
+
export declare const FOLD_MIN = 3;
|
|
13
|
+
export interface SocketRow {
|
|
14
|
+
/** The newest frame of the row. Tapping the row opens it. */
|
|
15
|
+
frame: SocketFrame;
|
|
16
|
+
/** How many frames the row stands for (1 unless it's a burst). */
|
|
17
|
+
count: number;
|
|
18
|
+
/** A burst's oldest frame id. It stays the same as the burst grows, so it names the burst for `expanded`. */
|
|
19
|
+
foldKey?: string;
|
|
20
|
+
/** The first problem any of its frames is part of. */
|
|
21
|
+
problem?: SocketProblem;
|
|
22
|
+
}
|
|
23
|
+
export type SocketListFilter = {
|
|
24
|
+
kind: "all";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "problems";
|
|
27
|
+
} | {
|
|
28
|
+
kind: "direction";
|
|
29
|
+
direction: "in" | "out";
|
|
30
|
+
} | {
|
|
31
|
+
kind: "type";
|
|
32
|
+
prefix: string;
|
|
33
|
+
};
|
|
34
|
+
export interface FoldOptions {
|
|
35
|
+
socketId?: string | null;
|
|
36
|
+
filter?: SocketListFilter;
|
|
37
|
+
showPings?: boolean;
|
|
38
|
+
search?: string;
|
|
39
|
+
/** Bursts (by `foldKey`) shown one row per message. */
|
|
40
|
+
expanded?: ReadonlySet<string>;
|
|
41
|
+
}
|
|
42
|
+
export declare function foldFrames(state: Pick<SocketsState, "frames" | "problems">, opts?: FoldOptions): SocketRow[];
|
|
43
|
+
/**
|
|
44
|
+
* A chip's prefix matches its family: `auth.` matches `auth.success` and the
|
|
45
|
+
* bare `auth` too, so one chip covers both.
|
|
46
|
+
*/
|
|
47
|
+
export declare function matchesPrefix(type: string | undefined, prefix: string): boolean;
|
|
48
|
+
/** The busiest message names, for the filter chips: `chat.*`, `presence.*`. */
|
|
49
|
+
export declare function topTypePrefixes(frames: SocketFrame[], max?: number): string[];
|
|
50
|
+
//# sourceMappingURL=foldFrames.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SocketConnection, SocketFrame } from "./types";
|
|
2
|
+
/** `10:14:52` */
|
|
3
|
+
export declare function clockTime(ms: number): string;
|
|
4
|
+
/** `42ms`, `3.2s`, `2m 14s`, `1h 3m` */
|
|
5
|
+
export declare function shortDuration(ms: number): string;
|
|
6
|
+
/** `wss://api.x/v0/ws` → `api.x/v0/ws` */
|
|
7
|
+
export declare function shortUrl(url: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* `Closed 1006 · lost connection`. A 1006 on a socket that never opened was
|
|
10
|
+
* refused or turned away, so it says `couldn't connect` instead.
|
|
11
|
+
*/
|
|
12
|
+
export declare function closeLabel(code: number | undefined, neverOpened?: boolean): string;
|
|
13
|
+
/** The connection card's status line, e.g. `Open 2m 14s`. */
|
|
14
|
+
export declare function connectionStatus(conn: SocketConnection, now: number): string;
|
|
15
|
+
/** A one-line preview of a message for a row. */
|
|
16
|
+
export declare function previewText(frame: SocketFrame): string;
|
|
17
|
+
/** Pretty JSON for the detail view, or the text as it came. */
|
|
18
|
+
export declare function prettyData(frame: SocketFrame): string;
|
|
19
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn one raw socket message into what the list shows: its name, the id that
|
|
3
|
+
* ties it to a reply, whether it's a heartbeat, and its text with secrets
|
|
4
|
+
* hidden. Pure, so the rules can be argued with in a unit test.
|
|
5
|
+
*
|
|
6
|
+
* Secrets are hidden BEFORE the message is stored, because the store feeds the
|
|
7
|
+
* Desktop mirror and the MCP: a token that reaches the store has already left.
|
|
8
|
+
*/
|
|
9
|
+
/** Longest message text kept. Longer ones are cut and marked `truncated`. */
|
|
10
|
+
export declare const MAX_FRAME_CHARS: number;
|
|
11
|
+
export declare const HIDDEN = "[hidden]";
|
|
12
|
+
/**
|
|
13
|
+
* Longer messages are not parsed: parsing, hiding and printing 5 MB on every
|
|
14
|
+
* message would stall the app. Their first MAX_FRAME_CHARS are scrubbed as text.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_PARSE_CHARS: number;
|
|
17
|
+
export interface ReadMessage {
|
|
18
|
+
data: string;
|
|
19
|
+
size: number;
|
|
20
|
+
binary?: boolean;
|
|
21
|
+
truncated?: boolean;
|
|
22
|
+
json?: boolean;
|
|
23
|
+
type?: string;
|
|
24
|
+
correlationId?: string;
|
|
25
|
+
ping?: boolean;
|
|
26
|
+
/** Carries a field only replies have (see REPLY_KEYS). */
|
|
27
|
+
replyShaped?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Hide every value under a secret key, and any JWT anywhere. */
|
|
30
|
+
export declare function hideSecrets(value: unknown, depth?: number): unknown;
|
|
31
|
+
/**
|
|
32
|
+
* Text that couldn't be parsed, with secrets hidden as well as text allows:
|
|
33
|
+
* every JWT, and the value after any secret-looking key (`"token":"x"`, `pass=x`).
|
|
34
|
+
*/
|
|
35
|
+
export declare function scrubText(text: string, depth?: number): string;
|
|
36
|
+
/** A URL with secret query values hidden: `wss://x/ws?token=[hidden]`. */
|
|
37
|
+
export declare function hideUrlSecrets(rawUrl: string): string;
|
|
38
|
+
/** `/v0/ws` part of a URL: what makes two sockets "the same socket". */
|
|
39
|
+
export declare function socketKey(url: string): string;
|
|
40
|
+
export declare function readMessage(raw: unknown): ReadMessage;
|
|
41
|
+
//# sourceMappingURL=readMessage.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SocketFrame } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* The socket moments the Events timeline shows: opened, closed, errored, and
|
|
4
|
+
* messages Buoy faked. Single messages stay in the Sockets screen; a chat app
|
|
5
|
+
* would bury every other tool's events in typing indicators.
|
|
6
|
+
*
|
|
7
|
+
* Shaped like `@buoy-gg/events`' UnifiedEvent without importing it, because
|
|
8
|
+
* Events loads this package, not the other way round (same deal as three's
|
|
9
|
+
* onThreeLoad).
|
|
10
|
+
*/
|
|
11
|
+
export interface SocketMoment {
|
|
12
|
+
id: string;
|
|
13
|
+
source: "socket";
|
|
14
|
+
timestamp: number;
|
|
15
|
+
title: string;
|
|
16
|
+
subtitle: string;
|
|
17
|
+
status: "success" | "error" | "pending" | "neutral";
|
|
18
|
+
originalEvent: SocketFrame;
|
|
19
|
+
correlationId: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function toSocketMoment(frame: SocketFrame, url: string): SocketMoment | null;
|
|
22
|
+
/** Follow socket moments as they happen. Returns the unsubscribe. */
|
|
23
|
+
export declare function onSocketMoment(listener: (moment: SocketMoment) => void): () => void;
|
|
24
|
+
//# sourceMappingURL=socketMoments.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { BaseEventStore } from "@buoy-gg/shared-ui";
|
|
2
|
+
import type { SocketAction, SocketActionResult, SocketFrame, SocketsState } from "./types";
|
|
3
|
+
import { type SocketControl, type SocketRecorder } from "./webSocketPatch";
|
|
4
|
+
export declare function isToolingSocket(url: string): boolean;
|
|
5
|
+
/** Desktop: how the mirror reaches the device. */
|
|
6
|
+
export interface SocketsRemote {
|
|
7
|
+
act(action: SocketAction): Promise<SocketActionResult>;
|
|
8
|
+
getFrame(id: string): Promise<{
|
|
9
|
+
frame: SocketFrame;
|
|
10
|
+
partner: SocketFrame | null;
|
|
11
|
+
} | null>;
|
|
12
|
+
}
|
|
13
|
+
declare class SocketStore extends BaseEventStore<SocketFrame> implements SocketRecorder {
|
|
14
|
+
private connections;
|
|
15
|
+
private controls;
|
|
16
|
+
/** Open requests by socket, then by correlation id → frame id and send time. */
|
|
17
|
+
private pending;
|
|
18
|
+
/** Message types that got a reply, per URL. Kept so cut-off frames don't forget them. */
|
|
19
|
+
private answered;
|
|
20
|
+
private socketSeq;
|
|
21
|
+
private frameSeq;
|
|
22
|
+
private blocked;
|
|
23
|
+
private offline;
|
|
24
|
+
private installed;
|
|
25
|
+
private mirror;
|
|
26
|
+
private remote;
|
|
27
|
+
private capturingSince;
|
|
28
|
+
private mirrored;
|
|
29
|
+
private cache;
|
|
30
|
+
private version;
|
|
31
|
+
private recheck;
|
|
32
|
+
private retryOnAccess;
|
|
33
|
+
constructor();
|
|
34
|
+
/**
|
|
35
|
+
* Install the WebSocket wrapper. Called at import in a React Native dev
|
|
36
|
+
* build (see preset.tsx), and on first subscribe elsewhere. Never in mirror
|
|
37
|
+
* mode: Desktop must not watch its own sockets.
|
|
38
|
+
*/
|
|
39
|
+
install(): boolean;
|
|
40
|
+
protected startCapturing(): void;
|
|
41
|
+
/** The wrapper stays installed; there is nothing to stop. */
|
|
42
|
+
protected stopCapturing(): void;
|
|
43
|
+
isCapturing(): boolean;
|
|
44
|
+
disableCapture(): void;
|
|
45
|
+
/** Tell listeners something besides the frame list changed (a connection, a test switch). */
|
|
46
|
+
private bump;
|
|
47
|
+
private nextFrameId;
|
|
48
|
+
private setConnection;
|
|
49
|
+
private push;
|
|
50
|
+
register(url: string, protocols: string[] | undefined, control: SocketControl): string | null;
|
|
51
|
+
private trimConnections;
|
|
52
|
+
open(socketId: string): void;
|
|
53
|
+
close(socketId: string, code: number | undefined, reason: string | undefined, byBuoy: boolean): void;
|
|
54
|
+
error(socketId: string): void;
|
|
55
|
+
message(socketId: string, direction: "in" | "out", raw: unknown, flags?: {
|
|
56
|
+
fake?: "inject" | "send";
|
|
57
|
+
held?: boolean;
|
|
58
|
+
sentWhileClosed?: boolean;
|
|
59
|
+
}): string | undefined;
|
|
60
|
+
/** Learn that a type gets replies, with caps: oldest URL and oldest type go first. */
|
|
61
|
+
private rememberAnswered;
|
|
62
|
+
/**
|
|
63
|
+
* Wake when the next open request's wait is over, so "no reply" shows on
|
|
64
|
+
* time even if nothing else happens, and again when it is too old to pair,
|
|
65
|
+
* so it is forgotten: a message with an `id` that never gets an answer (a
|
|
66
|
+
* one-way event) must not keep memory forever. Stops when none are left.
|
|
67
|
+
*/
|
|
68
|
+
private scheduleRecheck;
|
|
69
|
+
cutOff(): string | null;
|
|
70
|
+
isFrozen(socketId: string): boolean;
|
|
71
|
+
/** Network's offline mode reaches sockets too: open ones drop, new ones fail. */
|
|
72
|
+
private syncOffline;
|
|
73
|
+
getState(): SocketsState;
|
|
74
|
+
getFrame(id: string): SocketFrame | undefined;
|
|
75
|
+
/** Subscribe to every change: frames, connections, tests. Returns unsubscribe. */
|
|
76
|
+
subscribeState(listener: () => void): () => void;
|
|
77
|
+
/** Desktop: send actions to the device instead of running them here. */
|
|
78
|
+
setRemote(remote: SocketsRemote | null): void;
|
|
79
|
+
/** One whole frame and its reply or request. On Desktop it comes from the device. */
|
|
80
|
+
loadFrame(id: string): Promise<{
|
|
81
|
+
frame: SocketFrame;
|
|
82
|
+
partner: SocketFrame | null;
|
|
83
|
+
} | null>;
|
|
84
|
+
/** Desktop: show the device's state. */
|
|
85
|
+
applySnapshot(state: SocketsState | null): void;
|
|
86
|
+
act(action: SocketAction): Promise<SocketActionResult>;
|
|
87
|
+
actLocal(action: SocketAction): SocketActionResult;
|
|
88
|
+
clearEvents(): void;
|
|
89
|
+
protected notifyArrayListeners(): void;
|
|
90
|
+
}
|
|
91
|
+
/** Singleton: every socket the app opens, and what Buoy is doing to them. */
|
|
92
|
+
export declare const socketStore: SocketStore;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=socketStore.d.ts.map
|