@lensmcp/bridge 1.25.17 → 1.25.18
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/index.d.ts +1 -0
- package/index.js +1 -1
- package/lib/replay-ingest.d.ts +28 -0
- package/lib/replay-ingest.js +2 -0
- package/lib/server.d.ts +6 -0
- package/lib/server.js +1 -1
- package/package.json +3 -3
package/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createBridgeServer, makePublisher, handleClientEnvelope, findFreePort, CONNECTION_THROTTLE_NOTICE, type BridgeServer, type BridgeServerOptions, type BridgeDeps, type BridgeRefusal, type ConnectionRateLimit, type EnvelopeIngress, } from './lib/server.js';
|
|
2
2
|
export { createOriginPolicy, parseAllowedOrigins, isLoopbackHostname, MAX_LEARNED_ORIGINS, type OriginPolicy, } from './lib/origin-policy.js';
|
|
3
3
|
export { conformsToEnvelopeContract } from './lib/envelope-contract.js';
|
|
4
|
+
export { createReplayIngest, replayStoreFromEnv, REPLAY_INGEST_LIMITS, REPLAY_WIRE_VERSION, type ReplayStore, type ReplayIngestLimits, type ReplayIngestOptions, } from './lib/replay-ingest.js';
|
|
4
5
|
export { envelopeToEvent } from './lib/event-mappers.js';
|
|
5
6
|
export { redactUrl, redactText, redactDeep, isSecretParam, clip, safeText, safeTitle, REDACTED, MAX_TITLE_CHARS, MAX_MESSAGE_CHARS, MAX_URL_CHARS, } from './lib/redact.js';
|
|
6
7
|
export { resolveEnvSink, resetEnvSinkCache, closeEnvSink, udsEnvSink } from './lib/env-sink.js';
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export{createBridgeServer,makePublisher,handleClientEnvelope,findFreePort,CONNECTION_THROTTLE_NOTICE}from"./lib/server.js";export{createOriginPolicy,parseAllowedOrigins,isLoopbackHostname,MAX_LEARNED_ORIGINS}from"./lib/origin-policy.js";export{conformsToEnvelopeContract}from"./lib/envelope-contract.js";export{envelopeToEvent}from"./lib/event-mappers.js";export{redactUrl,redactText,redactDeep,isSecretParam,clip,safeText,safeTitle,REDACTED,MAX_TITLE_CHARS,MAX_MESSAGE_CHARS,MAX_URL_CHARS}from"./lib/redact.js";export{resolveEnvSink,resetEnvSinkCache,closeEnvSink,udsEnvSink}from"./lib/env-sink.js";export{DEFAULT_WS_HOST,DEFAULT_WS_PORT,ENV_WS_HOST,ENV_WS_PORT,ENV_SESSION_ID,ENV_BRIDGE_ALLOWED_ORIGINS,ENV_BRIDGE_TOKEN,BRIDGE_TOKEN_PARAM,MAX_BRIDGE_CONNECTIONS,CONNECTION_BURST,CONNECTION_PER_SEC,SHUTDOWN_GRACE_MS,ENV_SINK_CLOSE_GRACE_MS}from"./lib/constants.js";
|
|
1
|
+
"use strict";export{createBridgeServer,makePublisher,handleClientEnvelope,findFreePort,CONNECTION_THROTTLE_NOTICE}from"./lib/server.js";export{createOriginPolicy,parseAllowedOrigins,isLoopbackHostname,MAX_LEARNED_ORIGINS}from"./lib/origin-policy.js";export{conformsToEnvelopeContract}from"./lib/envelope-contract.js";export{createReplayIngest,replayStoreFromEnv,REPLAY_INGEST_LIMITS,REPLAY_WIRE_VERSION}from"./lib/replay-ingest.js";export{envelopeToEvent}from"./lib/event-mappers.js";export{redactUrl,redactText,redactDeep,isSecretParam,clip,safeText,safeTitle,REDACTED,MAX_TITLE_CHARS,MAX_MESSAGE_CHARS,MAX_URL_CHARS}from"./lib/redact.js";export{resolveEnvSink,resetEnvSinkCache,closeEnvSink,udsEnvSink}from"./lib/env-sink.js";export{DEFAULT_WS_HOST,DEFAULT_WS_PORT,ENV_WS_HOST,ENV_WS_PORT,ENV_SESSION_ID,ENV_BRIDGE_ALLOWED_ORIGINS,ENV_BRIDGE_TOKEN,BRIDGE_TOKEN_PARAM,MAX_BRIDGE_CONNECTIONS,CONNECTION_BURST,CONNECTION_PER_SEC,SHUTDOWN_GRACE_MS,ENV_SINK_CLOSE_GRACE_MS}from"./lib/constants.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { BaseEvent } from '@lensmcp/protocol-types';
|
|
3
|
+
import { replayStoreFromEnv, type ReplayStore } from '@lensmcp/core/replay-store';
|
|
4
|
+
export { replayStoreFromEnv, type ReplayStore };
|
|
5
|
+
/** Lock-step with `@lensmcp/client-runtime` (replay-format: `REPLAY_WIRE_VERSION`). */
|
|
6
|
+
export declare const REPLAY_WIRE_VERSION = 1;
|
|
7
|
+
export interface ReplayIngestLimits {
|
|
8
|
+
/** One chunk or blob body. */
|
|
9
|
+
bodyBytes: number;
|
|
10
|
+
/** One page load, per minute. */
|
|
11
|
+
loadBytesPerMinute: number;
|
|
12
|
+
/** Everything this bridge takes, per minute. */
|
|
13
|
+
bridgeBytesPerMinute: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const REPLAY_INGEST_LIMITS: ReplayIngestLimits;
|
|
16
|
+
export interface ReplayIngestOptions extends ReplayStore {
|
|
17
|
+
/** The bridge's origin policy. */
|
|
18
|
+
admits(origin: string | undefined): boolean;
|
|
19
|
+
/** The bridge's token check on the request URL (true when no token is configured). */
|
|
20
|
+
tokenOk(url: string | undefined): boolean;
|
|
21
|
+
/** The few lifecycle events the bus carries (`replay-started`) — never a chunk. */
|
|
22
|
+
publish?: (event: BaseEvent) => void;
|
|
23
|
+
sessionId?: string;
|
|
24
|
+
now?: () => number;
|
|
25
|
+
limits?: Partial<ReplayIngestLimits>;
|
|
26
|
+
}
|
|
27
|
+
/** Builds the bridge's replay routes; the handler returns false for any other path (the 426 stays theirs). */
|
|
28
|
+
export declare function createReplayIngest(o: ReplayIngestOptions): (req: IncomingMessage, res: ServerResponse) => boolean;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var U=Object.defineProperty;var z=(e,o)=>U(e,"name",{value:o,configurable:!0});var V=Object.defineProperty,O=z((e,o)=>V(e,"name",{value:o,configurable:!0}),"B"),W=Object.defineProperty,u=O((e,o)=>W(e,"name",{value:o,configurable:!0}),"d");import{createHash as C}from"node:crypto";import{appendFile as K,mkdir as _,readFile as Q,rename as D,rm as X,stat as ee,writeFile as J}from"node:fs/promises";import{promisify as te}from"node:util";import{gunzip as re}from"node:zlib";import{dirname as ae,join as y}from"node:path";import{replayStoreFromEnv as oe}from"@lensmcp/core/replay-store";import{ulid as se}from"@lensmcp/core";const ne=te(re);export{oe as replayStoreFromEnv};export const REPLAY_WIRE_VERSION=1,REPLAY_INGEST_LIMITS={bodyBytes:32*1024*1024,loadBytesPerMinute:64*1024*1024,bridgeBytesPerMinute:256*1024*1024};const j=/^[0-9a-f]{64}$/,ie=/^load-[a-z0-9]{1,16}-[a-z0-9]{1,16}$/,ce=/^[A-Za-z0-9_-]{1,64}$/,de=/^\d{1,15}$/,le=/^[A-Za-z0-9@/._-]{1,100}$/,ue=/^[frv]:[^,\s]{1,200}$/,be=256;function A(e){const o=e.get("v");if(o!==String(REPLAY_WIRE_VERSION))return`unsupported replay wire version ${o??"(none)"}`;const i=e.get("load")??"",I=e.get("tab")??"";if(!ie.test(i))return"bad load";if(!ce.test(I))return"bad tab";for(const p of["seq","t0","t1","n"])if(!de.test(e.get(p)??""))return`bad ${p}`;const m=e.get("enc");if(m!=="gzip"&&m!=="identity")return"bad enc";const b=e.get("full");if(b!=="0"&&b!=="1")return"bad full";const P=e.get("app")??void 0;if(P!==void 0&&!le.test(P))return"bad app";const w=e.get("base")??void 0;if(w!==void 0&&!j.test(w))return"bad base";const v=(e.get("a")??"").split(",").filter(Boolean);if(v.length>1e3||v.some(p=>!ue.test(p)))return"bad anchors";const S=(e.get("b")??"").split(",").filter(Boolean);return S.length>be||S.some(p=>!j.test(p))?"bad blobs":{load:i,tab:I,seq:Number(e.get("seq")),t0:Number(e.get("t0")),t1:Number(e.get("t1")),n:Number(e.get("n")),full:b==="1",enc:m,...P?{app:P}:{},...w?{base:w}:{},anchors:v,more:e.get("am")==="1",blobs:S}}z(A,"D"),O(A,"it"),u(A,"chunkParams");class Y{static{z(this,"L")}static{O(this,"L")}static{u(this,"MinuteBudget")}constructor(o){this.max=o,this.start=0,this.used=0}take(o,i){return i-this.start>=6e4&&(this.start=i,this.used=0),this.used+o>this.max?!1:(this.used+=o,!0)}}export function createReplayIngest(e){const o={...REPLAY_INGEST_LIMITS,...e.limits},i=e.now??Date.now,I=new Y(o.bridgeBytesPerMinute),m=new Map,b=new Map,P=y(e.root,"blobs"),w=u(t=>y(e.root,e.checkout,t),"loadDir"),v=u((t,s)=>y(P,t.slice(0,2),`${t}${s==="gzip"?".gz":".json"}`),"blobFile"),S=u(async t=>{try{return await ee(t),!0}catch{return!1}},"exists"),p=u(async t=>await S(v(t,"gzip"))||await S(v(t,"identity")),"hasBlob"),R=u((t,s,c)=>new Promise((h,g)=>{const d=[];let a=0,r=!1;t.on("data",n=>{a+=n.length,a>o.bodyBytes?(r=!0,d.length=0):r||d.push(n)}),t.on("error",g),t.on("end",()=>{if(r)return h("too-big");const n=Buffer.concat(d);(c?c(n):Promise.resolve(!0)).then($=>{if(!$)return h("bad-hash");const k=`${s}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;J(k,n,{mode:384}).then(()=>D(k,s)).then(()=>h(a),async B=>{await X(k,{force:!0}).catch(()=>{}),g(B)})},g)})}),"receive"),Z=u(async(t,s)=>{const c=b.get(t.load);if(c)return c;try{const a=JSON.parse(await Q(y(w(t.load),"meta.json"),"utf8"));return b.set(t.load,a),a}catch{}const h=s.headers.origin,g=s.headers["user-agent"],d={load:t.load,tab:t.tab,...t.app?{app:t.app}:{},checkout:e.checkout,...typeof h=="string"?{origin:h}:{},...typeof g=="string"?{userAgent:g.slice(0,300)}:{},wire:REPLAY_WIRE_VERSION,started:t.t0,lastAt:t.t1,chunks:0,bytes:0,blobs:[]};return b.size>=256&&b.delete(b.keys().next().value),b.set(t.load,d),e.publish?.(M(d,e.sessionId,i())),d},"loadMeta");return(t,s)=>{let c;try{c=new URL(t.url??"/","http://bridge.invalid")}catch{return!1}if(c.pathname!=="/replay"&&c.pathname!=="/replay/blob")return!1;const h=typeof t.headers.origin=="string"?t.headers.origin:void 0,g=e.admits(h),d=g&&h?{"access-control-allow-origin":h,vary:"origin"}:{},a=u((r,n)=>{const $=n===void 0?"":JSON.stringify(n);s.headersSent||s.writeHead(r,{...d,"content-type":"application/json","cache-control":"no-store","content-length":Buffer.byteLength($)}),s.end($),t.readableEnded||t.resume()},"answer");return t.method==="OPTIONS"?g?(s.writeHead(204,{...d,"access-control-allow-methods":"POST, HEAD","access-control-allow-headers":"content-type","access-control-max-age":"7200"}),s.end(),!0):(a(403,{error:"origin not allowed"}),!0):g?e.tokenOk(t.url)?((async()=>{try{if(c.pathname==="/replay/blob"){const l=c.searchParams.get("sha")??"";if(!j.test(l))return a(400,{error:"bad sha"});if(t.method==="HEAD"){const E=await p(l);return s.headersSent||s.writeHead(E?200:404,d),void s.end()}if(t.method!=="POST")return a(405,{error:"POST or HEAD"});if(await p(l))return a(200,{stored:!1,reason:"held"});const H=c.searchParams.get("enc")==="identity"?"identity":"gzip",q=Number(t.headers["content-length"]??0);if(q>o.bodyBytes||!I.take(q,i()))return a(413,{error:"over the replay byte budget"});const F=v(l,H);await _(ae(F),{recursive:!0,mode:448});const L=await R(t,F,async E=>{try{return C("sha256").update(H==="gzip"?await ne(E):E).digest("hex")===l}catch{return!1}});return L==="bad-hash"?a(400,{error:"the blob does not hash to its sha"}):L==="too-big"?a(413,{error:"blob over the body limit"}):a(201,{stored:!0})}if(t.method!=="POST")return a(405,{error:"POST"});const r=A(c.searchParams);if(typeof r=="string")return a(400,{error:r});const n=w(r.load),$=y(n,`${String(r.seq).padStart(6,"0")}${r.enc==="gzip"?".json.gz":".json"}`);if(await S($))return a(200,{stored:!1,reason:"duplicate"});const k=[];for(const l of r.blobs)await p(l)||k.push(l);if(k.length)return a(409,{missing:k});const B=Number(t.headers["content-length"]??0);let x=m.get(r.load);if(x||(m.size>=256&&m.delete(m.keys().next().value),x=new Y(o.loadBytesPerMinute),m.set(r.load,x)),B>o.bodyBytes||!x.take(B,i())||!I.take(B,i()))return a(413,{error:"over the replay byte budget"});await _(n,{recursive:!0,mode:448});const f=await Z(r,t),N=await R(t,$);if(N==="too-big"||N==="bad-hash")return a(413,{error:"chunk over the body limit"});const G={seq:r.seq,t0:r.t0,t1:r.t1,n:r.n,full:r.full,enc:r.enc,bytes:N,...r.base?{base:r.base}:{},...r.anchors.length?{anchors:r.anchors}:{},...r.more?{anchorsInBody:!0}:{},...r.blobs.length?{blobs:r.blobs}:{},at:i()};await K(y(n,"index.jsonl"),`${JSON.stringify(G)}
|
|
2
|
+
`,{mode:384}),f.chunks+=1,f.bytes+=N,f.lastAt=Math.max(f.lastAt,r.t1),r.base&&(f.base=r.base);for(const l of r.blobs)f.blobs.includes(l)||f.blobs.push(l);const T=y(n,`meta.json.${process.pid}.tmp`);return await J(T,JSON.stringify(f),{mode:384}),await D(T,y(n,"meta.json")),a(201,{stored:!0})}catch{return a(500,{error:"replay store unavailable"})}})(),!0):(a(401,{error:"missing or wrong bridge token"}),!0):(a(403,{error:"origin not allowed"}),!0)}}z(createReplayIngest,"createReplayIngest"),O(createReplayIngest,"createReplayIngest"),u(createReplayIngest,"createReplayIngest");function M(e,o,i){return{id:se(),sessionId:o??"pending",timestamp:i,source:"client-runtime",category:"runtime",severity:"info",title:`session replay recording${e.app?` ${e.app}`:""} (${e.tab})`,fingerprint:`replay:replay-started:${e.load}`,context:{sessionId:o??"pending",tabId:e.tab},raw:{kind:"replay-started",loadId:e.load,tabId:e.tab,checkout:e.checkout,...e.app?{app:e.app}:{},...e.origin?{origin:e.origin}:{}}}}z(M,"J"),O(M,"ct"),u(M,"startedEvent");
|
package/lib/server.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type BaseEvent } from '@lensmcp/protocol-types';
|
|
2
2
|
import type { EventBus } from '@lensmcp/core';
|
|
3
|
+
import { type ReplayStore } from './replay-ingest.js';
|
|
3
4
|
import type { EnvSink } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* Where bridge events go. Both are optional:
|
|
@@ -40,6 +41,11 @@ export interface BridgeServerOptions extends BridgeDeps {
|
|
|
40
41
|
connectionRateLimit?: ConnectionRateLimit;
|
|
41
42
|
/** Told about every refused upgrade. Default: one `console.warn` per distinct origin and status. */
|
|
42
43
|
onRefuse?: (refusal: BridgeRefusal) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Where session replay recordings go (`POST /replay`, replay-ingest.ts); `false` = no replay routes (a plain request
|
|
46
|
+
* is answered 426, as before). Default: from the environment (`replayStoreFromEnv`).
|
|
47
|
+
*/
|
|
48
|
+
replay?: ReplayStore | false;
|
|
43
49
|
}
|
|
44
50
|
/** A token bucket over ENVELOPES (each one inside a batch counts), one per connection. */
|
|
45
51
|
export interface ConnectionRateLimit {
|
package/lib/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var D=Object.defineProperty;var d=(e,r)=>D(e,"name",{value:r,configurable:!0});var x=Object.defineProperty,u=d((e,r)=>x(e,"name",{value:r,configurable:!0}),"d"),$=Object.defineProperty,o=u((e,r)=>$(e,"name",{value:r,configurable:!0}),"o");import{createHash as F,timingSafeEqual as U}from"node:crypto";import{createServer as M,STATUS_CODES as W}from"node:http";import{createServer as j}from"node:net";import{WebSocket as G,WebSocketServer as H}from"ws";import{boundedClone as J,createEventFloodGuard as q,DEFAULT_RAW_BYTE_LIMIT as z,exceedsJsonByteBudget as V}from"@lensmcp/protocol-types";import{BRIDGE_TOKEN_PARAM as X,CONNECTION_BURST as K,CONNECTION_PER_SEC as Y,DEFAULT_WS_HOST as Q,DEFAULT_WS_PORT as Z,ENV_BRIDGE_ALLOWED_ORIGINS as N,MAX_BATCH_EVENTS as ee,MAX_BRIDGE_CONNECTIONS as re,MAX_FRAME_BYTES as te}from"./constants.js";import{resolveEnvSink as ne}from"./env-sink.js";import{asBaseEvent as oe,conformsToEnvelopeContract as se}from"./envelope-contract.js";import{envelopeToEvent as ie}from"./event-mappers.js";import{createOriginPolicy as ae,parseAllowedOrigins as ce}from"./origin-policy.js";import{createReplayIngest as de,replayStoreFromEnv as le}from"./replay-ingest.js";export function makePublisher(e){let r;return t=>{if(e.bus){e.bus.publish(t);return}if(e.sink){e.sink(t);return}if(r===void 0&&(r=ne()),r){r(t);return}(t.severity==="error"||t.severity==="fatal")&&console.debug(`[lensmcp-bridge] ${t.severity}: ${t.title}`)}}d(makePublisher,"makePublisher"),u(makePublisher,"makePublisher"),o(makePublisher,"makePublisher");export function handleClientEnvelope(e,r,t,a={}){const c=a.admit??ue;let l;try{l=JSON.parse(e)}catch{c();return}if(!l||typeof l!="object"||!("kind"in l)){c();return}if(l.kind==="batch"){if(!Array.isArray(l.events)){c();return}for(const p of l.events.slice(0,ee))c()&&(!p||typeof p!="object"||!("kind"in p)||p.kind!=="batch"&&v(p,r,t));return}c()&&v(l,r,t)}d(handleClientEnvelope,"handleClientEnvelope"),u(handleClientEnvelope,"handleClientEnvelope"),o(handleClientEnvelope,"handleClientEnvelope");const ue=o(()=>!0,"admitAll");function v(e,r,t){if(!se(e))return;let a;try{const c=ie(e,t);a=c&&oe(c)}catch{return}if(a)try{r(y(a))}catch{}}d(v,"E"),u(v,"h"),o(v,"mapAndPublish");function y(e){const r=e.raw;return r==null||!V(r,z)?e:{...e,raw:J(r).value}}d(y,"C"),u(y,"oe"),o(y,"boundRaw");export function readTabId(e){try{const r=JSON.parse(e);return typeof r?.tabId=="string"&&r.tabId?r.tabId:void 0}catch{return}}d(readTabId,"readTabId"),u(readTabId,"readTabId"),o(readTabId,"readTabId");export async function createBridgeServer(e={}){const r=e.host??Q,t=e.port??Z,a=makePublisher(e),c=ae([...e.allowedOrigins??[],...ce(process.env[N])]),l=e.token?E(e.token):void 0,p=e.maxConnections??re,C=e.connectionRateLimit??{burst:K,perSec:Y},A=e.onRefuse??w(),R=o(n=>{const s=n.headers.origin,i=s===void 0?{}:{origin:s};if(g.clients.size>=p)return{status:503,reason:"bridge connection limit reached",...i};if(!c.admits(s))return{status:403,reason:"origin not allowed",...i};if(l&&!h(n.url,l))return{status:401,reason:"missing or wrong bridge token",...i}},"refuseUpgrade"),I=e.replay===!1?void 0:e.replay??le(),B=I?de({...I,admits:o(n=>c.admits(n),"admits"),tokenOk:o(n=>!l||h(n,l),"tokenOk"),publish:a,...e.sessionId?{sessionId:e.sessionId}:{}}):void 0,f=M((n,s)=>{B?.(n,s)||T(n,s)});f.on("error",()=>{});const g=new H({server:f,maxPayload:te,verifyClient:o((n,s)=>{const i=R(n.req);if(!i){s(!0);return}try{A(i)}catch{}s(!1,i.status,i.reason)},"verifyClient")});g.on("error",()=>{}),g.on("connection",n=>{if(n.on("error",()=>{}),g.clients.size>p){n.close(1013,"bridge connection limit reached");return}let s;const i=q({burst:C.burst,perSec:C.perSec,envOverride:!1}),b=o(m=>{handleClientEnvelope(JSON.stringify({kind:"console",level:"warn",at:Date.now(),...s?{tabId:s}:{},args:[CONNECTION_THROTTLE_NOTICE,{dropped:m}]}),a,e.sessionId)},"announceDropped"),S={admit:o(()=>{const m=i.admit(fe);return m.ok&&m.suppressed>0&&b(m.suppressed),m.ok},"admit")};n.on("message",m=>{const P=m.toString();s??=readTabId(P),handleClientEnvelope(P,a,e.sessionId,S)}),n.on("close",()=>{s&&handleClientEnvelope(JSON.stringify({kind:"gone",at:Date.now(),tabId:s}),a,e.sessionId)})}),await new Promise((n,s)=>{const i=o(S=>{f.off("listening",b),s(S)},"onError"),b=o(()=>{f.off("error",i),n()},"onListening");f.once("error",i),f.once("listening",b),f.listen(t,r)});const O=f.address(),L=typeof O=="object"&&O!==null?O.port:t;let k;return{port:L,host:r,learnOrigin:o(n=>c.learn(n),"learnOrigin"),admitsOrigin:o(n=>c.admits(n),"admitsOrigin"),close:o(()=>(k??=(async()=>{const n=[...g.clients],s=[new Promise(i=>g.close(()=>i())),new Promise(i=>f.close(()=>i())),...n.map(_)];for(const i of n)i.terminate();f.closeAllConnections(),await Promise.all(s)})(),k),"close")}}d(createBridgeServer,"createBridgeServer"),u(createBridgeServer,"createBridgeServer"),o(createBridgeServer,"createBridgeServer");function T(e,r){const t=W[426]??"Upgrade Required";r.writeHead(426,{"Content-Length":Buffer.byteLength(t),"Content-Type":"text/plain"}),r.end(t)}d(T,"I"),u(T,"ie"),o(T,"answerPlainRequest");function _(e){return e.readyState===G.CLOSED?Promise.resolve():new Promise(r=>e.once("close",()=>r()))}d(_,"k"),u(_,"se"),o(_,"socketClosed");const fe="connection";export const CONNECTION_THROTTLE_NOTICE="[lensmcp-bridge] per-connection rate limit: this tab sent envelopes faster than the bridge admits, and some were dropped";const pe=32;function w(){const e=new Set;return r=>{const t=`${r.status} ${r.origin??""}`;if(e.has(t)||e.size>=pe)return;e.add(t);const a=r.status===403?` \u2014 if this is your app, add its origin to ${N} (or the Vite plugin's \`wsAllowedOrigins\`)`:r.status===401?" \u2014 a tab opened before the dev server restarted needs a reload; a no-build page needs the token as __LENSMCP_WS_TOKEN__":"";console.warn(`[lensmcp-bridge] refused a connection${r.origin?` from ${r.origin}`:""}: ${r.reason} (${r.status})${a}`)}}d(w,"N"),u(w,"de"),o(w,"warnRefusalOnce");function E(e){return F("sha256").update(e).digest()}d(E,"O"),u(E,"w"),o(E,"digest");function h(e,r){let t;try{t=new URL(e??"/","http://bridge.invalid").searchParams.get(X)}catch{return!1}return t!==null&&U(E(t),r)}d(h,"S"),u(h,"T"),o(h,"carriesToken");export function findFreePort(){return new Promise((e,r)=>{const t=j();t.unref(),t.on("error",r),t.listen(0,()=>{const a=t.address();if(!a){t.close(),r(new Error("Could not allocate free port"));return}const c=a.port;t.close(()=>e(c))})})}d(findFreePort,"findFreePort"),u(findFreePort,"findFreePort"),o(findFreePort,"findFreePort");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/bridge",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.18",
|
|
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.25.
|
|
23
|
-
"@lensmcp/protocol-types": "1.25.
|
|
22
|
+
"@lensmcp/core": "1.25.18",
|
|
23
|
+
"@lensmcp/protocol-types": "1.25.18",
|
|
24
24
|
"tslib": "^2.3.0",
|
|
25
25
|
"ws": "^8.21.0"
|
|
26
26
|
},
|