@lensmcp/vite-plugin 1.24.1 → 1.24.2

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 CHANGED
@@ -48,6 +48,7 @@ lensmcpVitePlugin({ applyOnBuild: true }); // or LENSMCP_VITE_APPLY_ON_BUILD=1
48
48
  ## What it does
49
49
 
50
50
  - **`valtio` → `@lensmcp/valtio-instrumentation` alias.** A `config()` hook rewrites bare `import … from 'valtio'` to the instrumented drop-in, so existing Valtio stores become observable without edits. The alias uses an exact `/^valtio$/` match so the drop-in's own `valtio/vanilla` and `valtio/react` imports are left untouched. (Toggle with `aliasValtio`.)
51
+ - **Its own dependency cache in a worktree.** When the checkout's `node_modules` is a symlink to another checkout's (how agent worktrees are often made), the `config()` hook moves Vite's `cacheDir` to the checkout's own `.lensmcp/cache/vite/…`, so two checkouts' Vites never optimize into one folder. A checkout that owns `node_modules` is untouched.
51
52
  - **`@lensmcp/react-instrumentation` Babel transform.** A `transform()` hook runs the LensMCP Babel plugin (`lensmcpBabelPlugin`) over every `.jsx`/`.tsx` file outside `node_modules`, wrapping the app root, instrumenting hooks, and stamping JSX identity props. The output is plain JSX/TSX that downstream React compilers preserve. (Toggle with `transformSource`.)
52
53
  - **Browser event bridge / client runtime injection.** `transformIndexHtml()` injects `<script type="module" src="/@lensmcp/client-runtime">` into the page head; the plugin resolves that virtual module to a small client runtime. The runtime opens a WebSocket back to the plugin and forwards browser events — runtime errors, unhandled rejections, `console.*`, `fetch`, History/route changes, plus the pre-formed `publish` envelopes from the in-page React and Valtio instrumentation (via `window.__LENSMCP_PUBLISH__`). Each tab gets a stable per-tab id so multiple tabs stay distinct.
53
54
  - **Build + HMR events.** `handleHotUpdate()` emits an HMR event with the affected files, and `generateBundle()` emits a bundle-size report (per-chunk and per-module byte sizes). Vite's native `vite:error` stream is forwarded too.
@@ -68,6 +69,7 @@ Incoming browser envelopes and Vite/Rollup events are normalised into LensMCP's
68
69
  | `injectClient` | `boolean` | `true` | Inject the client runtime via a `<script>` tag. |
69
70
  | `aliasValtio` | `boolean` | `true` | Alias `valtio` to the instrumented drop-in in dev. |
70
71
  | `transformSource` | `boolean` | `true` | Run the LensMCP source transform (root-wrap + hooks + JSX ids). |
72
+ | `hmrRecovery` | `boolean` | `true` | When Vite gives up on its HMR socket at page load, the runtime always reports it (`hmr` `ws-failed`); with this on it also reloads the page once — only in a tab whose socket worked earlier, at most once per two minutes. `false` = report only. |
71
73
 
72
74
  The returned plugin also exposes a `wsPort(): number \| undefined` accessor (`LensmcpViteRuntime`), available after Vite's `configureServer` hook fires.
73
75
 
