@lensmcp/bridge 1.18.6 → 1.18.7
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/lib/env-sink.js +3 -3
- package/lib/event-mappers.js +3 -3
- package/lib/server.d.ts +2 -0
- package/lib/server.js +1 -1
- package/lib/types.d.ts +18 -0
- package/main.js +1 -1
- package/package.json +3 -3
package/lib/env-sink.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var y=Object.defineProperty;var f=(n
|
|
2
|
-
`;
|
|
3
|
-
`);
|
|
1
|
+
"use strict";var y=Object.defineProperty;var f=(e,n)=>y(e,"name",{value:n,configurable:!0});var b=Object.defineProperty,l=f((e,n)=>b(e,"name",{value:n,configurable:!0}),"f"),_=Object.defineProperty,c=l((e,n)=>_(e,"name",{value:n,configurable:!0}),"i");import{connect as M}from"node:net";import{createSocket as C}from"node:dgram";import{appendFileSync as g,renameSync as d,statSync as A,unlinkSync as L}from"node:fs";const P=(()=>{const e=Number(process.env.LENSMCP_EVENT_MAX_BYTES);return Number.isFinite(e)&&e>0?e:50*1024*1024})(),h=(()=>{const e=Number(process.env.LENSMCP_EVENT_MAX_FILES);return Number.isFinite(e)&&e>=1&&e<=20?Math.floor(e):5})(),w=1024*1024;let p=0;function E(e,n){if(p+=n,!(p<w)){p=0;try{if(A(e).size<=P)return}catch{return}try{L(`${e}.${h}`)}catch{}for(let t=h-1;t>=1;t--)try{d(`${e}.${t}`,`${e}.${t+1}`)}catch{}try{d(e,`${e}.1`)}catch{}}}f(E,"d"),l(E,"C"),c(E,"rotateIfLarge");const S=(()=>{const e=process.env.LENSMCP_EVENT_MAX_EPS;if(e==="off")return 1/0;const n=Number(e);return e!==void 0&&e!==""&&Number.isFinite(n)?n<=0?1/0:n:5})(),m=Math.max(30,S===1/0?0:S*4),a=new Map;function v(e){if(S===1/0)return 0;const n=Date.now();let t=a.get(e);if(!t){if(a.size>=2048){for(const[r,s]of a)n-s.lastSeenAt>6e4&&a.delete(r);a.size>=2048&&a.clear()}t={tokens:m,lastRefillAt:n,suppressed:0,lastSeenAt:n},a.set(e,t)}if(t.lastSeenAt=n,t.tokens=Math.min(m,t.tokens+(n-t.lastRefillAt)/1e3*S),t.lastRefillAt=n,t.tokens>=1){t.tokens-=1;const r=t.suppressed;return t.suppressed=0,r}return t.suppressed+=1,-1}f(v,"v"),l(v,"T"),c(v,"floodAdmit");let o;export function resolveEnvSink(){if(o!==void 0)return o;const e=process.env.LENSMCP_EVENT_FILE;if(e)return o=c(r=>{try{const s=v(r.fingerprint);if(s<0)return;const u=JSON.stringify(s>0?{...r,raw:{...r.raw??{},floodSuppressed:s}}:r)+`
|
|
2
|
+
`;E(e,u.length),g(e,u)}catch{}},"cachedEnvSink"),o;const n=process.env.LENSMCP_UDS;if(n)return o=udsEnvSink(n),o;const t=process.env.LENSMCP_IPC_SOCKET;if(t){const[r,s]=t.split(":"),u=Number(s);if(r&&Number.isFinite(u)){const i=C("udp4");return i.unref(),o=c(N=>{try{const k=Buffer.from(JSON.stringify(N));i.send(k,u,r,()=>{})}catch{}},"cachedEnvSink"),o}}return o=null,null}f(resolveEnvSink,"resolveEnvSink"),l(resolveEnvSink,"resolveEnvSink"),c(resolveEnvSink,"resolveEnvSink");export function resetEnvSinkCache(){o=void 0}f(resetEnvSinkCache,"resetEnvSinkCache"),l(resetEnvSinkCache,"resetEnvSinkCache"),c(resetEnvSinkCache,"resetEnvSinkCache");export function udsEnvSink(e){const n=[];let t=null,r=!1;const s=c(()=>{if(!(!t||!t.writable))for(;n.length>0;)t.write(n.shift())},"flush"),u=c(()=>{if(r||t)return;r=!0;const i=M(e);i.on("connect",()=>{r=!1,t=i,s()}),i.on("drain",s),i.on("error",()=>{r=!1,t=null}),i.on("close",()=>{t=null}),i.unref()},"open");return u(),i=>{for(n.push(JSON.stringify(i)+`
|
|
3
|
+
`);n.length>1e4;)n.shift();if(t&&t.writable){s();return}if(t){try{t.destroy()}catch{}t=null}u()}}f(udsEnvSink,"udsEnvSink"),l(udsEnvSink,"udsEnvSink"),c(udsEnvSink,"udsEnvSink");
|
package/lib/event-mappers.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var h=Object.defineProperty;var c=(e
|
|
2
|
-
`)[0]??
|
|
3
|
-
`).slice(0,2);for(const
|
|
1
|
+
"use strict";var h=Object.defineProperty;var c=(t,e)=>h(t,"name",{value:e,configurable:!0});var x=Object.defineProperty,l=c((t,e)=>x(t,"name",{value:e,configurable:!0}),"c"),U=Object.defineProperty,d=l((t,e)=>U(t,"name",{value:e,configurable:!0}),"s");import{fingerprint as o,ulid as a}from"@lensmcp/core";import{redactDeep as f,redactText as I,redactUrl as m,safeText as u,safeTitle as g}from"./redact.js";const b="pending";export function envelopeToEvent(t,e=b){const s=t.tabId?{tabId:t.tabId}:{};switch(t.kind){case"error":{const r=!t.stack&&!t.source&&(!t.message||t.message==="Script error."),i=t.name&&t.name!=="Error"?`${t.name}: `:"",n=r?"Uncaught cross-origin script error (no detail available \u2014 add crossorigin to the script tag)":t.message?`${i}${t.message}`:`Uncaught ${t.name??"error"}`;return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:e,...s},fingerprint:o({kind:"runtime",identity:`error:${v(t.stack)}`,file:t.source,detail:t.message??t.name}),title:g(n),message:u(t.stack??t.message),location:t.source?{file:m(t.source),line:t.lineno,column:t.colno}:void 0,raw:{...t,...t.message?{message:I(t.message)}:{},...t.stack?{stack:u(t.stack)}:{},...t.source?{source:m(t.source)}:{}}}}case"resource-error":{const r=t.url?m(t.url):void 0;return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:e,...s},fingerprint:o({kind:"runtime",identity:`resource-error:${t.tagName}:${p(t.url??"")}`}),title:g(`Failed to load <${t.tagName}>${r?` ${r}`:""}`),...r?{relatedUrls:[r]}:{},raw:{...t,...r?{url:r}:{}}}}case"unhandledrejection":{const r=t.name&&t.name!=="Error"?`${t.name}: `:"",i=t.reason.split(`
|
|
2
|
+
`)[0]??t.reason;return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:e,...s},fingerprint:o({kind:"runtime",identity:`unhandledrejection:${t.reason.slice(0,80)}`}),title:g(`Unhandled promise rejection: ${r}${i}`),message:u(t.stack??t.reason),raw:{...t,reason:I(t.reason),...t.stack?{stack:u(t.stack)}:{}}}}case"console":{const r=t.level==="error"?"error":t.level==="warn"?"warning":"info",i=f(t.args),n=i.map($).join(" ");return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:r,context:{sessionId:e,...s},fingerprint:o({kind:"runtime",identity:`console:${t.level}:${n.slice(0,80)}`}),title:`console.${t.level}`,message:u(n),raw:{...t,args:i}}}case"fetch":{const r=t.error!==void 0||t.status!==void 0&&t.status>=400,i=m(t.url),n=t.status!==void 0?` \u2192 ${t.status}${t.statusText?` ${t.statusText}`:""}`:"";return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"network",severity:r?"error":"info",context:{sessionId:e,...s},fingerprint:o({kind:"network",identity:`${t.method}:${p(t.url)}:${t.status??"err"}`}),title:g(`${t.method} ${i}${n}`),message:u(t.error),relatedUrls:[i],raw:{...t,url:i}}}case"route":return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:e,url:t.url,...s},fingerprint:o({kind:"runtime",identity:`route:${t.url}`}),title:`Route \u2192 ${t.url}`,raw:t};case"ready":return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:e,url:t.url,...s},fingerprint:o({kind:"runtime",identity:"client-ready"}),title:"Client runtime ready",message:t.userAgent,raw:t};case"gone":return{id:a(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:e,...s},fingerprint:o({kind:"runtime",identity:"client-gone"}),title:"Client runtime gone",raw:t};case"publish":{const r=y(t.category,t.raw),i=r&&r.status!==void 0&&r.status>=400?"error":t.severity??"info",n=d(w=>r&&r.url!==r.redactedUrl?w.split(r.url).join(r.redactedUrl):w,"scrub"),k=n(t.title);return{id:a(),sessionId:e,timestamp:t.at,source:t.source,category:t.category,severity:i,context:{sessionId:e,...s,...t.context??{}},fingerprint:t.fingerprint?n(t.fingerprint):o({kind:"publish",identity:k}),title:g(k),message:u(t.message),...r?{relatedUrls:[r.redactedUrl]}:{},raw:r?{...f(t.raw),url:r.redactedUrl}:f(t.raw)}}}}c(envelopeToEvent,"envelopeToEvent"),l(envelopeToEvent,"envelopeToEvent"),d(envelopeToEvent,"envelopeToEvent");function y(t,e){if(t!=="network"||!e||typeof e!="object")return;const s=e;if(!(typeof s.url!="string"||s.url.length===0))return{url:s.url,redactedUrl:m(s.url),...typeof s.status=="number"?{status:s.status}:{}}}c(y,"k"),l(y,"w"),d(y,"networkDetails");function v(t){if(!t)return"unknown";const e=t.split(`
|
|
3
|
+
`).slice(0,2);for(const s of e){const r=s.match(/at\s+(\S+)/);if(r&&r[1])return r[1]}return"unknown"}c(v,"v"),l(v,"h"),d(v,"normaliseStackTopFrame");function p(t){try{return`${new URL(t,"http://x").pathname}`}catch{return t}}c(p,"p"),l(p,"y"),d(p,"normaliseUrl");function $(t){if(typeof t=="string")return t;try{return JSON.stringify(t)}catch{return String(t)}}c($,"w"),l($,"x"),d($,"stringify");
|
package/lib/server.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export declare function makePublisher(deps: BridgeDeps): (event: BaseEvent) => v
|
|
|
41
41
|
* exercised over a real WebSocket without booting any bundler.
|
|
42
42
|
*/
|
|
43
43
|
export declare function handleClientEnvelope(raw: string, publish: (event: BaseEvent) => void, sessionId?: string): void;
|
|
44
|
+
/** The tab a raw envelope belongs to, or undefined for a malformed/untagged one. */
|
|
45
|
+
export declare function readTabId(raw: string): string | undefined;
|
|
44
46
|
/**
|
|
45
47
|
* Stand up the browser-event bridge: a WebSocket server the injected
|
|
46
48
|
* `@lensmcp/client-runtime` connects to, forwarding every envelope onto
|
package/lib/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var v=Object.defineProperty;var i=(e,r)=>v(e,"name",{value:r,configurable:!0});var b=Object.defineProperty,c=i((e,r)=>b(e,"name",{value:r,configurable:!0}),"i"),m=Object.defineProperty,t=c((e,r)=>m(e,"name",{value:r,configurable:!0}),"o");import{createServer as g}from"node:net";import{WebSocketServer as S}from"ws";import{DEFAULT_WS_HOST as h,DEFAULT_WS_PORT as y}from"./constants.js";import{resolveEnvSink as P}from"./env-sink.js";import{envelopeToEvent as k}from"./event-mappers.js";export function makePublisher(e){let r;return o=>{if(e.bus){e.bus.publish(o);return}if(e.sink){e.sink(o);return}if(r===void 0&&(r=P()),r){r(o);return}(o.severity==="error"||o.severity==="fatal")&&console.debug(`[lensmcp-bridge] ${o.severity}: ${o.title}`)}}i(makePublisher,"makePublisher"),c(makePublisher,"makePublisher"),t(makePublisher,"makePublisher");export function handleClientEnvelope(e,r,o){let n;try{n=JSON.parse(e)}catch{return}!n||typeof n!="object"||!("kind"in n)||r(k(n,o))}i(handleClientEnvelope,"handleClientEnvelope"),c(handleClientEnvelope,"handleClientEnvelope"),t(handleClientEnvelope,"handleClientEnvelope");export function readTabId(e){try{const r=JSON.parse(e);return typeof r?.tabId=="string"&&r.tabId?r.tabId:void 0}catch{return}}i(readTabId,"readTabId"),c(readTabId,"readTabId"),t(readTabId,"readTabId");export async function createBridgeServer(e={}){const r=e.host??h,o=e.port??y,n=o===0?await findFreePort():o,f=makePublisher(e),s=new S({host:r,port:n});return s.on("connection",a=>{let l;a.on("message",u=>{const d=u.toString();l??=readTabId(d),handleClientEnvelope(d,f,e.sessionId)}),a.on("close",()=>{l&&handleClientEnvelope(JSON.stringify({kind:"gone",at:Date.now(),tabId:l}),f,e.sessionId)})}),await new Promise((a,l)=>{const u=t(p=>{s.off("listening",d),l(p)},"onError"),d=t(()=>{s.off("error",u),a()},"onListening");s.once("error",u),s.once("listening",d)}),{port:n,host:r,close:t(()=>new Promise(a=>{s.close(()=>a())}),"close")}}i(createBridgeServer,"createBridgeServer"),c(createBridgeServer,"createBridgeServer"),t(createBridgeServer,"createBridgeServer");export function findFreePort(){return new Promise((e,r)=>{const o=g();o.unref(),o.on("error",r),o.listen(0,()=>{const n=o.address();if(!n){o.close(),r(new Error("Could not allocate free port"));return}const f=n.port;o.close(()=>e(f))})})}i(findFreePort,"findFreePort"),c(findFreePort,"findFreePort"),t(findFreePort,"findFreePort");
|
package/lib/types.d.ts
CHANGED
|
@@ -79,6 +79,24 @@ export type ClientEnvelope = (
|
|
|
79
79
|
at: number;
|
|
80
80
|
userAgent: string;
|
|
81
81
|
url: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The tab's transport closed — its document is GONE.
|
|
85
|
+
*
|
|
86
|
+
* Synthesised by the BRIDGE on socket close, never sent by the page: the
|
|
87
|
+
* failure this exists for is exactly the one a page cannot report. `ready`
|
|
88
|
+
* comes from the injected client runtime, so a tab whose entry script failed
|
|
89
|
+
* to load can never announce a document boundary — and under `ready`-only
|
|
90
|
+
* retraction its error was immortal by construction.
|
|
91
|
+
*
|
|
92
|
+
* Measured on foodguard (2026-08-01): 38 retained browser errors across 26
|
|
93
|
+
* DISTINCT tabs, none with more than two — 26 dead one-shot documents, 15 of
|
|
94
|
+
* them the client script itself failing to load. `agent://current-status`
|
|
95
|
+
* reported `failing` on all of it while the app served cleanly.
|
|
96
|
+
*/
|
|
97
|
+
| {
|
|
98
|
+
kind: 'gone';
|
|
99
|
+
at: number;
|
|
82
100
|
} | {
|
|
83
101
|
kind: 'publish';
|
|
84
102
|
at: number;
|
package/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var d=Object.defineProperty;var n=(s,e)=>d(s,"name",{value:e,configurable:!0});var v=Object.defineProperty,a=n((s,e)=>v(s,"name",{value:e,configurable:!0}),"n"),g=Object.defineProperty,l=a((s,e)=>g(s,"name",{value:e,configurable:!0}),"n");import{DEFAULT_WS_HOST as u,DEFAULT_WS_PORT as _,ENV_SESSION_ID as b,ENV_WS_HOST as T,ENV_WS_PORT as p}from"./lib/constants.js";import{createBridgeServer as f}from"./lib/server.js";async function t(){const s=process.env[T]??u,e=process.env[p],o=e!==void 0&&e!==""?Number(e):_;(!Number.isFinite(o)||o<0)&&(console.error(`[lensmcp-bridge] invalid ${p}=${e}`),process.exit(2));const m=process.env[b];let r;try{r=await f({host:s,port:o,sessionId:m})}catch(S){console.error(`[lensmcp-bridge] failed to listen on ws://${s}:${o} \u2014 ${S.message}`),process.exit(1)}console.log(`[lensmcp-bridge] listening on ws://${r.host}:${r.port}`);let c=!1;const i=l(()=>{c||(c=!0,r.close().then(()=>process.exit(0)))},"shutdown");process.on("SIGINT",i),process.on("SIGTERM",i)}n(t,"l"),a(t,"g"),l(t,"main"),t();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/bridge",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@lensmcp/core": "1.18.
|
|
23
|
-
"@lensmcp/protocol-types": "1.18.
|
|
22
|
+
"@lensmcp/core": "1.18.7",
|
|
23
|
+
"@lensmcp/protocol-types": "1.18.7",
|
|
24
24
|
"tslib": "^2.3.0",
|
|
25
25
|
"ws": "^8.21.0"
|
|
26
26
|
},
|