@buoy-gg/external-sync 7.0.74 → 7.0.76
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildHandshakeQuery=buildHandshakeQuery;exports.getInstanceId=getInstanceId;exports.useExternalSyncSocket=useExternalSyncSocket;var _brokerAccess=require("./brokerAccess");var _react=require("react");var _socket=require("socket.io-client");var _platformUtils=require("./utils/platformUtils");var _getDefaultSocketURL=require("./utils/getDefaultSocketURL");var _logger=require("./utils/logger");var _syncStatusStore=require("./syncStatusStore");var _realNow=require("./realNow");const isDev=typeof __DEV__!=="undefined"?__DEV__:false;let globalSocketInstance=null;let currentSocketURL="";let currentDeviceId="";let currentPlatform="";let socketUsers=0;let releaseTimer=null;const RELEASE_GRACE_MS=2e3;const makeConnectionNonce=()=>`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`;function getInstanceId(){const
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildHandshakeQuery=buildHandshakeQuery;exports.closeWithPage=closeWithPage;exports.getInstanceId=getInstanceId;exports.useExternalSyncSocket=useExternalSyncSocket;exports.withPage=withPage;var _brokerAccess=require("./brokerAccess");var _react=require("react");var _socket=require("socket.io-client");var _platformUtils=require("./utils/platformUtils");var _getDefaultSocketURL=require("./utils/getDefaultSocketURL");var _logger=require("./utils/logger");var _syncStatusStore=require("./syncStatusStore");var _realNow=require("./realNow");const isDev=typeof __DEV__!=="undefined"?__DEV__:false;let globalSocketInstance=null;let currentSocketURL="";let currentDeviceId="";let currentPlatform="";let socketUsers=0;let releaseTimer=null;const RELEASE_GRACE_MS=2e3;const makeConnectionNonce=()=>`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`;function getInstanceId(){const e=globalThis;if(!e.__buoyInstanceId)e.__buoyInstanceId=makeConnectionNonce();return e.__buoyInstanceId}function buildHandshakeQuery(e){const u=t=>t&&Object.fromEntries(Object.entries(t).filter(([l])=>!["BUOY_LICENSE_KEY","BUOY_KEY","EXPO_PUBLIC_BUOY_KEY","VITE_BUOY_KEY","NEXT_PUBLIC_BUOY_KEY","REACT_APP_BUOY_KEY","licenseKey"].includes(l)));return{deviceName:e.deviceName,deviceId:e.persistentDeviceId,platform:e.platform,extraDeviceInfo:JSON.stringify(u(e.extraDeviceInfo)),envVariables:JSON.stringify(u(e.envVariables)),connectionNonce:makeConnectionNonce(),instanceId:getInstanceId()}}function withPage(e,u,t=globalThis.location){if(u!=="web"||!t?.host)return e;const l=t.pathname&&t.pathname!=="/"?t.pathname:"";return`${e} \xB7 ${t.host}${l.length>24?`${l.slice(0,23)}\u2026`:l}`}function closeWithPage(e,u){e.addEventListener("pagehide",()=>u()?.disconnect());e.addEventListener("pageshow",t=>{if(t.persisted)u()?.connect()})}let pageHooked=false;function disconnectWithPage(){const e=globalThis;if(pageHooked||!e.document||typeof e.addEventListener!=="function")return;pageHooked=true;closeWithPage(e,()=>globalSocketInstance)}function useExternalSyncSocket({deviceName:e,socketURL:u,persistentDeviceId:t,extraDeviceInfo:l,envVariables:h,platform:i,enableLogs:c=false}){const s=(0,_react.useRef)(null);const[a,$]=(0,_react.useState)(null);const[U,f]=(0,_react.useState)(false);const S=u??(0,_getDefaultSocketURL.getDefaultSocketURL)();const d=(0,_react.useRef)(false);const r=`[${e}]`;const g=()=>{(0,_logger.log)(`${r} Socket connected successfully`,c);d.current=false;f(true);(0,_syncStatusStore.setSyncStatus)({state:"connected",lastError:null})};const _=o=>{(0,_logger.log)(`${r} Socket disconnected. Reason: ${o}`,c);f(false);(0,_syncStatusStore.setSyncStatus)({state:"retrying"})};const k=o=>{(0,_syncStatusStore.setSyncStatus)({state:"retrying",lastError:o.message});if(!isDev)return;if(d.current)return;d.current=true;const n=i==="android"&&/localhost|127\.0\.0\.1/.test(currentSocketURL)?`If this device is connected over USB, run: adb reverse tcp:42831 tcp:42831. `:`If it's running on another machine or port, pass socketURL via the externalSync prop. `;(0,_logger.log)(`[buoy] desktop sync: can't reach ${currentSocketURL} (is Buoy Desktop running?). Retrying in the background. ${n}(${o.message})`,c,"warn")};const y=()=>{(0,_logger.log)(`${r} Socket connection timeout`,c,"error")};(0,_react.useEffect)(()=>{if(!t)return;const o=(0,_platformUtils.getPlatformSpecificURL)(S,i);(0,_syncStatusStore.setSyncStatus)({targetURL:o,state:"connecting",lastError:null});(0,_logger.log)(`${r} Platform: ${i}, using URL: ${o}`,c);try{if(globalSocketInstance&&(currentSocketURL!==o||currentDeviceId!==t||currentPlatform!==i)){globalSocketInstance.disconnect();globalSocketInstance=null}if(!globalSocketInstance){(0,_logger.log)(`${r} Creating new socket instance to ${o}`,c);try{const I=globalThis;(I.__BUOY_INTERNAL_HOSTS__??=new Set).add(new URL(o).host.toLowerCase())}catch{}globalSocketInstance=(0,_socket.io)(o,{autoConnect:true,query:buildHandshakeQuery({deviceName:withPage(e,i),persistentDeviceId:t,platform:i,extraDeviceInfo:l,envVariables:h}),reconnection:true,reconnectionAttempts:Infinity,reconnectionDelay:1e3,reconnectionDelayMax:5e3,transports:["websocket"]});(0,_realNow.pinSocketTimers)(globalSocketInstance);disconnectWithPage();currentSocketURL=o;currentDeviceId=t;currentPlatform=i}else{(0,_logger.log)(`${r} Reusing existing socket instance to ${o}`,c)}s.current=globalSocketInstance;const n=s.current;socketUsers++;if(releaseTimer){(0,_realNow.realClearTimeout)(releaseTimer);releaseTimer=null}$(n);n.on("connect_error",k);n.on("connect_timeout",y);f(n.connected);if(n.connected){(0,_logger.log)(`${r} Socket already connected on init`,c)}n.on("connect",g);n.on("disconnect",_);return()=>{(0,_logger.log)(`${r} Cleaning up socket event listeners`,c);n.off("connect",g);n.off("disconnect",_);n.off("connect_error",k);n.off("connect_timeout",y);socketUsers--;if(socketUsers===0){releaseTimer=(0,_realNow.realSetTimeout)(()=>{releaseTimer=null;if(socketUsers>0||globalSocketInstance!==n)return;(0,_logger.log)(`${r} No longer used; disconnecting`,c);n.disconnect();globalSocketInstance=null;(0,_syncStatusStore.setSyncStatus)({state:"idle",lastError:null})},RELEASE_GRACE_MS)}}}catch(n){(0,_logger.log)(`${r} Failed to initialize socket: ${n}`,c,"error")}},[t,S,i]);(0,_react.useEffect)(()=>{if(s.current&&s.current.io.opts.query&&t){(0,_logger.log)(`${r} Updating device name in socket connection`,c);s.current.io.opts.query={...s.current.io.opts.query,deviceName:withPage(e,i),deviceId:t,platform:i}}},[e,r,t,i,c]);function v(){if(s.current&&!s.current.connected){(0,_logger.log)(`${r} Manually connecting to socket server`,c);s.current.connect()}}function m(){if(s.current&&s.current.connected){(0,_logger.log)(`${r} Manually disconnecting from socket server`,c);s.current.disconnect()}}const[E,p]=(0,_react.useState)(null);(0,_react.useEffect)(()=>{if(!a)return;return(0,_brokerAccess.watchBrokerAccess)(a,o=>{p(o?a:null);(0,_syncStatusStore.setSyncStatus)(o?{state:"connected",lastError:null}:{state:"retrying",lastError:"Update Buoy Desktop and sign in to enable desktop sync."})})},[a]);return{socket:E===a?a:null,connect:v,disconnect:m,isConnected:U&&E===a&&a!==null}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{watchBrokerAccess as
|
|
1
|
+
"use strict";import{watchBrokerAccess as b}from"./brokerAccess";import{useEffect as y,useRef as v,useState as $}from"react";import{io as M}from"socket.io-client";import{getPlatformSpecificURL as V}from"./utils/platformUtils";import{getDefaultSocketURL as q}from"./utils/getDefaultSocketURL";import{log as s}from"./utils/logger";import{setSyncStatus as _}from"./syncStatusStore";import{pinSocketTimers as L,realClearTimeout as H,realSetTimeout as j}from"./realNow";const J=typeof __DEV__!=="undefined"?__DEV__:false;let a=null;let m="";let O="";let T="";let E=0;let k=null;const W=2e3;const B=()=>`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`;export function getInstanceId(){const t=globalThis;if(!t.__buoyInstanceId)t.__buoyInstanceId=B();return t.__buoyInstanceId}export function buildHandshakeQuery(t){const l=n=>n&&Object.fromEntries(Object.entries(n).filter(([d])=>!["BUOY_LICENSE_KEY","BUOY_KEY","EXPO_PUBLIC_BUOY_KEY","VITE_BUOY_KEY","NEXT_PUBLIC_BUOY_KEY","REACT_APP_BUOY_KEY","licenseKey"].includes(d)));return{deviceName:t.deviceName,deviceId:t.persistentDeviceId,platform:t.platform,extraDeviceInfo:JSON.stringify(l(t.extraDeviceInfo)),envVariables:JSON.stringify(l(t.envVariables)),connectionNonce:B(),instanceId:getInstanceId()}}export function withPage(t,l,n=globalThis.location){if(l!=="web"||!n?.host)return t;const d=n.pathname&&n.pathname!=="/"?n.pathname:"";return`${t} \xB7 ${n.host}${d.length>24?`${d.slice(0,23)}\u2026`:d}`}export function closeWithPage(t,l){t.addEventListener("pagehide",()=>l()?.disconnect());t.addEventListener("pageshow",n=>{if(n.persisted)l()?.connect()})}let Y=false;function X(){const t=globalThis;if(Y||!t.document||typeof t.addEventListener!=="function")return;Y=true;closeWithPage(t,()=>a)}export function useExternalSyncSocket({deviceName:t,socketURL:l,persistentDeviceId:n,extraDeviceInfo:d,envVariables:w,platform:u,enableLogs:c=false}){const i=v(null);const[f,x]=$(null);const[P,h]=$(false);const g=l??q();const S=v(false);const r=`[${t}]`;const p=()=>{s(`${r} Socket connected successfully`,c);S.current=false;h(true);_({state:"connected",lastError:null})};const U=o=>{s(`${r} Socket disconnected. Reason: ${o}`,c);h(false);_({state:"retrying"})};const I=o=>{_({state:"retrying",lastError:o.message});if(!J)return;if(S.current)return;S.current=true;const e=u==="android"&&/localhost|127\.0\.0\.1/.test(m)?`If this device is connected over USB, run: adb reverse tcp:42831 tcp:42831. `:`If it's running on another machine or port, pass socketURL via the externalSync prop. `;s(`[buoy] desktop sync: can't reach ${m} (is Buoy Desktop running?). Retrying in the background. ${e}(${o.message})`,c,"warn")};const C=()=>{s(`${r} Socket connection timeout`,c,"error")};y(()=>{if(!n)return;const o=V(g,u);_({targetURL:o,state:"connecting",lastError:null});s(`${r} Platform: ${u}, using URL: ${o}`,c);try{if(a&&(m!==o||O!==n||T!==u)){a.disconnect();a=null}if(!a){s(`${r} Creating new socket instance to ${o}`,c);try{const D=globalThis;(D.__BUOY_INTERNAL_HOSTS__??=new Set).add(new URL(o).host.toLowerCase())}catch{}a=M(o,{autoConnect:true,query:buildHandshakeQuery({deviceName:withPage(t,u),persistentDeviceId:n,platform:u,extraDeviceInfo:d,envVariables:w}),reconnection:true,reconnectionAttempts:Infinity,reconnectionDelay:1e3,reconnectionDelayMax:5e3,transports:["websocket"]});L(a);X();m=o;O=n;T=u}else{s(`${r} Reusing existing socket instance to ${o}`,c)}i.current=a;const e=i.current;E++;if(k){H(k);k=null}x(e);e.on("connect_error",I);e.on("connect_timeout",C);h(e.connected);if(e.connected){s(`${r} Socket already connected on init`,c)}e.on("connect",p);e.on("disconnect",U);return()=>{s(`${r} Cleaning up socket event listeners`,c);e.off("connect",p);e.off("disconnect",U);e.off("connect_error",I);e.off("connect_timeout",C);E--;if(E===0){k=j(()=>{k=null;if(E>0||a!==e)return;s(`${r} No longer used; disconnecting`,c);e.disconnect();a=null;_({state:"idle",lastError:null})},W)}}}catch(e){s(`${r} Failed to initialize socket: ${e}`,c,"error")}},[n,g,u]);y(()=>{if(i.current&&i.current.io.opts.query&&n){s(`${r} Updating device name in socket connection`,c);i.current.io.opts.query={...i.current.io.opts.query,deviceName:withPage(t,u),deviceId:n,platform:u}}},[t,r,n,u,c]);function A(){if(i.current&&!i.current.connected){s(`${r} Manually connecting to socket server`,c);i.current.connect()}}function N(){if(i.current&&i.current.connected){s(`${r} Manually disconnecting from socket server`,c);i.current.disconnect()}}const[R,K]=$(null);y(()=>{if(!f)return;return b(f,o=>{K(o?f:null);_(o?{state:"connected",lastError:null}:{state:"retrying",lastError:"Update Buoy Desktop and sign in to enable desktop sync."})})},[f]);return{socket:R===f?f:null,connect:A,disconnect:N,isConnected:P&&R===f&&f!==null}}
|
|
@@ -22,6 +22,29 @@ export interface HandshakeIdentity {
|
|
|
22
22
|
* runtime (see above). Pure, so the shape is unit-tested.
|
|
23
23
|
*/
|
|
24
24
|
export declare function buildHandshakeQuery(identity: HandshakeIdentity): Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* A browser tab's name also says which page it is on ("localhost:3013/games").
|
|
27
|
+
* Tabs of one app, and one app on two dev servers, were told apart only by a
|
|
28
|
+
* 4-character tail. The path is where the tab was when it connected.
|
|
29
|
+
*/
|
|
30
|
+
export declare function withPage(deviceName: string, platform: string, page?: {
|
|
31
|
+
host?: string;
|
|
32
|
+
pathname?: string;
|
|
33
|
+
} | undefined): string;
|
|
34
|
+
/**
|
|
35
|
+
* A page that closes or goes away says goodbye at once. Before, the broker
|
|
36
|
+
* kept a closed tab listed as connected until its ping timed out (about a
|
|
37
|
+
* minute), and calls to it failed with "may be crashing".
|
|
38
|
+
*/
|
|
39
|
+
type PageTarget = {
|
|
40
|
+
addEventListener: (type: string, listener: (event: {
|
|
41
|
+
persisted?: boolean;
|
|
42
|
+
}) => void) => void;
|
|
43
|
+
};
|
|
44
|
+
export declare function closeWithPage(page: PageTarget, getSocket: () => {
|
|
45
|
+
connect(): unknown;
|
|
46
|
+
disconnect(): unknown;
|
|
47
|
+
} | null): void;
|
|
25
48
|
/**
|
|
26
49
|
* Hook that manages a Socket.IO connection for device-to-dashboard communication.
|
|
27
50
|
*
|
|
@@ -36,4 +59,5 @@ export declare function useExternalSyncSocket({ deviceName, socketURL, persisten
|
|
|
36
59
|
disconnect: () => void;
|
|
37
60
|
isConnected: boolean;
|
|
38
61
|
};
|
|
62
|
+
export {};
|
|
39
63
|
//# sourceMappingURL=useExternalSyncSocket.d.ts.map
|
package/lib/web/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";var oe=(()=>{try{let e=globalThis.Capacitor;if(e&&typeof e.isNativePlatform=="function"&&e.isNativePlatform())return e.DEBUG===true}catch{}return process.env.NODE_ENV!=="production"})();typeof window<"u"&&typeof globalThis.global>"u"&&(globalThis.global=globalThis);import{useAccountAccess as be}from"@buoy-gg/license/web";import{useEffect as ne,useRef as re}from"react";import{LicenseManager as x}from"@buoy-gg/license/web";function p(e,n,o="log"){if(!(!n&&o==="log"))switch(o){case"warn":console.warn("[Buoy]",e);break;case"error":console.error("[Buoy]",e);break;default:console.log("[Buoy]",e)}}var z="__buoyFlushToolSync";function H(){let e=globalThis;return e[z]||(e[z]=new Set),e[z]}function ce(e){H().add(e)}function ie(e){H().delete(e)}function ve(e){try{for(let n of H())try{n(e)}catch{}}catch{}}function se(e,n){let o=0,s=[e],l=new Set;for(;s.length>0;){if(o>n)return{over:true,approxBytes:o};let r=s.pop();if(r==null)o+=4;else if(typeof r=="string")o+=r.length+2;else if(typeof r=="number")o+=8;else if(typeof r=="boolean")o+=5;else if(typeof r=="object"){if(l.has(r))continue;if(l.add(r),Array.isArray(r)){o+=2+r.length;for(let c=0;c<r.length;c++)s.push(r[c])}else{o+=2;for(let c in r)o+=c.length+4,s.push(r[c])}}else o+=4}return{over:o>n,approxBytes:o}}var _e=1;function L(){let e=globalThis.__buoyRealNow;return typeof e=="function"?e():Date.now()}function ae(){return globalThis.__buoyRealTimers??globalThis}function le(e,n){return ae().setTimeout(e,n)}function ue(e){ae().clearTimeout(e)}function Ie(e){let n=globalThis.__buoyWithRealClock;return typeof n=="function"?n(e):e()}var $e=["_resetPingTimeout","_hasPingExpired"];function ke(e){let n=e?.io;if(!n||n.__buoyRealTimers)return;let o=l=>{if(l){l.setTimeoutFn=le,l.clearTimeoutFn=ue;for(let r of $e){let c=l[r];if(typeof c!="function"||c.__buoyReal)continue;let a=function(...y){return Ie(()=>c.apply(this,y))};a.__buoyReal=true,l[r]=a}}};o(n),o(n.engine);let s=n.open;typeof s=="function"&&(n.open=function(...l){let r=s.apply(this,l);return o(this?.engine),r}),n.__buoyRealTimers=true}var P="[ExternalSync]";function Se({socket:e,deviceId:n,getTools:o,watched:s,throttleMs:l=200,enableLogs:r=false}){let c=false,a=false,y=0,g=()=>!c&&a&&e.connected&&x.hasAccountAccess(),m=new Map,_={},b={},S=t=>t==="All"||t===n,f=(t,i)=>{if(g())try{e.emit(t,i)}catch(h){p(`${P} Failed to emit "${t}": ${h}. Payload must be JSON-serializable.`,r,"error")}},B=new Set,$=t=>{if(!g())return;let i=o()[t];if(!i)return;_[t]=L();let h;try{h=i.getSnapshot()}catch{return}let{over:k,approxBytes:U}=se(h,2097152);if(k){B.has(t)||(B.add(t),console.warn("[Buoy]",`${P} "${t}" snapshot \u2248${(U/1e6).toFixed(1)}MB exceeds the ${2097152/1e6}MB sync budget \u2014 dropped (dashboard will be stale for this tool). The adapter should ship a wire-form snapshot (summaries + on-demand detail).`));return}let j={toolId:t,persistentDeviceId:n,timestamp:_[t],version:i.version,kind:"snapshot",payload:h};f("devtool-sync",j)},R=t=>{if(b[t])return;let i=L()-(_[t]??0);if(i>=l){$(t);return}b[t]=setTimeout(()=>{delete b[t],$(t)},l-i)},O=t=>{e.connected&&m.has(t)&&(b[t]&&(clearTimeout(b[t]),delete b[t]),$(t))};ce(O);let w=new Map,A=(t,i)=>{if(!g())return;let h=o()[t];if(h){if(m.has(t)){if(w.get(t)===i)return;let k=m.get(t);m.set(t,h.subscribe(i==="stream"?()=>R(t):()=>{})),w.set(t,i),k?.(),p(`${P} "${t}" watch mode \u2192 ${i}`,r);return}m.set(t,h.subscribe(i==="stream"?()=>R(t):()=>{})),w.set(t,i),p(`${P} Watching "${t}" (${i})`,r)}},E=t=>{m.get(t)?.(),m.delete(t),w.delete(t),b[t]&&(clearTimeout(b[t]),delete b[t]),p(`${P} Stopped watching "${t}"`,r)},F=()=>{let t={persistentDeviceId:n,protocolVersion:1,tools:Object.entries(o()).map(([i,h])=>({toolId:i,version:h.version,actions:Object.keys(h.actions??{})}))};f("devtool-capabilities",t)},d=t=>{if(!(!g()||!S(t.targetDeviceId)))if(t.watching){let i=t.consolidated===true&&t.stream===false?"capture":"stream";s.set(t.toolId,i),A(t.toolId,i),i==="stream"&&$(t.toolId)}else s.delete(t.toolId),E(t.toolId)},u=t=>{!g()||!S(t.targetDeviceId)||$(t.toolId)},N=async t=>{if(!g()||!S(t.targetDeviceId))return;let i=y,{requestId:h,toolId:k,action:U,params:j}=t,v={requestId:h,toolId:k,action:U,persistentDeviceId:n,timestamp:L(),ok:false},ee=o()[k],te=ee?.actions?.[U];if(!te){v.error=ee?`Unknown action "${U}" on tool "${k}"`:`Unknown tool "${k}"`,f("devtool-action-result",v);return}try{v.result=await te(j),v.ok=true}catch(D){v.error=D instanceof Error?D.message:String(D)}if(!(i!==y||!g())){if(v.ok){let{over:D,approxBytes:me}=se(v.result,8388608);D&&(v.ok=false,v.result=void 0,v.error=`Result \u2248${(me/1e6).toFixed(1)}MB exceeds the ${8388608/1e6}MB sync budget. Narrow the request (e.g. includeValues:false, a limit, or a more specific action).`)}v.timestamp=L(),f("devtool-action-result",v)}},C=()=>{y+=1;for(let i of[...m.keys()])E(i);s.clear();for(let i of Object.values(b))clearTimeout(i);let t=e;t.sendBuffer&&(t.sendBuffer=t.sendBuffer.filter(i=>!["devtool-sync","devtool-capabilities","devtool-action-result","device-license"].includes(String(i.data?.[0]))))},V=()=>{let t=x.getState();if(t.tier!=="pro"&&(t.isInitialized===false||t.isValidating===true)){t.isInitialized===false&&x.initialize().then(()=>{g()&&V()},()=>{});return}e.emit("device-license",{tier:t.tier,isPro:t.isPro?"1":"0"}),F()},Q=t=>{a=t?.protocol===1&&t.ready===true,g()?V():C()},he=x.subscribe(()=>{x.hasAccountAccess()?g()&&V():(e.connected&&e.emit("device-license",{tier:"locked",isPro:"0"}),C())}),K=()=>{a=false,e.emit("buoy:access:request")},Z=()=>{a=false,C()};e.on("buoy:access",Q);for(let[t,i]of s)o()[t]&&A(t,i);return e.on("connect",K),e.on("disconnect",Z),e.on("devtool-watch",d),e.on("request-devtool-state",u),e.on("devtool-action",N),e.connected&&K(),()=>{c=true,he(),e.off("buoy:access",Q),C(),ie(O),e.off("connect",K),e.off("disconnect",Z),e.off("devtool-watch",d),e.off("request-devtool-state",u),e.off("devtool-action",N);for(let t of[...m.keys()])E(t)}}function Te({tools:e,socket:n,deviceId:o,enabled:s=true,throttleMs:l=200,enableLogs:r=false}){let c=be(),a=c.status==="admitted",y=re(e);y.current=e;let g=re(new Map);ne(()=>{if(!n||c.status!=="locked")return;let _=()=>n.emit("device-license",{tier:"locked",isPro:"0"});return n.connected&&_(),n.on("connect",_),()=>{n.off("connect",_)}},[n,c.status]);let m=Object.keys(e).sort().join(",");ne(()=>{if(!n||!o||!s||!a){g.current.clear();return}return Se({socket:n,deviceId:o,getTools:()=>y.current,watched:g.current,throttleMs:l,enableLogs:r})},[n,o,s,a,l,r,m])}function we(e,n){let o=()=>n(false),s=()=>{o(),e.emit("buoy:access:request")},l=r=>{let c=r;n(c?.protocol===1&&c?.ready===true)};return e.on("connect",s),e.on("disconnect",o),e.on("buoy:access",l),o(),e.connected&&s(),()=>{e.off("connect",s),e.off("disconnect",o),e.off("buoy:access",l)}}import{useEffect as J,useRef as fe,useState as W}from"react";import{io as Ee}from"socket.io-client";function Be(e,n){return e}var Re=42831;function Oe(e){return`http://${e??"localhost"}:42831`}function Ne(){return null}function de(){return Oe(typeof location>"u"?null:location.hostname)}var X={targetURL:null,state:"idle",lastError:null,since:0},G=new Set;function T(e){X={...X,...e,since:L()},G.forEach(n=>n())}function Ue(){return X}function De(e){return G.add(e),()=>{G.delete(e)}}var xe=typeof oe<"u"?oe:false,I=null,Y="",pe="",ye="",q=0,M=null,Le=2e3,ge=()=>`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`;function Pe(){let e=globalThis;return e.__buoyInstanceId||(e.__buoyInstanceId=ge()),e.__buoyInstanceId}function Me(e){let n=o=>o&&Object.fromEntries(Object.entries(o).filter(([s])=>!["BUOY_LICENSE_KEY","BUOY_KEY","EXPO_PUBLIC_BUOY_KEY","VITE_BUOY_KEY","NEXT_PUBLIC_BUOY_KEY","REACT_APP_BUOY_KEY","licenseKey"].includes(s)));return{deviceName:e.deviceName,deviceId:e.persistentDeviceId,platform:e.platform,extraDeviceInfo:JSON.stringify(n(e.extraDeviceInfo)),envVariables:JSON.stringify(n(e.envVariables)),connectionNonce:ge(),instanceId:Pe()}}function Ae({deviceName:e,socketURL:n,persistentDeviceId:o,extraDeviceInfo:s,envVariables:l,platform:r,enableLogs:c=false}){let a=fe(null),[y,g]=W(null),[m,_]=W(false),b=n??de(),S=fe(false),f=`[${e}]`,B=()=>{p(`${f} Socket connected successfully`,c),S.current=false,_(true),T({state:"connected",lastError:null})},$=d=>{p(`${f} Socket disconnected. Reason: ${d}`,c),_(false),T({state:"retrying"})},R=d=>{if(T({state:"retrying",lastError:d.message}),!xe||S.current)return;S.current=true;let u=r==="android"&&/localhost|127\.0\.0\.1/.test(Y)?"If this device is connected over USB, run: adb reverse tcp:42831 tcp:42831. ":"If it's running on another machine or port, pass socketURL via the externalSync prop. ";p(`[buoy] desktop sync: can't reach ${Y} (is Buoy Desktop running?). Retrying in the background. ${u}(${d.message})`,c,"warn")},O=()=>{p(`${f} Socket connection timeout`,c,"error")};J(()=>{if(!o)return;let d=Be(b,r);T({targetURL:d,state:"connecting",lastError:null}),p(`${f} Platform: ${r}, using URL: ${d}`,c);try{if(I&&(Y!==d||pe!==o||ye!==r)&&(I.disconnect(),I=null),I)p(`${f} Reusing existing socket instance to ${d}`,c);else{p(`${f} Creating new socket instance to ${d}`,c);try{let N=globalThis;(N.__BUOY_INTERNAL_HOSTS__??(N.__BUOY_INTERNAL_HOSTS__=new Set)).add(new URL(d).host.toLowerCase())}catch{}I=Ee(d,{autoConnect:true,query:Me({deviceName:e,persistentDeviceId:o,platform:r,extraDeviceInfo:s,envVariables:l}),reconnection:true,reconnectionAttempts:1/0,reconnectionDelay:1e3,reconnectionDelayMax:5e3,transports:["websocket"]}),ke(I),Y=d,pe=o,ye=r}a.current=I;let u=a.current;return q++,M&&(ue(M),M=null),g(u),u.on("connect_error",R),u.on("connect_timeout",O),_(u.connected),u.connected&&p(`${f} Socket already connected on init`,c),u.on("connect",B),u.on("disconnect",$),()=>{p(`${f} Cleaning up socket event listeners`,c),u.off("connect",B),u.off("disconnect",$),u.off("connect_error",R),u.off("connect_timeout",O),q--,q===0&&(M=le(()=>{M=null,!(q>0||I!==u)&&(p(`${f} No longer used; disconnecting`,c),u.disconnect(),I=null,T({state:"idle",lastError:null}))},Le))}}catch(u){p(`${f} Failed to initialize socket: ${u}`,c,"error")}},[o,b,r]),J(()=>{a.current&&a.current.io.opts.query&&o&&(p(`${f} Updating device name in socket connection`,c),a.current.io.opts.query={...a.current.io.opts.query,deviceName:e,deviceId:o,platform:r})},[e,f,o,r,c]);function w(){a.current&&!a.current.connected&&(p(`${f} Manually connecting to socket server`,c),a.current.connect())}function A(){a.current&&a.current.connected&&(p(`${f} Manually disconnecting from socket server`,c),a.current.disconnect())}let[E,F]=W(null);return J(()=>{if(y)return we(y,d=>{F(d?y:null),T(d?{state:"connected",lastError:null}:{state:"retrying",lastError:"Update Buoy Desktop and sign in to enable desktop sync."})})},[y]),{socket:E===y?y:null,connect:w,disconnect:A,isConnected:m&&E===y&&y!==null}}export{Re as DEFAULT_BROKER_PORT,_e as SYNC_PROTOCOL_VERSION,ve as flushToolSyncNow,de as getDefaultSocketURL,Ne as getMetroHost,Ue as getSyncStatus,ce as registerToolFlusher,T as setSyncStatus,De as subscribeSyncStatus,ie as unregisterToolFlusher,Te as useExternalSync,Ae as useExternalSyncSocket};
|
|
1
|
+
"use client";var ne=(()=>{try{let e=globalThis.Capacitor;if(e&&typeof e.isNativePlatform=="function"&&e.isNativePlatform())return e.DEBUG===true}catch{}return process.env.NODE_ENV!=="production"})();typeof window<"u"&&typeof globalThis.global>"u"&&(globalThis.global=globalThis);import{useAccountAccess as _e}from"@buoy-gg/license/web";import{useEffect as oe,useRef as re}from"react";import{LicenseManager as L}from"@buoy-gg/license/web";function p(e,o,n="log"){if(!(!o&&n==="log"))switch(n){case"warn":console.warn("[Buoy]",e);break;case"error":console.error("[Buoy]",e);break;default:console.log("[Buoy]",e)}}var z="__buoyFlushToolSync";function H(){let e=globalThis;return e[z]||(e[z]=new Set),e[z]}function ce(e){H().add(e)}function ie(e){H().delete(e)}function $e(e){try{for(let o of H())try{o(e)}catch{}}catch{}}function se(e,o){let n=0,s=[e],l=new Set;for(;s.length>0;){if(n>o)return{over:true,approxBytes:n};let r=s.pop();if(r==null)n+=4;else if(typeof r=="string")n+=r.length+2;else if(typeof r=="number")n+=8;else if(typeof r=="boolean")n+=5;else if(typeof r=="object"){if(l.has(r))continue;if(l.add(r),Array.isArray(r)){n+=2+r.length;for(let c=0;c<r.length;c++)s.push(r[c])}else{n+=2;for(let c in r)n+=c.length+4,s.push(r[c])}}else n+=4}return{over:n>o,approxBytes:n}}var Ie=1;function x(){let e=globalThis.__buoyRealNow;return typeof e=="function"?e():Date.now()}function ae(){return globalThis.__buoyRealTimers??globalThis}function le(e,o){return ae().setTimeout(e,o)}function ue(e){ae().clearTimeout(e)}function Se(e){let o=globalThis.__buoyWithRealClock;return typeof o=="function"?o(e):e()}var ke=["_resetPingTimeout","_hasPingExpired"];function Te(e){let o=e?.io;if(!o||o.__buoyRealTimers)return;let n=l=>{if(l){l.setTimeoutFn=le,l.clearTimeoutFn=ue;for(let r of ke){let c=l[r];if(typeof c!="function"||c.__buoyReal)continue;let a=function(...y){return Se(()=>c.apply(this,y))};a.__buoyReal=true,l[r]=a}}};n(o),n(o.engine);let s=o.open;typeof s=="function"&&(o.open=function(...l){let r=s.apply(this,l);return n(this?.engine),r}),o.__buoyRealTimers=true}var P="[ExternalSync]";function Ee({socket:e,deviceId:o,getTools:n,watched:s,throttleMs:l=200,enableLogs:r=false}){let c=false,a=false,y=0,h=()=>!c&&a&&e.connected&&L.hasAccountAccess(),m=new Map,_={},v={},k=t=>t==="All"||t===o,f=(t,i)=>{if(h())try{e.emit(t,i)}catch(g){p(`${P} Failed to emit "${t}": ${g}. Payload must be JSON-serializable.`,r,"error")}},B=new Set,I=t=>{if(!h())return;let i=n()[t];if(!i)return;_[t]=x();let g;try{g=i.getSnapshot()}catch{return}let{over:S,approxBytes:U}=se(g,2097152);if(S){B.has(t)||(B.add(t),console.warn("[Buoy]",`${P} "${t}" snapshot \u2248${(U/1e6).toFixed(1)}MB exceeds the ${2097152/1e6}MB sync budget \u2014 dropped (dashboard will be stale for this tool). The adapter should ship a wire-form snapshot (summaries + on-demand detail).`));return}let j={toolId:t,persistentDeviceId:o,timestamp:_[t],version:i.version,kind:"snapshot",payload:g};f("devtool-sync",j)},R=t=>{if(v[t])return;let i=x()-(_[t]??0);if(i>=l){I(t);return}v[t]=setTimeout(()=>{delete v[t],I(t)},l-i)},O=t=>{e.connected&&m.has(t)&&(v[t]&&(clearTimeout(v[t]),delete v[t]),I(t))};ce(O);let E=new Map,A=(t,i)=>{if(!h())return;let g=n()[t];if(g){if(m.has(t)){if(E.get(t)===i)return;let S=m.get(t);m.set(t,g.subscribe(i==="stream"?()=>R(t):()=>{})),E.set(t,i),S?.(),p(`${P} "${t}" watch mode \u2192 ${i}`,r);return}m.set(t,g.subscribe(i==="stream"?()=>R(t):()=>{})),E.set(t,i),p(`${P} Watching "${t}" (${i})`,r)}},w=t=>{m.get(t)?.(),m.delete(t),E.delete(t),v[t]&&(clearTimeout(v[t]),delete v[t]),p(`${P} Stopped watching "${t}"`,r)},F=()=>{let t={persistentDeviceId:o,protocolVersion:1,tools:Object.entries(n()).map(([i,g])=>({toolId:i,version:g.version,actions:Object.keys(g.actions??{})}))};f("devtool-capabilities",t)},d=t=>{if(!(!h()||!k(t.targetDeviceId)))if(t.watching){let i=t.consolidated===true&&t.stream===false?"capture":"stream";s.set(t.toolId,i),A(t.toolId,i),i==="stream"&&I(t.toolId)}else s.delete(t.toolId),w(t.toolId)},u=t=>{!h()||!k(t.targetDeviceId)||I(t.toolId)},N=async t=>{if(!h()||!k(t.targetDeviceId))return;let i=y,{requestId:g,toolId:S,action:U,params:j}=t,b={requestId:g,toolId:S,action:U,persistentDeviceId:o,timestamp:x(),ok:false},ee=n()[S],te=ee?.actions?.[U];if(!te){b.error=ee?`Unknown action "${U}" on tool "${S}"`:`Unknown tool "${S}"`,f("devtool-action-result",b);return}try{b.result=await te(j),b.ok=true}catch(D){b.error=D instanceof Error?D.message:String(D)}if(!(i!==y||!h())){if(b.ok){let{over:D,approxBytes:be}=se(b.result,8388608);D&&(b.ok=false,b.result=void 0,b.error=`Result \u2248${(be/1e6).toFixed(1)}MB exceeds the ${8388608/1e6}MB sync budget. Narrow the request (e.g. includeValues:false, a limit, or a more specific action).`)}b.timestamp=x(),f("devtool-action-result",b)}},Y=()=>{y+=1;for(let i of[...m.keys()])w(i);s.clear();for(let i of Object.values(v))clearTimeout(i);let t=e;t.sendBuffer&&(t.sendBuffer=t.sendBuffer.filter(i=>!["devtool-sync","devtool-capabilities","devtool-action-result","device-license"].includes(String(i.data?.[0]))))},V=()=>{let t=L.getState();if(t.tier!=="pro"&&(t.isInitialized===false||t.isValidating===true)){t.isInitialized===false&&L.initialize().then(()=>{h()&&V()},()=>{});return}e.emit("device-license",{tier:t.tier,isPro:t.isPro?"1":"0"}),F()},Q=t=>{a=t?.protocol===1&&t.ready===true,h()?V():Y()},ve=L.subscribe(()=>{L.hasAccountAccess()?h()&&V():(e.connected&&e.emit("device-license",{tier:"locked",isPro:"0"}),Y())}),K=()=>{a=false,e.emit("buoy:access:request")},Z=()=>{a=false,Y()};e.on("buoy:access",Q);for(let[t,i]of s)n()[t]&&A(t,i);return e.on("connect",K),e.on("disconnect",Z),e.on("devtool-watch",d),e.on("request-devtool-state",u),e.on("devtool-action",N),e.connected&&K(),()=>{c=true,ve(),e.off("buoy:access",Q),Y(),ie(O),e.off("connect",K),e.off("disconnect",Z),e.off("devtool-watch",d),e.off("request-devtool-state",u),e.off("devtool-action",N);for(let t of[...m.keys()])w(t)}}function we({tools:e,socket:o,deviceId:n,enabled:s=true,throttleMs:l=200,enableLogs:r=false}){let c=_e(),a=c.status==="admitted",y=re(e);y.current=e;let h=re(new Map);oe(()=>{if(!o||c.status!=="locked")return;let _=()=>o.emit("device-license",{tier:"locked",isPro:"0"});return o.connected&&_(),o.on("connect",_),()=>{o.off("connect",_)}},[o,c.status]);let m=Object.keys(e).sort().join(",");oe(()=>{if(!o||!n||!s||!a){h.current.clear();return}return Ee({socket:o,deviceId:n,getTools:()=>y.current,watched:h.current,throttleMs:l,enableLogs:r})},[o,n,s,a,l,r,m])}function Be(e,o){let n=()=>o(false),s=()=>{n(),e.emit("buoy:access:request")},l=r=>{let c=r;o(c?.protocol===1&&c?.ready===true)};return e.on("connect",s),e.on("disconnect",n),e.on("buoy:access",l),n(),e.connected&&s(),()=>{e.off("connect",s),e.off("disconnect",n),e.off("buoy:access",l)}}import{useEffect as J,useRef as fe,useState as W}from"react";import{io as Re}from"socket.io-client";function Oe(e,o){return e}var Ne=42831;function Ue(e){return`http://${e??"localhost"}:42831`}function De(){return null}function de(){return Ue(typeof location>"u"?null:location.hostname)}var X={targetURL:null,state:"idle",lastError:null,since:0},G=new Set;function T(e){X={...X,...e,since:x()},G.forEach(o=>o())}function Le(){return X}function xe(e){return G.add(e),()=>{G.delete(e)}}var Pe=typeof ne<"u"?ne:false,$=null,C="",pe="",ye="",q=0,M=null,Me=2e3,he=()=>`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`;function Ae(){let e=globalThis;return e.__buoyInstanceId||(e.__buoyInstanceId=he()),e.__buoyInstanceId}function Ye(e){let o=n=>n&&Object.fromEntries(Object.entries(n).filter(([s])=>!["BUOY_LICENSE_KEY","BUOY_KEY","EXPO_PUBLIC_BUOY_KEY","VITE_BUOY_KEY","NEXT_PUBLIC_BUOY_KEY","REACT_APP_BUOY_KEY","licenseKey"].includes(s)));return{deviceName:e.deviceName,deviceId:e.persistentDeviceId,platform:e.platform,extraDeviceInfo:JSON.stringify(o(e.extraDeviceInfo)),envVariables:JSON.stringify(o(e.envVariables)),connectionNonce:he(),instanceId:Ae()}}function ge(e,o,n=globalThis.location){if(o!=="web"||!n?.host)return e;let s=n.pathname&&n.pathname!=="/"?n.pathname:"";return`${e} \xB7 ${n.host}${s.length>24?`${s.slice(0,23)}\u2026`:s}`}function Ce(e,o){e.addEventListener("pagehide",()=>o()?.disconnect()),e.addEventListener("pageshow",n=>{n.persisted&&o()?.connect()})}var me=false;function qe(){let e=globalThis;me||!e.document||typeof e.addEventListener!="function"||(me=true,Ce(e,()=>$))}function Fe({deviceName:e,socketURL:o,persistentDeviceId:n,extraDeviceInfo:s,envVariables:l,platform:r,enableLogs:c=false}){let a=fe(null),[y,h]=W(null),[m,_]=W(false),v=o??de(),k=fe(false),f=`[${e}]`,B=()=>{p(`${f} Socket connected successfully`,c),k.current=false,_(true),T({state:"connected",lastError:null})},I=d=>{p(`${f} Socket disconnected. Reason: ${d}`,c),_(false),T({state:"retrying"})},R=d=>{if(T({state:"retrying",lastError:d.message}),!Pe||k.current)return;k.current=true;let u=r==="android"&&/localhost|127\.0\.0\.1/.test(C)?"If this device is connected over USB, run: adb reverse tcp:42831 tcp:42831. ":"If it's running on another machine or port, pass socketURL via the externalSync prop. ";p(`[buoy] desktop sync: can't reach ${C} (is Buoy Desktop running?). Retrying in the background. ${u}(${d.message})`,c,"warn")},O=()=>{p(`${f} Socket connection timeout`,c,"error")};J(()=>{if(!n)return;let d=Oe(v,r);T({targetURL:d,state:"connecting",lastError:null}),p(`${f} Platform: ${r}, using URL: ${d}`,c);try{if($&&(C!==d||pe!==n||ye!==r)&&($.disconnect(),$=null),$)p(`${f} Reusing existing socket instance to ${d}`,c);else{p(`${f} Creating new socket instance to ${d}`,c);try{let N=globalThis;(N.__BUOY_INTERNAL_HOSTS__??(N.__BUOY_INTERNAL_HOSTS__=new Set)).add(new URL(d).host.toLowerCase())}catch{}$=Re(d,{autoConnect:true,query:Ye({deviceName:ge(e,r),persistentDeviceId:n,platform:r,extraDeviceInfo:s,envVariables:l}),reconnection:true,reconnectionAttempts:1/0,reconnectionDelay:1e3,reconnectionDelayMax:5e3,transports:["websocket"]}),Te($),qe(),C=d,pe=n,ye=r}a.current=$;let u=a.current;return q++,M&&(ue(M),M=null),h(u),u.on("connect_error",R),u.on("connect_timeout",O),_(u.connected),u.connected&&p(`${f} Socket already connected on init`,c),u.on("connect",B),u.on("disconnect",I),()=>{p(`${f} Cleaning up socket event listeners`,c),u.off("connect",B),u.off("disconnect",I),u.off("connect_error",R),u.off("connect_timeout",O),q--,q===0&&(M=le(()=>{M=null,!(q>0||$!==u)&&(p(`${f} No longer used; disconnecting`,c),u.disconnect(),$=null,T({state:"idle",lastError:null}))},Me))}}catch(u){p(`${f} Failed to initialize socket: ${u}`,c,"error")}},[n,v,r]),J(()=>{a.current&&a.current.io.opts.query&&n&&(p(`${f} Updating device name in socket connection`,c),a.current.io.opts.query={...a.current.io.opts.query,deviceName:ge(e,r),deviceId:n,platform:r})},[e,f,n,r,c]);function E(){a.current&&!a.current.connected&&(p(`${f} Manually connecting to socket server`,c),a.current.connect())}function A(){a.current&&a.current.connected&&(p(`${f} Manually disconnecting from socket server`,c),a.current.disconnect())}let[w,F]=W(null);return J(()=>{if(y)return Be(y,d=>{F(d?y:null),T(d?{state:"connected",lastError:null}:{state:"retrying",lastError:"Update Buoy Desktop and sign in to enable desktop sync."})})},[y]),{socket:w===y?y:null,connect:E,disconnect:A,isConnected:m&&w===y&&y!==null}}export{Ne as DEFAULT_BROKER_PORT,Ie as SYNC_PROTOCOL_VERSION,$e as flushToolSyncNow,de as getDefaultSocketURL,De as getMetroHost,Le as getSyncStatus,ce as registerToolFlusher,T as setSyncStatus,xe as subscribeSyncStatus,ie as unregisterToolFlusher,we as useExternalSync,Fe as useExternalSyncSocket};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/external-sync",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.76",
|
|
4
4
|
"description": "Generic device-to-desktop sync for Buoy dev tools",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
],
|
|
35
35
|
"sideEffects": false,
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"socket.io-client": "^4.8.
|
|
38
|
-
"@buoy-gg/license": "^7.0.
|
|
37
|
+
"socket.io-client": "^4.8.4",
|
|
38
|
+
"@buoy-gg/license": "^7.0.76"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "*"
|