@@ -1,2 +1,2 @@
1
- "use strict";var se=Object.defineProperty;var u=(e,n)=>se(e,"name",{value:n,configurable:!0});var fe=Object.defineProperty,v=u((e,n)=>fe(e,"name",{value:n,configurable:!0}),"c"),pe=Object.defineProperty,s=v((e,n)=>pe(e,"name",{value:n,configurable:!0}),"c"),de=Object.defineProperty,f=s((e,n)=>de(e,"name",{value:n,configurable:!0}),"c"),be=Object.defineProperty,p=f((e,n)=>be(e,"name",{value:n,configurable:!0}),"u"),me=Object.defineProperty,d=p((e,n)=>me(e,"name",{value:n,configurable:!0}),"u"),ge=Object.defineProperty,b=d((e,n)=>ge(e,"name",{value:n,configurable:!0}),"u"),ye=Object.defineProperty,m=b((e,n)=>ye(e,"name",{value:n,configurable:!0}),"c"),je=Object.defineProperty,g=m((e,n)=>je(e,"name",{value:n,configurable:!0}),"c"),Te=Object.defineProperty,y=g((e,n)=>Te(e,"name",{value:n,configurable:!0}),"c"),Ee=Object.defineProperty,j=y((e,n)=>Ee(e,"name",{value:n,configurable:!0}),"u"),Oe=Object.defineProperty,T=j((e,n)=>Oe(e,"name",{value:n,configurable:!0}),"u"),Pe=Object.defineProperty,E=T((e,n)=>Pe(e,"name",{value:n,configurable:!0}),"u"),Ie=Object.defineProperty,O=E((e,n)=>Ie(e,"name",{value:n,configurable:!0}),"c"),we=Object.defineProperty,P=O((e,n)=>we(e,"name",{value:n,configurable:!0}),"c"),ke=Object.defineProperty,I=P((e,n)=>ke(e,"name",{value:n,configurable:!0}),"c"),$e=Object.defineProperty,w=I((e,n)=>$e(e,"name",{value:n,configurable:!0}),"u"),he=Object.defineProperty,k=w((e,n)=>he(e,"name",{value:n,configurable:!0}),"u"),xe=Object.defineProperty,$=k((e,n)=>xe(e,"name",{value:n,configurable:!0}),"u"),Se=Object.defineProperty,h=$((e,n)=>Se(e,"name",{value:n,configurable:!0}),"u"),Ue=Object.defineProperty,x=h((e,n)=>Ue(e,"name",{value:n,configurable:!0}),"u"),Re=Object.defineProperty,S=x((e,n)=>Re(e,"name",{value:n,configurable:!0}),"u"),Ae=Object.defineProperty,U=S((e,n)=>Ae(e,"name",{value:n,configurable:!0}),"u"),Ce=Object.defineProperty,R=U((e,n)=>Ce(e,"name",{value:n,configurable:!0}),"u"),Fe=Object.defineProperty,A=R((e,n)=>Fe(e,"name",{value:n,configurable:!0}),"u"),Je=Object.defineProperty,C=A((e,n)=>Je(e,"name",{value:n,configurable:!0}),"s"),Le=Object.defineProperty,F=C((e,n)=>Le(e,"name",{value:n,configurable:!0}),"s"),Ne=Object.defineProperty,J=F((e,n)=>Ne(e,"name",{value:n,configurable:!0}),"s"),qe=Object.defineProperty,L=J((e,n)=>qe(e,"name",{value:n,configurable:!0}),"c"),ze=Object.defineProperty,N=L((e,n)=>ze(e,"name",{value:n,configurable:!0}),"c"),Be=Object.defineProperty,q=N((e,n)=>Be(e,"name",{value:n,configurable:!0}),"c"),De=Object.defineProperty,z=q((e,n)=>De(e,"name",{value:n,configurable:!0}),"c"),Ge=Object.defineProperty,B=z((e,n)=>Ge(e,"name",{value:n,configurable:!0}),"c"),He=Object.defineProperty,D=B((e,n)=>He(e,"name",{value:n,configurable:!0}),"c"),Ke=Object.defineProperty,G=D((e,n)=>Ke(e,"name",{value:n,configurable:!0}),"l"),Me=Object.defineProperty,H=G((e,n)=>Me(e,"name",{value:n,configurable:!0}),"l"),Qe=Object.defineProperty,K=H((e,n)=>Qe(e,"name",{value:n,configurable:!0}),"c"),Ve=Object.defineProperty,M=K((e,n)=>Ve(e,"name",{value:n,configurable:!0}),"c"),We=Object.defineProperty,Q=M((e,n)=>We(e,"name",{value:n,configurable:!0}),"c"),Xe=Object.defineProperty,V=Q((e,n)=>Xe(e,"name",{value:n,configurable:!0}),"u"),Ye=Object.defineProperty,W=V((e,n)=>Ye(e,"name",{value:n,configurable:!0}),"u"),Ze=Object.defineProperty,X=W((e,n)=>Ze(e,"name",{value:n,configurable:!0}),"u"),_e=Object.defineProperty,Y=X((e,n)=>_e(e,"name",{value:n,configurable:!0}),"c"),en=Object.defineProperty,Z=Y((e,n)=>en(e,"name",{value:n,configurable:!0}),"c"),nn=Object.defineProperty,_=Z((e,n)=>nn(e,"name",{value:n,configurable:!0}),"c"),tn=Object.defineProperty,ee=_((e,n)=>tn(e,"name",{value:n,configurable:!0}),"u"),rn=Object.defineProperty,ne=ee((e,n)=>rn(e,"name",{value:n,configurable:!0}),"u"),on=Object.defineProperty,te=ne((e,n)=>on(e,"name",{value:n,configurable:!0}),"u"),an=Object.defineProperty,re=te((e,n)=>an(e,"name",{value:n,configurable:!0}),"u"),ln=Object.defineProperty,oe=re((e,n)=>ln(e,"name",{value:n,configurable:!0}),"o"),cn=Object.defineProperty,ie=oe((e,n)=>cn(e,"name",{value:n,configurable:!0}),"o"),un=Object.defineProperty,ae=ie((e,n)=>un(e,"name",{value:n,configurable:!0}),"o"),vn=Object.defineProperty,le=ae((e,n)=>vn(e,"name",{value:n,configurable:!0}),"o"),sn=Object.defineProperty,ce=le((e,n)=>sn(e,"name",{value:n,configurable:!0}),"o"),fn=Object.defineProperty,ue=ce((e,n)=>fn(e,"name",{value:n,configurable:!0}),"o");import{fingerprint as l,ulid as c}from"@lensmcp/core";const pn="pending";export function envelopeToEvent(e,n=pn){const i=e.tabId?{tabId:e.tabId}:{};switch(e.kind){case"error":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:n,...i},fingerprint:l({kind:"runtime",identity:`error:${t(e.stack)}`,file:e.source,detail:e.message}),title:e.message,message:e.stack??e.message,location:e.source?{file:e.source,line:e.lineno,column:e.colno}:void 0,raw:e};case"unhandledrejection":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:n,...i},fingerprint:l({kind:"runtime",identity:`unhandledrejection:${e.reason.slice(0,80)}`}),title:"Unhandled promise rejection",message:e.reason,raw:e};case"console":{const a=e.level==="error"?"error":e.level==="warn"?"warning":"info",ve=e.args.map(o).join(" ");return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:a,context:{sessionId:n,...i},fingerprint:l({kind:"runtime",identity:`console:${e.level}:${ve.slice(0,80)}`}),title:`console.${e.level}`,message:ve,raw:e}}case"fetch":{const a=e.error!==void 0||e.status!==void 0&&e.status>=400;return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"network",severity:a?"error":"info",context:{sessionId:n,...i},fingerprint:l({kind:"network",identity:`${e.method}:${r(e.url)}:${e.status??"err"}`}),title:`${e.method} ${e.url}${e.status!==void 0?` \u2192 ${e.status}`:""}`,message:e.error,relatedUrls:[e.url],raw:e}}case"route":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:n,url:e.url,...i},fingerprint:l({kind:"runtime",identity:`route:${e.url}`}),title:`Route \u2192 ${e.url}`,raw:e};case"ready":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:n,url:e.url,...i},fingerprint:l({kind:"runtime",identity:"client-ready"}),title:"Client runtime ready",message:e.userAgent,raw:e};case"publish":return{id:c(),sessionId:n,timestamp:e.at,source:e.source,category:e.category,severity:e.severity??"info",context:{sessionId:n,...i,...e.context??{}},fingerprint:e.fingerprint??l({kind:"publish",identity:e.title}),title:e.title,message:e.message,raw:e.raw}}}u(envelopeToEvent,"envelopeToEvent"),v(envelopeToEvent,"envelopeToEvent"),s(envelopeToEvent,"envelopeToEvent"),f(envelopeToEvent,"envelopeToEvent"),p(envelopeToEvent,"envelopeToEvent"),d(envelopeToEvent,"envelopeToEvent"),b(envelopeToEvent,"envelopeToEvent"),m(envelopeToEvent,"envelopeToEvent"),g(envelopeToEvent,"envelopeToEvent"),y(envelopeToEvent,"envelopeToEvent"),j(envelopeToEvent,"envelopeToEvent"),T(envelopeToEvent,"envelopeToEvent"),E(envelopeToEvent,"envelopeToEvent"),O(envelopeToEvent,"envelopeToEvent"),P(envelopeToEvent,"envelopeToEvent"),I(envelopeToEvent,"envelopeToEvent"),w(envelopeToEvent,"envelopeToEvent"),k(envelopeToEvent,"envelopeToEvent"),$(envelopeToEvent,"envelopeToEvent"),h(envelopeToEvent,"envelopeToEvent"),x(envelopeToEvent,"envelopeToEvent"),S(envelopeToEvent,"envelopeToEvent"),U(envelopeToEvent,"envelopeToEvent"),R(envelopeToEvent,"envelopeToEvent"),A(envelopeToEvent,"envelopeToEvent"),C(envelopeToEvent,"envelopeToEvent"),F(envelopeToEvent,"envelopeToEvent"),J(envelopeToEvent,"envelopeToEvent"),L(envelopeToEvent,"envelopeToEvent"),N(envelopeToEvent,"envelopeToEvent"),q(envelopeToEvent,"envelopeToEvent"),z(envelopeToEvent,"envelopeToEvent"),B(envelopeToEvent,"envelopeToEvent"),D(envelopeToEvent,"envelopeToEvent"),G(envelopeToEvent,"envelopeToEvent"),H(envelopeToEvent,"envelopeToEvent"),K(envelopeToEvent,"envelopeToEvent"),M(envelopeToEvent,"envelopeToEvent"),Q(envelopeToEvent,"envelopeToEvent"),V(envelopeToEvent,"envelopeToEvent"),W(envelopeToEvent,"envelopeToEvent"),X(envelopeToEvent,"envelopeToEvent"),Y(envelopeToEvent,"envelopeToEvent"),Z(envelopeToEvent,"envelopeToEvent"),_(envelopeToEvent,"envelopeToEvent"),ee(envelopeToEvent,"envelopeToEvent"),ne(envelopeToEvent,"envelopeToEvent"),te(envelopeToEvent,"envelopeToEvent"),re(envelopeToEvent,"envelopeToEvent"),oe(envelopeToEvent,"envelopeToEvent"),ie(envelopeToEvent,"envelopeToEvent"),ae(envelopeToEvent,"envelopeToEvent"),le(envelopeToEvent,"envelopeToEvent"),ce(envelopeToEvent,"envelopeToEvent"),ue(envelopeToEvent,"envelopeToEvent");function t(e){if(!e)return"unknown";const n=e.split(`
2
- `).slice(0,2);for(const i of n){const a=i.match(/at\s+(\S+)/);if(a&&a[1])return a[1]}return"unknown"}u(t,"t"),v(t,"t"),s(t,"t"),f(t,"t"),p(t,"t"),d(t,"t"),b(t,"t"),m(t,"t"),g(t,"t"),y(t,"t"),j(t,"t"),T(t,"t"),E(t,"t"),O(t,"t"),P(t,"t"),I(t,"t"),w(t,"t"),k(t,"t"),$(t,"t"),h(t,"t"),x(t,"t"),S(t,"t"),U(t,"t"),R(t,"t"),A(t,"t"),C(t,"t"),F(t,"t"),J(t,"t"),L(t,"t"),N(t,"t"),q(t,"t"),z(t,"n"),B(t,"t"),D(t,"n"),G(t,"t"),H(t,"n"),K(t,"t"),M(t,"n"),Q(t,"t"),V(t,"r"),W(t,"t"),X(t,"r"),Y(t,"t"),Z(t,"n"),_(t,"n"),ee(t,"r"),ne(t,"n"),te(t,"o"),re(t,"a"),oe(t,"l"),ie(t,"l"),ae(t,"c"),le(t,"c"),ce(t,"s"),ue(t,"normaliseStackTopFrame");function r(e){try{return`${new URL(e,"http://x").pathname}`}catch{return e}}u(r,"r"),v(r,"r"),s(r,"r"),f(r,"r"),p(r,"r"),d(r,"r"),b(r,"r"),m(r,"r"),g(r,"r"),y(r,"r"),j(r,"r"),T(r,"r"),E(r,"r"),O(r,"r"),P(r,"r"),I(r,"r"),w(r,"r"),k(r,"r"),$(r,"r"),h(r,"r"),x(r,"r"),S(r,"r"),U(r,"r"),R(r,"r"),A(r,"r"),C(r,"r"),F(r,"r"),J(r,"r"),L(r,"r"),N(r,"r"),q(r,"r"),z(r,"r"),B(r,"r"),D(r,"r"),G(r,"r"),H(r,"r"),K(r,"r"),M(r,"r"),Q(r,"r"),V(r,"n"),W(r,"r"),X(r,"n"),Y(r,"r"),Z(r,"r"),_(r,"r"),ee(r,"i"),ne(r,"i"),te(r,"a"),re(r,"c"),oe(r,"d"),ie(r,"d"),ae(r,"l"),le(r,"l"),ce(r,"m"),ue(r,"normaliseUrl");function o(e){if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}u(o,"o"),v(o,"o"),s(o,"o"),f(o,"o"),p(o,"o"),d(o,"o"),b(o,"o"),m(o,"o"),g(o,"o"),y(o,"o"),j(o,"o"),T(o,"o"),E(o,"o"),O(o,"o"),P(o,"o"),I(o,"o"),w(o,"o"),k(o,"o"),$(o,"o"),h(o,"o"),x(o,"o"),S(o,"i"),U(o,"o"),R(o,"i"),A(o,"i"),C(o,"i"),F(o,"i"),J(o,"i"),L(o,"i"),N(o,"i"),q(o,"i"),z(o,"i"),B(o,"i"),D(o,"i"),G(o,"i"),H(o,"i"),K(o,"i"),M(o,"i"),Q(o,"i"),V(o,"i"),W(o,"i"),X(o,"i"),Y(o,"i"),Z(o,"i"),_(o,"i"),ee(o,"o"),ne(o,"o"),te(o,"c"),re(o,"u"),oe(o,"m"),ie(o,"m"),ae(o,"d"),le(o,"d"),ce(o,"d"),ue(o,"stringify");
1
+ "use strict";var pe=Object.defineProperty;var u=(e,n)=>pe(e,"name",{value:n,configurable:!0});var de=Object.defineProperty,v=u((e,n)=>de(e,"name",{value:n,configurable:!0}),"u"),be=Object.defineProperty,s=v((e,n)=>be(e,"name",{value:n,configurable:!0}),"u"),me=Object.defineProperty,f=s((e,n)=>me(e,"name",{value:n,configurable:!0}),"c"),ge=Object.defineProperty,p=f((e,n)=>ge(e,"name",{value:n,configurable:!0}),"c"),ye=Object.defineProperty,d=p((e,n)=>ye(e,"name",{value:n,configurable:!0}),"c"),je=Object.defineProperty,b=d((e,n)=>je(e,"name",{value:n,configurable:!0}),"u"),Te=Object.defineProperty,m=b((e,n)=>Te(e,"name",{value:n,configurable:!0}),"u"),Ee=Object.defineProperty,g=m((e,n)=>Ee(e,"name",{value:n,configurable:!0}),"u"),Oe=Object.defineProperty,y=g((e,n)=>Oe(e,"name",{value:n,configurable:!0}),"c"),Pe=Object.defineProperty,j=y((e,n)=>Pe(e,"name",{value:n,configurable:!0}),"c"),Ie=Object.defineProperty,T=j((e,n)=>Ie(e,"name",{value:n,configurable:!0}),"c"),we=Object.defineProperty,E=T((e,n)=>we(e,"name",{value:n,configurable:!0}),"u"),ke=Object.defineProperty,O=E((e,n)=>ke(e,"name",{value:n,configurable:!0}),"u"),$e=Object.defineProperty,P=O((e,n)=>$e(e,"name",{value:n,configurable:!0}),"u"),he=Object.defineProperty,I=P((e,n)=>he(e,"name",{value:n,configurable:!0}),"c"),xe=Object.defineProperty,w=I((e,n)=>xe(e,"name",{value:n,configurable:!0}),"c"),Se=Object.defineProperty,k=w((e,n)=>Se(e,"name",{value:n,configurable:!0}),"c"),Ue=Object.defineProperty,$=k((e,n)=>Ue(e,"name",{value:n,configurable:!0}),"u"),Re=Object.defineProperty,h=$((e,n)=>Re(e,"name",{value:n,configurable:!0}),"u"),Ae=Object.defineProperty,x=h((e,n)=>Ae(e,"name",{value:n,configurable:!0}),"u"),Ce=Object.defineProperty,S=x((e,n)=>Ce(e,"name",{value:n,configurable:!0}),"u"),Fe=Object.defineProperty,U=S((e,n)=>Fe(e,"name",{value:n,configurable:!0}),"u"),Je=Object.defineProperty,R=U((e,n)=>Je(e,"name",{value:n,configurable:!0}),"u"),Le=Object.defineProperty,A=R((e,n)=>Le(e,"name",{value:n,configurable:!0}),"u"),Ne=Object.defineProperty,C=A((e,n)=>Ne(e,"name",{value:n,configurable:!0}),"u"),qe=Object.defineProperty,F=C((e,n)=>qe(e,"name",{value:n,configurable:!0}),"u"),ze=Object.defineProperty,J=F((e,n)=>ze(e,"name",{value:n,configurable:!0}),"s"),Be=Object.defineProperty,L=J((e,n)=>Be(e,"name",{value:n,configurable:!0}),"s"),De=Object.defineProperty,N=L((e,n)=>De(e,"name",{value:n,configurable:!0}),"s"),Ge=Object.defineProperty,q=N((e,n)=>Ge(e,"name",{value:n,configurable:!0}),"c"),He=Object.defineProperty,z=q((e,n)=>He(e,"name",{value:n,configurable:!0}),"c"),Ke=Object.defineProperty,B=z((e,n)=>Ke(e,"name",{value:n,configurable:!0}),"c"),Me=Object.defineProperty,D=B((e,n)=>Me(e,"name",{value:n,configurable:!0}),"c"),Qe=Object.defineProperty,G=D((e,n)=>Qe(e,"name",{value:n,configurable:!0}),"c"),Ve=Object.defineProperty,H=G((e,n)=>Ve(e,"name",{value:n,configurable:!0}),"c"),We=Object.defineProperty,K=H((e,n)=>We(e,"name",{value:n,configurable:!0}),"l"),Xe=Object.defineProperty,M=K((e,n)=>Xe(e,"name",{value:n,configurable:!0}),"l"),Ye=Object.defineProperty,Q=M((e,n)=>Ye(e,"name",{value:n,configurable:!0}),"c"),Ze=Object.defineProperty,V=Q((e,n)=>Ze(e,"name",{value:n,configurable:!0}),"c"),_e=Object.defineProperty,W=V((e,n)=>_e(e,"name",{value:n,configurable:!0}),"c"),en=Object.defineProperty,X=W((e,n)=>en(e,"name",{value:n,configurable:!0}),"u"),nn=Object.defineProperty,Y=X((e,n)=>nn(e,"name",{value:n,configurable:!0}),"u"),tn=Object.defineProperty,Z=Y((e,n)=>tn(e,"name",{value:n,configurable:!0}),"u"),rn=Object.defineProperty,_=Z((e,n)=>rn(e,"name",{value:n,configurable:!0}),"c"),on=Object.defineProperty,ee=_((e,n)=>on(e,"name",{value:n,configurable:!0}),"c"),an=Object.defineProperty,ne=ee((e,n)=>an(e,"name",{value:n,configurable:!0}),"c"),ln=Object.defineProperty,te=ne((e,n)=>ln(e,"name",{value:n,configurable:!0}),"u"),cn=Object.defineProperty,re=te((e,n)=>cn(e,"name",{value:n,configurable:!0}),"u"),un=Object.defineProperty,oe=re((e,n)=>un(e,"name",{value:n,configurable:!0}),"u"),vn=Object.defineProperty,ie=oe((e,n)=>vn(e,"name",{value:n,configurable:!0}),"u"),sn=Object.defineProperty,ae=ie((e,n)=>sn(e,"name",{value:n,configurable:!0}),"o"),fn=Object.defineProperty,le=ae((e,n)=>fn(e,"name",{value:n,configurable:!0}),"o"),pn=Object.defineProperty,ce=le((e,n)=>pn(e,"name",{value:n,configurable:!0}),"o"),dn=Object.defineProperty,ue=ce((e,n)=>dn(e,"name",{value:n,configurable:!0}),"o"),bn=Object.defineProperty,ve=ue((e,n)=>bn(e,"name",{value:n,configurable:!0}),"o"),mn=Object.defineProperty,se=ve((e,n)=>mn(e,"name",{value:n,configurable:!0}),"o");import{fingerprint as l,ulid as c}from"@lensmcp/core";const gn="pending";export function envelopeToEvent(e,n=gn){const i=e.tabId?{tabId:e.tabId}:{};switch(e.kind){case"error":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:n,...i},fingerprint:l({kind:"runtime",identity:`error:${t(e.stack)}`,file:e.source,detail:e.message}),title:e.message,message:e.stack??e.message,location:e.source?{file:e.source,line:e.lineno,column:e.colno}:void 0,raw:e};case"unhandledrejection":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:n,...i},fingerprint:l({kind:"runtime",identity:`unhandledrejection:${e.reason.slice(0,80)}`}),title:"Unhandled promise rejection",message:e.reason,raw:e};case"console":{const a=e.level==="error"?"error":e.level==="warn"?"warning":"info",fe=e.args.map(o).join(" ");return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:a,context:{sessionId:n,...i},fingerprint:l({kind:"runtime",identity:`console:${e.level}:${fe.slice(0,80)}`}),title:`console.${e.level}`,message:fe,raw:e}}case"fetch":{const a=e.error!==void 0||e.status!==void 0&&e.status>=400;return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"network",severity:a?"error":"info",context:{sessionId:n,...i},fingerprint:l({kind:"network",identity:`${e.method}:${r(e.url)}:${e.status??"err"}`}),title:`${e.method} ${e.url}${e.status!==void 0?` \u2192 ${e.status}`:""}`,message:e.error,relatedUrls:[e.url],raw:e}}case"route":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:n,url:e.url,...i},fingerprint:l({kind:"runtime",identity:`route:${e.url}`}),title:`Route \u2192 ${e.url}`,raw:e};case"ready":return{id:c(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:n,url:e.url,...i},fingerprint:l({kind:"runtime",identity:"client-ready"}),title:"Client runtime ready",message:e.userAgent,raw:e};case"publish":return{id:c(),sessionId:n,timestamp:e.at,source:e.source,category:e.category,severity:e.severity??"info",context:{sessionId:n,...i,...e.context??{}},fingerprint:e.fingerprint??l({kind:"publish",identity:e.title}),title:e.title,message:e.message,raw:e.raw}}}u(envelopeToEvent,"envelopeToEvent"),v(envelopeToEvent,"envelopeToEvent"),s(envelopeToEvent,"envelopeToEvent"),f(envelopeToEvent,"envelopeToEvent"),p(envelopeToEvent,"envelopeToEvent"),d(envelopeToEvent,"envelopeToEvent"),b(envelopeToEvent,"envelopeToEvent"),m(envelopeToEvent,"envelopeToEvent"),g(envelopeToEvent,"envelopeToEvent"),y(envelopeToEvent,"envelopeToEvent"),j(envelopeToEvent,"envelopeToEvent"),T(envelopeToEvent,"envelopeToEvent"),E(envelopeToEvent,"envelopeToEvent"),O(envelopeToEvent,"envelopeToEvent"),P(envelopeToEvent,"envelopeToEvent"),I(envelopeToEvent,"envelopeToEvent"),w(envelopeToEvent,"envelopeToEvent"),k(envelopeToEvent,"envelopeToEvent"),$(envelopeToEvent,"envelopeToEvent"),h(envelopeToEvent,"envelopeToEvent"),x(envelopeToEvent,"envelopeToEvent"),S(envelopeToEvent,"envelopeToEvent"),U(envelopeToEvent,"envelopeToEvent"),R(envelopeToEvent,"envelopeToEvent"),A(envelopeToEvent,"envelopeToEvent"),C(envelopeToEvent,"envelopeToEvent"),F(envelopeToEvent,"envelopeToEvent"),J(envelopeToEvent,"envelopeToEvent"),L(envelopeToEvent,"envelopeToEvent"),N(envelopeToEvent,"envelopeToEvent"),q(envelopeToEvent,"envelopeToEvent"),z(envelopeToEvent,"envelopeToEvent"),B(envelopeToEvent,"envelopeToEvent"),D(envelopeToEvent,"envelopeToEvent"),G(envelopeToEvent,"envelopeToEvent"),H(envelopeToEvent,"envelopeToEvent"),K(envelopeToEvent,"envelopeToEvent"),M(envelopeToEvent,"envelopeToEvent"),Q(envelopeToEvent,"envelopeToEvent"),V(envelopeToEvent,"envelopeToEvent"),W(envelopeToEvent,"envelopeToEvent"),X(envelopeToEvent,"envelopeToEvent"),Y(envelopeToEvent,"envelopeToEvent"),Z(envelopeToEvent,"envelopeToEvent"),_(envelopeToEvent,"envelopeToEvent"),ee(envelopeToEvent,"envelopeToEvent"),ne(envelopeToEvent,"envelopeToEvent"),te(envelopeToEvent,"envelopeToEvent"),re(envelopeToEvent,"envelopeToEvent"),oe(envelopeToEvent,"envelopeToEvent"),ie(envelopeToEvent,"envelopeToEvent"),ae(envelopeToEvent,"envelopeToEvent"),le(envelopeToEvent,"envelopeToEvent"),ce(envelopeToEvent,"envelopeToEvent"),ue(envelopeToEvent,"envelopeToEvent"),ve(envelopeToEvent,"envelopeToEvent"),se(envelopeToEvent,"envelopeToEvent");function t(e){if(!e)return"unknown";const n=e.split(`
2
+ `).slice(0,2);for(const i of n){const a=i.match(/at\s+(\S+)/);if(a&&a[1])return a[1]}return"unknown"}u(t,"t"),v(t,"t"),s(t,"t"),f(t,"t"),p(t,"t"),d(t,"t"),b(t,"t"),m(t,"t"),g(t,"t"),y(t,"t"),j(t,"t"),T(t,"t"),E(t,"t"),O(t,"t"),P(t,"t"),I(t,"t"),w(t,"t"),k(t,"t"),$(t,"t"),h(t,"t"),x(t,"t"),S(t,"t"),U(t,"t"),R(t,"t"),A(t,"t"),C(t,"t"),F(t,"t"),J(t,"t"),L(t,"t"),N(t,"t"),q(t,"t"),z(t,"t"),B(t,"t"),D(t,"n"),G(t,"t"),H(t,"n"),K(t,"t"),M(t,"n"),Q(t,"t"),V(t,"n"),W(t,"t"),X(t,"r"),Y(t,"t"),Z(t,"r"),_(t,"t"),ee(t,"n"),ne(t,"n"),te(t,"r"),re(t,"n"),oe(t,"o"),ie(t,"a"),ae(t,"l"),le(t,"l"),ce(t,"c"),ue(t,"c"),ve(t,"s"),se(t,"normaliseStackTopFrame");function r(e){try{return`${new URL(e,"http://x").pathname}`}catch{return e}}u(r,"r"),v(r,"r"),s(r,"r"),f(r,"r"),p(r,"r"),d(r,"r"),b(r,"r"),m(r,"r"),g(r,"r"),y(r,"r"),j(r,"r"),T(r,"r"),E(r,"r"),O(r,"r"),P(r,"r"),I(r,"r"),w(r,"r"),k(r,"r"),$(r,"r"),h(r,"r"),x(r,"r"),S(r,"r"),U(r,"r"),R(r,"r"),A(r,"r"),C(r,"r"),F(r,"r"),J(r,"r"),L(r,"r"),N(r,"r"),q(r,"r"),z(r,"r"),B(r,"r"),D(r,"r"),G(r,"r"),H(r,"r"),K(r,"r"),M(r,"r"),Q(r,"r"),V(r,"r"),W(r,"r"),X(r,"n"),Y(r,"r"),Z(r,"n"),_(r,"r"),ee(r,"r"),ne(r,"r"),te(r,"i"),re(r,"i"),oe(r,"a"),ie(r,"c"),ae(r,"d"),le(r,"d"),ce(r,"l"),ue(r,"l"),ve(r,"m"),se(r,"normaliseUrl");function o(e){if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}u(o,"o"),v(o,"o"),s(o,"o"),f(o,"o"),p(o,"o"),d(o,"o"),b(o,"o"),m(o,"o"),g(o,"o"),y(o,"o"),j(o,"o"),T(o,"o"),E(o,"o"),O(o,"o"),P(o,"o"),I(o,"o"),w(o,"o"),k(o,"o"),$(o,"o"),h(o,"o"),x(o,"o"),S(o,"o"),U(o,"o"),R(o,"i"),A(o,"o"),C(o,"i"),F(o,"i"),J(o,"i"),L(o,"i"),N(o,"i"),q(o,"i"),z(o,"i"),B(o,"i"),D(o,"i"),G(o,"i"),H(o,"i"),K(o,"i"),M(o,"i"),Q(o,"i"),V(o,"i"),W(o,"i"),X(o,"i"),Y(o,"i"),Z(o,"i"),_(o,"i"),ee(o,"i"),ne(o,"i"),te(o,"o"),re(o,"o"),oe(o,"c"),ie(o,"u"),ae(o,"m"),le(o,"m"),ce(o,"d"),ue(o,"d"),ve(o,"d"),se(o,"stringify");
package/lib/plugin.d.ts CHANGED
@@ -1,6 +1,17 @@
1
1
  import type { Plugin } from 'vite';
2
2
  import { type EventBus } from '@lensmcp/core';
3
3
  import type { LensmcpViteOptions, LensmcpViteRuntime } from './types.js';
4
+ /**
5
+ * The dependency cache this app's Vite should use when the checkout's `node_modules` is NOT its own — a symlink to
6
+ * another checkout's, as agent worktrees are made — or `undefined` to leave Vite's choice alone.
7
+ *
8
+ * Vite keeps its pre-bundled dependencies under `node_modules/.vite` (Nx apps pin `cacheDir` there). Through a
9
+ * symlinked `node_modules` two checkouts' Vites then optimize into ONE folder, and each rewrite is a "new
10
+ * dependencies optimized, reloading" for the other's open tabs. So the cache moves to the checkout's own
11
+ * `.lensmcp/cache/vite/…` (git-ignored with the rest of `.lensmcp/`), keeping the path below `node_modules/`.
12
+ * A checkout that owns its `node_modules` is untouched. Mirrors `@lensmcp/cluster`'s checkout-cache.ts.
13
+ */
14
+ export declare function checkoutViteCacheDir(root: string | undefined, cacheDir: string | undefined, cwd?: string): string | undefined;
4
15
  /**
5
16
  * PURE (fs reads only): the extra roots to add to Vite's watcher for a source SCOPE the gateway handed over
6
17
  * (`LENSMCP_WATCH_DIRS`, `path.delimiter`-joined absolute dirs — e.g. `<ws>/web:<ws>/shared:<ws>/ai`).
package/lib/plugin.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var V=Object.defineProperty;var g=(e,t)=>V(e,"name",{value:t,configurable:!0});var $=Object.defineProperty,d=g((e,t)=>$(e,"name",{value:t,configurable:!0}),"c");import{randomBytes as J}from"node:crypto";import{existsSync as G,readdirSync as K,statSync as q}from"node:fs";import{delimiter as Y,join as B,relative as R,resolve as T,sep as O}from"node:path";import{fingerprint as I,ulid as F}from"@lensmcp/core";import{BRIDGE_TOKEN_PARAM as z,createBridgeServer as Z,makePublisher as L,handleClientEnvelope as Q}from"@lensmcp/bridge";import{CLIENT_VIRTUAL_ID as D,createRuntimeModuleGuard as ee,RESOLVED_CLIENT_VIRTUAL_ID as N}from"./runtime-guard.js";const te="/__lensmcp/watcher-health",x=256,ne=64,se=new Set(["node_modules",".git","dist","tmp","coverage","build","out","target",".nx",".lensmcp"]);export function resolveExtraWatchRoots(e,t){if(!e)return[];const s=T(t),p=d((u,r)=>{const a=R(r,u);return a!==""&&!a.startsWith("..")&&!a.startsWith(O)&&!/^[A-Za-z]:/.test(a)},"inside"),i=[],f=d(u=>{i.length<ne&&!i.includes(u)&&i.push(u)},"push");for(const u of e.split(Y)){const r=u.trim();if(!r)continue;const a=T(r);let y;try{y=G(a)&&q(a).isDirectory()}catch{y=!1}if(y&&!(a===s||p(a,s))){if(p(s,a)){const _=R(a,s).split(O).filter(Boolean);let w=a;for(const n of _){let l;try{l=K(w,{withFileTypes:!0})}catch{l=[]}for(const o of l)!o.isDirectory()||o.name===n||o.name.startsWith(".")||se.has(o.name)||f(B(w,o.name));w=B(w,n)}continue}f(a)}}return i}g(resolveExtraWatchRoots,"resolveExtraWatchRoots"),d(resolveExtraWatchRoots,"resolveExtraWatchRoots");export function lensmcpVitePlugin(e={},t={}){const s=e.enabled??!0,p=e.wsHost??"127.0.0.1",i=e.injectClient??!0;let f,u,r;const a=L(t),y=e.aliasValtio??!0,_=e.transformSource??!0,w=e.applyOnBuild??process.env.LENSMCP_VITE_APPLY_ON_BUILD==="1";return{name:"@lensmcp/vite-plugin",apply:d((n,l)=>s&&(l.command==="serve"||w),"apply"),enforce:"pre",wsPort:d(()=>f,"wsPort"),config(){if(!(!s||!y))return{resolve:{alias:[{find:/^valtio$/,replacement:"@lensmcp/valtio-instrumentation"}]}}},async configureServer(n){if(!s)return;r=J(24).toString("base64url"),u=await Z({host:p,port:e.wsPort??0,bus:t.bus,sessionId:t.sessionId,token:r,...e.wsAllowedOrigins?{allowedOrigins:e.wsAllowedOrigins}:{}}),f=u.port,n.middlewares.use(ee(u)),n.ws.on("vite:error",c=>{a(U(c,t.sessionId))});const l=resolveExtraWatchRoots(process.env.LENSMCP_WATCH_DIRS,n.config?.root??process.cwd());if(l.length>0&&typeof n.watcher?.add=="function")try{n.watcher.add(l),console.log(`[lensmcp] watching ${l.length} extra source root(s) for set changes: ${l.map(c=>R(n.config?.root??process.cwd(),c)||c).join(", ")}`)}catch(c){console.warn(`[lensmcp] could not widen the watcher: ${c?.message??String(c)}`)}let o=0,h=0,E=0;const S=[],m=d((c,v)=>{o=Date.now(),h+=1,E+=1,S.push({sequence:E,kind:c,path:T(v),at:o}),S.length>x&&S.splice(0,S.length-x)},"stampFsSetEvent");n.watcher.on("add",c=>m("add",c)),n.watcher.on("addDir",c=>m("addDir",c)),n.watcher.on("unlink",c=>m("unlink",c)),n.watcher.on("unlinkDir",c=>m("unlinkDir",c)),n.middlewares.use(te,(c,v)=>{v.setHeader("content-type","application/json"),v.setHeader("cache-control","no-store"),v.end(JSON.stringify({schemaVersion:2,ready:!0,sequence:E,lastFsSetEventAt:o,fsSetEvents:h,recentEvents:S,extraWatchRoots:l,now:Date.now()}))})},resolveId(n){return n===D?N:null},load(n){if(n!==N)return null;const l=f??e.wsPort??0;return X({host:p,port:l,token:r})},async transform(n,l){if(!s||!_)return null;const o=l.split("?")[0]??l;if(o.includes("/node_modules/")||!/\.[jt]sx$/.test(o))return null;const h=await j(n,o);return h?{code:h.code,map:h.map}:null},transformIndexHtml(){if(!(!i||!s))return[{tag:"script",attrs:{type:"module",src:D},injectTo:"head-prepend"}]},handleHotUpdate(n){if(!s)return;const l=n.modules.map(h=>h.file??h.id??null).filter(h=>!!h),o=hmrReloadCause(n.modules);a({id:F(),sessionId:t.sessionId??"pending",timestamp:Date.now(),source:"vite",category:"build",severity:o.willFullReload?"warning":"info",context:{sessionId:t.sessionId??"pending"},fingerprint:I({kind:"hmr",identity:n.file}),title:o.willFullReload?`HMR ${n.file} \u2014 FULL RELOAD (no boundary above ${o.deadEnd??"it"})`:`HMR ${n.file}`,location:{file:n.file},relatedFiles:l,raw:{kind:"hmr",file:n.file,willFullReload:o.willFullReload,deadEnd:o.deadEnd,chain:o.chain,acceptedBy:o.boundaries}})},generateBundle(n,l){if(!s)return;const o=[];let h=0;for(const S of Object.values(l)){const m=S,c=m.fileName;let v;if(m.type==="chunk")v=(m.code??"").length;else{const b=m.source;v=typeof b=="string"?b.length:b instanceof Uint8Array?b.byteLength:0}h+=v;const k=[];if(m.type==="chunk"&&m.modules){for(const[b,A]of Object.entries(m.modules)){const W=A.renderedLength??A.originalLength??0;k.push({id:b,sizeBytes:W})}k.sort((b,A)=>A.sizeBytes-b.sizeBytes)}o.push({name:c,sizeBytes:v,modules:k})}const E={timestamp:Date.now(),totalBytes:h,chunks:o};a({id:F(),sessionId:t.sessionId??"pending",timestamp:Date.now(),source:"rollup",category:"build",severity:"info",context:{sessionId:t.sessionId??"pending"},fingerprint:I({kind:"bundle",identity:"report"}),title:`Bundle report: ${o.length} chunks, ${h} bytes`,raw:{kind:"bundle-report",report:E}})},async closeBundle(){u&&(await u.close(),u=void 0,r=void 0)}}}g(lensmcpVitePlugin,"lensmcpVitePlugin"),d(lensmcpVitePlugin,"lensmcpVitePlugin");const M=12;export function hmrReloadCause(e){const t=[],s=[],p=new Set;let i=null;const f=d(r=>r.file??r.id??"(anonymous module)","nameOf"),u=d((r,a)=>{if(p.has(r))return!1;if(p.add(r),r.isSelfAccepting===!0)return s.length<M&&s.push(f(r)),!1;const y=[...r.importers??[]];if(y.length===0)return i===null&&(i=f(r),t.push(...a.slice(-M),f(r))),!0;let _=!1;for(const w of y){if(w.acceptedHmrDeps?.has(r)){s.length<M&&s.push(f(w));continue}u(w,[...a,f(r)])&&(_=!0)}return _},"walk");return{willFullReload:e.map(r=>u(r,[])).some(Boolean),deadEnd:i,chain:t,boundaries:s}}g(hmrReloadCause,"hmrReloadCause"),d(hmrReloadCause,"hmrReloadCause");async function j(e,t){try{const[{transformSync:s},{lensmcpBabelPlugin:p}]=await Promise.all([import("@babel/core"),import("@lensmcp/react-instrumentation")]),i=s(e,{filename:t,babelrc:!1,configFile:!1,sourceMaps:!0,parserOpts:{plugins:["jsx","typescript"]},plugins:[[p,{rootDir:process.cwd()}]],code:!0});return i?.code?{code:i.code,map:i.map}:null}catch(s){return P(t,s),null}}g(j,"z"),d(j,"runLensmcpBabel");const C=new Set;function P(e,t){C.has(e)||(C.add(e),console.warn(`[lensmcp] could not instrument ${e}: ${t?.message??t}. The file still builds, but its renders, effects and hooks will be MISSING from react://renders/*, react://effects/recent and react://hooks/recent.`))}g(P,"Z"),d(P,"warnTransformFailed");export function resetTransformFailuresForTests(){C.clear()}g(resetTransformFailuresForTests,"resetTransformFailuresForTests"),d(resetTransformFailuresForTests,"resetTransformFailuresForTests");export function handleClientEnvelope(e,t){Q(e,L(t),t.sessionId)}g(handleClientEnvelope,"handleClientEnvelope"),d(handleClientEnvelope,"handleClientEnvelope");function H(e){const t=e?.loc?.file??e?.id;return typeof t!="string"||t.length===0?void 0:{file:t.split("?")[0]||t,...typeof e?.loc?.line=="number"?{line:e.loc.line}:{},...typeof e?.loc?.column=="number"?{column:e.loc.column}:{}}}g(H,"Q"),d(H,"errorLocation");function U(e,t){const s=e.err?.message??"Vite error",p=e.err?.stack,i=H(e.err);return{id:F(),sessionId:t??"pending",timestamp:Date.now(),source:"vite",category:"build",severity:"error",context:{sessionId:t??"pending"},fingerprint:I({kind:"vite",identity:e.type??s,...i?{file:i.file}:{}}),title:s,message:p,...i?{location:i}:{},raw:{kind:"build-diagnostic",...i?{file:i.file}:{},...e.err?.plugin?{plugin:e.err.plugin}:{}}}}g(U,"ee"),d(U,"viteErrorToEvent");function X(e){const t=`ws://${e.host}:${e.port}${e.token?`/?${z}=${encodeURIComponent(e.token)}`:""}`;return`// @lensmcp/client-runtime (inlined by @lensmcp/vite-plugin)
1
+ "use strict";var $=Object.defineProperty;var g=(e,t)=>$(e,"name",{value:t,configurable:!0});var G=Object.defineProperty,h=g((e,t)=>G(e,"name",{value:t,configurable:!0}),"u");import{randomBytes as J}from"node:crypto";import{existsSync as O,readdirSync as q,realpathSync as N,statSync as z}from"node:fs";import{delimiter as Z,dirname as L,join as _,relative as I,resolve as k,sep as A}from"node:path";import{fingerprint as C,ulid as F}from"@lensmcp/core";import{BRIDGE_TOKEN_PARAM as Q,createBridgeServer as ee,makePublisher as x,handleClientEnvelope as te}from"@lensmcp/bridge";import{CLIENT_VIRTUAL_ID as H,createRuntimeModuleGuard as ne,RESOLVED_CLIENT_VIRTUAL_ID as j}from"./runtime-guard.js";const se="/__lensmcp/watcher-health",V=256,oe=64,re=new Set(["node_modules",".git","dist","tmp","coverage","build","out","target",".nx",".lensmcp"]);export function checkoutViteCacheDir(e,t,r=process.cwd()){const p=k(r,e??".");let i;if(t)i=k(p,t);else{let m=p;for(;!O(_(m,"package.json"))&&L(m)!==m;)m=L(m);i=_(O(_(m,"package.json"))?m:p,"node_modules",".vite")}const f=i.split(A),c=f.indexOf("node_modules");if(c<1)return;const s=f.slice(0,c).join(A)||A;try{if(N(_(s,"node_modules"))===_(N(s),"node_modules"))return}catch{return}const l=f.slice(c+1);return _(s,".lensmcp","cache","vite",...l[0]===".vite"?l.slice(1):l)}g(checkoutViteCacheDir,"checkoutViteCacheDir"),h(checkoutViteCacheDir,"checkoutViteCacheDir");export function resolveExtraWatchRoots(e,t){if(!e)return[];const r=k(t),p=h((c,s)=>{const l=I(s,c);return l!==""&&!l.startsWith("..")&&!l.startsWith(A)&&!/^[A-Za-z]:/.test(l)},"inside"),i=[],f=h(c=>{i.length<oe&&!i.includes(c)&&i.push(c)},"push");for(const c of e.split(Z)){const s=c.trim();if(!s)continue;const l=k(s);let m;try{m=O(l)&&z(l).isDirectory()}catch{m=!1}if(m&&!(l===r||p(l,r))){if(p(r,l)){const S=I(l,r).split(A).filter(Boolean);let w=l;for(const n of S){let a;try{a=q(w,{withFileTypes:!0})}catch{a=[]}for(const o of a)!o.isDirectory()||o.name===n||o.name.startsWith(".")||re.has(o.name)||f(_(w,o.name));w=_(w,n)}continue}f(l)}}return i}g(resolveExtraWatchRoots,"resolveExtraWatchRoots"),h(resolveExtraWatchRoots,"resolveExtraWatchRoots");export function lensmcpVitePlugin(e={},t={}){const r=e.enabled??!0,p=e.wsHost??"127.0.0.1",i=e.injectClient??!0;let f,c,s;const l=x(t),m=e.aliasValtio??!0,S=e.transformSource??!0,w=e.applyOnBuild??process.env.LENSMCP_VITE_APPLY_ON_BUILD==="1";return{name:"@lensmcp/vite-plugin",apply:h((n,a)=>r&&(a.command==="serve"||w),"apply"),enforce:"pre",wsPort:h(()=>f,"wsPort"),config(n){if(!r)return;const a=checkoutViteCacheDir(n?.root,n?.cacheDir);a&&console.log(`[lensmcp] node_modules is shared with another checkout \u2014 Vite's dependency cache moves to ${a}`);const o=process.env.LENSMCP_FREEZE==="1";o&&console.log("[lensmcp] frozen worktree instance \u2014 Vite watches no files");const u={...a?{cacheDir:a}:{},...o?{server:{watch:null}}:{}};return m?{...u,resolve:{alias:[{find:/^valtio$/,replacement:"@lensmcp/valtio-instrumentation"}]}}:Object.keys(u).length?u:void 0},async configureServer(n){if(!r)return;s=J(24).toString("base64url"),c=await ee({host:p,port:e.wsPort??0,bus:t.bus,sessionId:t.sessionId,token:s,...e.wsAllowedOrigins?{allowedOrigins:e.wsAllowedOrigins}:{}}),f=c.port,n.middlewares.use(ne(c)),n.ws.on("vite:error",d=>{l(X(d,t.sessionId))});const a=resolveExtraWatchRoots(process.env.LENSMCP_WATCH_DIRS,n.config?.root??process.cwd());if(a.length>0&&typeof n.watcher?.add=="function")try{n.watcher.add(a),console.log(`[lensmcp] watching ${a.length} extra source root(s) for set changes: ${a.map(d=>I(n.config?.root??process.cwd(),d)||d).join(", ")}`)}catch(d){console.warn(`[lensmcp] could not widen the watcher: ${d?.message??String(d)}`)}let o=0,u=0,R=0;const E=[],v=h((d,y)=>{o=Date.now(),u+=1,R+=1,E.push({sequence:R,kind:d,path:k(y),at:o}),E.length>V&&E.splice(0,E.length-V)},"stampFsSetEvent");n.watcher.on("add",d=>v("add",d)),n.watcher.on("addDir",d=>v("addDir",d)),n.watcher.on("unlink",d=>v("unlink",d)),n.watcher.on("unlinkDir",d=>v("unlinkDir",d)),n.middlewares.use(se,(d,y)=>{y.setHeader("content-type","application/json"),y.setHeader("cache-control","no-store"),y.end(JSON.stringify({schemaVersion:2,ready:!0,sequence:R,lastFsSetEventAt:o,fsSetEvents:u,recentEvents:E,extraWatchRoots:a,now:Date.now()}))})},resolveId(n){return n===H?j:null},load(n){if(n!==j)return null;const a=f??e.wsPort??0;return Y({host:p,port:a,token:s,hmrRecovery:e.hmrRecovery!==!1})},async transform(n,a){if(!r||!S)return null;const o=a.split("?")[0]??a;if(o.includes("/node_modules/")||!/\.[jt]sx$/.test(o))return null;const u=await P(n,o);return u?{code:u.code,map:u.map}:null},transformIndexHtml(){if(!(!i||!r))return[{tag:"script",attrs:{type:"module",src:H},injectTo:"head-prepend"}]},handleHotUpdate(n){if(!r)return;const a=n.modules.map(u=>u.file??u.id??null).filter(u=>!!u),o=hmrReloadCause(n.modules);l({id:F(),sessionId:t.sessionId??"pending",timestamp:Date.now(),source:"vite",category:"build",severity:o.willFullReload?"warning":"info",context:{sessionId:t.sessionId??"pending"},fingerprint:C({kind:"hmr",identity:n.file}),title:o.willFullReload?`HMR ${n.file} \u2014 FULL RELOAD (no boundary above ${o.deadEnd??"it"})`:`HMR ${n.file}`,location:{file:n.file},relatedFiles:a,raw:{kind:"hmr",file:n.file,willFullReload:o.willFullReload,deadEnd:o.deadEnd,chain:o.chain,acceptedBy:o.boundaries}})},generateBundle(n,a){if(!r)return;const o=[];let u=0;for(const E of Object.values(a)){const v=E,d=v.fileName;let y;if(v.type==="chunk")y=(v.code??"").length;else{const b=v.source;y=typeof b=="string"?b.length:b instanceof Uint8Array?b.byteLength:0}u+=y;const M=[];if(v.type==="chunk"&&v.modules){for(const[b,T]of Object.entries(v.modules)){const K=T.renderedLength??T.originalLength??0;M.push({id:b,sizeBytes:K})}M.sort((b,T)=>T.sizeBytes-b.sizeBytes)}o.push({name:d,sizeBytes:y,modules:M})}const R={timestamp:Date.now(),totalBytes:u,chunks:o};l({id:F(),sessionId:t.sessionId??"pending",timestamp:Date.now(),source:"rollup",category:"build",severity:"info",context:{sessionId:t.sessionId??"pending"},fingerprint:C({kind:"bundle",identity:"report"}),title:`Bundle report: ${o.length} chunks, ${u} bytes`,raw:{kind:"bundle-report",report:R}})},async closeBundle(){c&&(await c.close(),c=void 0,s=void 0)}}}g(lensmcpVitePlugin,"lensmcpVitePlugin"),h(lensmcpVitePlugin,"lensmcpVitePlugin");const D=12;export function hmrReloadCause(e){const t=[],r=[],p=new Set;let i=null;const f=h(s=>s.file??s.id??"(anonymous module)","nameOf"),c=h((s,l)=>{if(p.has(s))return!1;if(p.add(s),s.isSelfAccepting===!0)return r.length<D&&r.push(f(s)),!1;const m=[...s.importers??[]];if(m.length===0)return i===null&&(i=f(s),t.push(...l.slice(-D),f(s))),!0;let S=!1;for(const w of m){if(w.acceptedHmrDeps?.has(s)){r.length<D&&r.push(f(w));continue}c(w,[...l,f(s)])&&(S=!0)}return S},"walk");return{willFullReload:e.map(s=>c(s,[])).some(Boolean),deadEnd:i,chain:t,boundaries:r}}g(hmrReloadCause,"hmrReloadCause"),h(hmrReloadCause,"hmrReloadCause");async function P(e,t){try{const[{transformSync:r},{lensmcpBabelPlugin:p}]=await Promise.all([import("@babel/core"),import("@lensmcp/react-instrumentation")]),i=r(e,{filename:t,babelrc:!1,configFile:!1,sourceMaps:!0,parserOpts:{plugins:["jsx","typescript"]},plugins:[[p,{rootDir:process.cwd()}]],code:!0});return i?.code?{code:i.code,map:i.map}:null}catch(r){return U(t,r),null}}g(P,"Q"),h(P,"runLensmcpBabel");const B=new Set;function U(e,t){B.has(e)||(B.add(e),console.warn(`[lensmcp] could not instrument ${e}: ${t?.message??t}. The file still builds, but its renders, effects and hooks will be MISSING from react://renders/*, react://effects/recent and react://hooks/recent.`))}g(U,"ee"),h(U,"warnTransformFailed");export function resetTransformFailuresForTests(){B.clear()}g(resetTransformFailuresForTests,"resetTransformFailuresForTests"),h(resetTransformFailuresForTests,"resetTransformFailuresForTests");export function handleClientEnvelope(e,t){te(e,x(t),t.sessionId)}g(handleClientEnvelope,"handleClientEnvelope"),h(handleClientEnvelope,"handleClientEnvelope");function W(e){const t=e?.loc?.file??e?.id;return typeof t!="string"||t.length===0?void 0:{file:t.split("?")[0]||t,...typeof e?.loc?.line=="number"?{line:e.loc.line}:{},...typeof e?.loc?.column=="number"?{column:e.loc.column}:{}}}g(W,"te"),h(W,"errorLocation");function X(e,t){const r=e.err?.message??"Vite error",p=e.err?.stack,i=W(e.err);return{id:F(),sessionId:t??"pending",timestamp:Date.now(),source:"vite",category:"build",severity:"error",context:{sessionId:t??"pending"},fingerprint:C({kind:"vite",identity:e.type??r,...i?{file:i.file}:{}}),title:r,message:p,...i?{location:i}:{},raw:{kind:"build-diagnostic",...i?{file:i.file}:{},...e.err?.plugin?{plugin:e.err.plugin}:{}}}}g(X,"ne"),h(X,"viteErrorToEvent");function Y(e){const t=`ws://${e.host}:${e.port}${e.token?`/?${Q}=${encodeURIComponent(e.token)}`:""}`;return`// @lensmcp/client-runtime (inlined by @lensmcp/vite-plugin)
2
2
  (() => {
3
3
  if (typeof window === 'undefined') return;
4
4
  if (window.__LENSMCP_CLIENT__) return;
@@ -208,10 +208,50 @@
208
208
  });
209
209
  hot.on('vite:invalidate', (p) => send({ kind: 'hmr', event: 'invalidate', path: p && p.path, message: p && p.message }));
210
210
  hot.on('vite:ws:disconnect', () => send({ kind: 'hmr', event: 'ws-disconnect' }));
211
- hot.on('vite:ws:connect', () => send({ kind: 'hmr', event: 'ws-connect' }));
211
+ hot.on('vite:ws:connect', () => {
212
+ try { sessionStorage.setItem(HMR_OK_KEY, '1'); } catch (e) {}
213
+ send({ kind: 'hmr', event: 'ws-connect' });
214
+ });
212
215
  }
213
216
  } catch (e) { /* strictly passive */ }
214
217
 
218
+ // HMR SOCKET WATCHDOG (issues/closed/foodguard-49). Vite opens its HMR socket once, at page load; when that
219
+ // socket and its direct fallback both close before opening, it prints "[vite] failed to connect to
220
+ // websocket" and NEVER tries again. The page still works \u2014 it just runs the code it loaded, gets no hot
221
+ // update and no reload notice (not even the next dev-server handoff's), and says so nowhere. A tab
222
+ // reloaded by a lens handoff into a still-cold Vite came back exactly like that on FoodGuard.
223
+ // So: always report it, and \u2014 in a tab whose socket WORKED earlier in this session (the handoff case),
224
+ // at most once per HMR_RECOVERY_COOLDOWN_MS \u2014 reload once, which opens a fresh socket against whichever
225
+ // Vite serves the page now. A tab whose socket never connected (HMR off, a proxy that drops WebSockets)
226
+ // is reported and left alone: reloading it would only loop.
227
+ const HMR_OK_KEY = 'lensmcp:hmrOk';
228
+ const HMR_RECOVERY_KEY = 'lensmcp:hmrRecoveryAt';
229
+ const HMR_RECOVERY = ${JSON.stringify(e.hmrRecovery!==!1)};
230
+ const HMR_RECOVERY_COOLDOWN_MS = 120000;
231
+ let hmrGaveUp = false;
232
+ function onViteGaveUp(text) {
233
+ if (hmrGaveUp) return;
234
+ hmrGaveUp = true;
235
+ let workedBefore = false;
236
+ let lastRecovery = 0;
237
+ try {
238
+ workedBefore = sessionStorage.getItem(HMR_OK_KEY) === '1';
239
+ lastRecovery = Number(sessionStorage.getItem(HMR_RECOVERY_KEY)) || 0;
240
+ } catch (e) {}
241
+ const recovering = HMR_RECOVERY && workedBefore && Date.now() - lastRecovery > HMR_RECOVERY_COOLDOWN_MS;
242
+ send({ kind: 'hmr', event: 'ws-failed', recovering, message: String(text).slice(0, 2000) });
243
+ if (recovering) {
244
+ try {
245
+ sessionStorage.setItem(HMR_RECOVERY_KEY, String(Date.now()));
246
+ // The next document's \`ready\` says why it was reloaded (the same channel a Vite full reload uses).
247
+ sessionStorage.setItem('lensmcp:reloadCause', JSON.stringify({ triggeredBy: 'lensmcp: the Vite HMR socket failed to connect', at: Date.now() }));
248
+ } catch (e) {}
249
+ }
250
+ flushBatch(); // before a reload unloads the page, or so the report is not left in the macrotask queue
251
+ if (recovering) setTimeout(() => { try { location.reload(); } catch (e) {} }, 0);
252
+ }
253
+ const VITE_GAVE_UP = '[vite] failed to connect to websocket';
254
+
215
255
  // Bridge for in-page instrumentation (@lensmcp/react-instrumentation,
216
256
  // @lensmcp/valtio-instrumentation). Their publish() calls land here and
217
257
  // ride the same WS as a 'publish' envelope, so React renders + Valtio
@@ -293,7 +333,12 @@
293
333
  const original = console[level];
294
334
  console[level] = function(...args) {
295
335
  try { send({ kind: 'console', level, args: args.map((a) => safe(a)) }); } catch {}
296
- return original.apply(this, args);
336
+ const result = original.apply(this, args);
337
+ // Vite's only signal that it gave up on the HMR socket is this line (see the HMR watchdog above).
338
+ if (level === 'error' && typeof args[0] === 'string' && args[0].indexOf(VITE_GAVE_UP) === 0) {
339
+ try { onViteGaveUp(args[0]); } catch (e) {}
340
+ }
341
+ return result;
297
342
  };
298
343
  }
299
344
 
@@ -370,4 +415,4 @@
370
415
  try { return safeWalk(v, 0, { left: SAFE_MAX_BYTES }); } catch (e) { return String(v); }
371
416
  }
372
417
  })();
373
- `}g(X,"te"),d(X,"buildInlineClientRuntime");
418
+ `}g(Y,"se"),h(Y,"buildInlineClientRuntime");
package/lib/types.d.ts CHANGED
@@ -28,6 +28,15 @@ export interface LensmcpViteOptions {
28
28
  aliasValtio?: boolean;
29
29
  /** Run the LensMCP source transform (root-wrap + hooks + JSX ids). Default true. */
30
30
  transformSource?: boolean;
31
+ /**
32
+ * When Vite gives up opening its HMR socket at page load ("[vite] failed to connect to websocket"), the
33
+ * page still works but never gets another hot update or reload notice — it silently runs the code it
34
+ * loaded. The injected runtime always reports that (`hmr` `ws-failed`). With this on (the default) it
35
+ * also RELOADS the page once, but only in a tab whose HMR socket worked earlier in its session (the
36
+ * case a dev-server handoff leaves behind — issues/closed/foodguard-49), and at most once per two
37
+ * minutes, so a setup where the socket never connects is reported, never looped. `false` = report only.
38
+ */
39
+ hmrRecovery?: boolean;
31
40
  /**
32
41
  * Also attach during `vite build`. Default **false** — everything this plugin does (the WS
33
42
  * bridge, the injected client runtime, `data-agent-source` on every element, source paths in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/vite-plugin",
3
- "version": "1.24.1",
3
+ "version": "1.24.2",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -15,11 +15,11 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@babel/core": "^7.0.0",
18
- "@lensmcp/bridge": "1.24.1",
19
- "@lensmcp/core": "1.24.1",
20
- "@lensmcp/protocol-types": "1.24.1",
21
- "@lensmcp/react-instrumentation": "1.24.1",
22
- "@lensmcp/valtio-instrumentation": "1.24.1",
18
+ "@lensmcp/bridge": "1.24.2",
19
+ "@lensmcp/core": "1.24.2",
20
+ "@lensmcp/protocol-types": "1.24.2",
21
+ "@lensmcp/react-instrumentation": "1.24.2",
22
+ "@lensmcp/valtio-instrumentation": "1.24.2",
23
23
  "tslib": "^2.3.0"
24
24
  },
25
25
  "peerDependencies": {