@lensmcp/cluster 1.22.1 → 1.22.3
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";var O=Object.defineProperty;var w=(p,i)=>O(p,"name",{value:i,configurable:!0});var E=Object.defineProperty,h=w((p,i)=>E(p,"name",{value:i,configurable:!0}),"h");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHandler=createHandler;const manifest_1=require("../manifest"),service_keys_1=require("./service-keys"),gateway_errors_1=require("../gateway-errors"),discovery_1=require("./discovery"),edge_1=require("./edge"),observability_1=require("./observability"),chooser_1=require("./chooser");function createHandler(p){const{rt:i,obs:d,auth:y,proxy:m,hooks:c}=p,g=h(e=>(e.headers.host||"").split(":")[0],"hostOf"),_=h((e,r)=>{if((0,edge_1.cors)(e,r,i.routes),e.method==="OPTIONS"){r.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_NOROUTE,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),r.end();return}const s=(e.headers.host||"").split(":")[0],t=i.routes.filter(o=>o.host&&(0,manifest_1.hostMatches)(o.host,s)&&o.prefix),n=(e.url??"/").split("?")[0];if((n==="/"||n==="/index.html"||n==="")&&i.routes.some(o=>o.host&&(0,manifest_1.hostMatches)(o.host,s)&&o.project===observability_1.LENS_DASHBOARD_PROJECT)){(0,chooser_1.serveChooser)(r,i.wsKey,e);return}if(t.length>0&&(n==="/health"||n==="/healthz")){const o=t.map(l=>({project:l.project,prefix:l.prefix,pods:l.pool?((0,discovery_1.pickSock)(l.pool,0),l.pool.socks.length):void 0,state:l.svc?.state??(l.target?"up":"unknown")})),f=o.every(l=>l.state!=="down"||!0);r.writeHead(f?200:503,{"content-type":"application/json"}),r.end(JSON.stringify({status:"ok",host:s,services:o},null,2));return}if(t.length>0&&n==="/.well-known/services"){r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({host:s,services:t.map(o=>({prefix:o.prefix,project:o.project,internal:!!o.internal}))},null,2));return}(0,edge_1.sendError)(r,e,404,"not_found",t.length>0?`No service mounted at ${n}. Mounted on ${s}: ${[...new Set(t.map(o=>o.prefix))].join(", ")} (+ /health, /.well-known/services)`:"No gateway route for this hostname.")},"handleNoRoute"),S=h(async(e,r)=>{if(!e.headers.host){const n=e.authority??e.headers[":authority"];n&&(e.headers.host=n)}for(const n of edge_1.GATEWAY_OWNED_HEADERS)delete e.headers[n];(0,gateway_errors_1.ensureRequestId)(e);const s=d.beginTrace(e);s&&(d.traceStep(s,"received",{host:s.host,method:s.method,url:s.url}),e.__lensmcpTrace=s);const t=c.httpContext(e,r,s);try{const n=e.url??"/",o=(0,edge_1.canonicalize)(n);if(o===void 0){d.traceStep(s,"bad-path",{}),d.finishTrace(s,"gateway",400),(0,edge_1.sendError)(r,e,400,"bad_request","Bad Request");return}const f=o.policy,l=o.path+o.query;if(l!==n&&(0,edge_1.rememberRawTarget)(e,n),e.url=l,(0,edge_1.resolveClientIp)(i,e),c.has("onRequest")&&(await c.run("onRequest",t),t.responded()))return;const a=(0,manifest_1.matchRoute)(i.routes,g(e),f+o.query);if(!a){_(e,r);return}if(t.route=a,d.traceStep(s,"route-match",{project:a.project,...a.prefix?{prefix:a.prefix}:{},...a.host?{routeHost:a.host}:{},...a.internal?{internal:!0}:{}}),e.method==="OPTIONS"){(0,edge_1.cors)(e,r,i.routes),r.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_ROUTED,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),r.end();return}if((0,edge_1.cors)(e,r,i.routes),c.has("beforeAuth")&&(await c.run("beforeAuth",t),t.responded()))return;if(a.internal){const u=y.internal(e,a,s);if(!u.ok){d.finishTrace(s,a.project,u.status),(0,edge_1.sendError)(r,e,u.status,u.reason,u.detail);return}t.caller=u.caller}else{const u=y.public(e,a,s,f);if(!u.ok){d.finishTrace(s,a.project,u.status),(0,edge_1.sendError)(r,e,u.status,u.reason,u.detail);return}if(t.claims=u.claims,c.has("afterAuth")&&(await c.run("afterAuth",t),t.responded()))return}const v=a.pool?i.serviceKeys[(0,service_keys_1.serviceKeyId)(a.svc?.wsKey,a.project)]:void 0;if(v&&(e.headers["x-internal-token"]=v),(0,edge_1.stampForwardingHeaders)(i,e),c.has("beforeForward")&&(await c.run("beforeForward",t),t.responded()))return;c.has("onResponse")&&r.on("close",()=>{t.status=r.statusCode??0,c.run("onResponse",t)}),m.forward(a,e,r)}catch(n){t.error=n,console.error(`[gateway] ${new Date().toISOString()} pipeline error traceId=${(0,gateway_errors_1.ensureRequestId)(e)}: ${n?.stack??String(n)}`),c.has("onError")&&await c.run("onError",t),t.responded()||(0,edge_1.sendError)(r,e,500,"internal","Internal gateway error.")}},"runPipeline");return(e,r)=>{S(e,r)}}w(createHandler,"createHandler"),h(createHandler,"createHandler");
|
|
1
|
+
"use strict";var O=Object.defineProperty;var w=(p,i)=>O(p,"name",{value:i,configurable:!0});var E=Object.defineProperty,h=w((p,i)=>E(p,"name",{value:i,configurable:!0}),"h");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHandler=createHandler;const manifest_1=require("../manifest"),service_keys_1=require("./service-keys"),gateway_errors_1=require("../gateway-errors"),discovery_1=require("./discovery"),edge_1=require("./edge"),observability_1=require("./observability"),chooser_1=require("./chooser");function createHandler(p){const{rt:i,obs:d,auth:y,proxy:m,hooks:c}=p,g=h(e=>(e.headers.host||"").split(":")[0],"hostOf"),_=h((e,r)=>{if((0,edge_1.cors)(e,r,i.routes),e.method==="OPTIONS"){r.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_NOROUTE,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),r.end();return}const s=(e.headers.host||"").split(":")[0],t=i.routes.filter(o=>o.host&&(0,manifest_1.hostMatches)(o.host,s)&&o.prefix),n=(e.url??"/").split("?")[0];if((n==="/"||n==="/index.html"||n==="")&&i.routes.some(o=>o.host&&(0,manifest_1.hostMatches)(o.host,s)&&o.project===observability_1.LENS_DASHBOARD_PROJECT)){(0,chooser_1.serveChooser)(r,i.wsKey,e);return}if(t.length>0&&(n==="/health"||n==="/healthz")){const o=t.map(l=>({project:l.project,prefix:l.prefix,pods:l.pool?((0,discovery_1.pickSock)(l.pool,0),l.pool.socks.length):void 0,state:l.svc?.state??(l.target?"up":"unknown")})),f=o.every(l=>l.state!=="down"||!0);r.writeHead(f?200:503,{"content-type":"application/json"}),r.end(JSON.stringify({status:"ok",host:s,services:o},null,2));return}if(t.length>0&&n==="/.well-known/services"){r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({host:s,services:t.map(o=>({prefix:o.prefix,project:o.project,internal:!!o.internal}))},null,2));return}(0,edge_1.sendError)(r,e,404,"not_found",t.length>0?`No service mounted at ${n}. Mounted on ${s}: ${[...new Set(t.map(o=>o.prefix))].join(", ")} (+ /health, /.well-known/services)`:"No gateway route for this hostname.")},"handleNoRoute"),S=h(async(e,r)=>{if(!e.headers.host){const n=e.authority??e.headers[":authority"];n&&(e.headers.host=n)}for(const n of edge_1.GATEWAY_OWNED_HEADERS)delete e.headers[n];(0,gateway_errors_1.ensureRequestId)(e);const s=d.beginTrace(e);s&&(d.traceStep(s,"received",{host:s.host,method:s.method,url:s.url}),e.__lensmcpTrace=s);const t=c.httpContext(e,r,s);try{const n=e.url??"/",o=(0,edge_1.canonicalize)(n);if(o===void 0){d.traceStep(s,"bad-path",{}),d.finishTrace(s,"gateway",400),(0,edge_1.sendError)(r,e,400,"bad_request","Bad Request");return}const f=o.policy,l=o.path+o.query;if(l!==n&&(0,edge_1.rememberRawTarget)(e,n),e.url=l,(0,edge_1.resolveClientIp)(i,e),c.has("onRequest")&&(await c.run("onRequest",t),t.responded()))return;const a=(0,manifest_1.matchRoute)(i.routes,g(e),f+o.query);if(!a){_(e,r);return}if(t.route=a,d.traceStep(s,"route-match",{project:a.project,...a.prefix?{prefix:a.prefix}:{},...a.host?{routeHost:a.host}:{},...a.internal?{internal:!0}:{}}),e.method==="OPTIONS"){(0,edge_1.cors)(e,r,i.routes),r.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_ROUTED,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),r.end();return}if((0,edge_1.cors)(e,r,i.routes),c.has("beforeAuth")&&(await c.run("beforeAuth",t),t.responded()))return;if(a.internal){const u=y.internal(e,a,s);if(!u.ok){d.finishTrace(s,a.project,u.status,void 0,a),(0,edge_1.sendError)(r,e,u.status,u.reason,u.detail);return}t.caller=u.caller}else{const u=y.public(e,a,s,f);if(!u.ok){d.finishTrace(s,a.project,u.status,void 0,a),(0,edge_1.sendError)(r,e,u.status,u.reason,u.detail);return}if(t.claims=u.claims,c.has("afterAuth")&&(await c.run("afterAuth",t),t.responded()))return}const v=a.pool?i.serviceKeys[(0,service_keys_1.serviceKeyId)(a.svc?.wsKey,a.project)]:void 0;if(v&&(e.headers["x-internal-token"]=v),(0,edge_1.stampForwardingHeaders)(i,e),c.has("beforeForward")&&(await c.run("beforeForward",t),t.responded()))return;c.has("onResponse")&&r.on("close",()=>{t.status=r.statusCode??0,c.run("onResponse",t)}),m.forward(a,e,r)}catch(n){t.error=n,console.error(`[gateway] ${new Date().toISOString()} pipeline error traceId=${(0,gateway_errors_1.ensureRequestId)(e)}: ${n?.stack??String(n)}`),c.has("onError")&&await c.run("onError",t),t.responded()||(0,edge_1.sendError)(r,e,500,"internal","Internal gateway error.")}},"runPipeline");return(e,r)=>{S(e,r)}}w(createHandler,"createHandler"),h(createHandler,"createHandler");
|
|
@@ -18,7 +18,8 @@ export interface Observability {
|
|
|
18
18
|
eid(): string;
|
|
19
19
|
beginTrace(req: http.IncomingMessage): GwTrace | undefined;
|
|
20
20
|
traceStep(t: GwTrace | undefined, stepName: string, info?: Record<string, unknown>): void;
|
|
21
|
-
|
|
21
|
+
/** `route` identifies the OWNING workspace, so a guest's request narration goes to its own bus. */
|
|
22
|
+
finishTrace(t: GwTrace | undefined, project: string, status: number, extra?: Record<string, unknown>, route?: Route): void;
|
|
22
23
|
recordEdge(route: Route, ms: number, status: number, caller?: string): void;
|
|
23
24
|
/** Start the periodic traffic-edge flush; returns the interval (already `.unref()`'d). */
|
|
24
25
|
startEdgeFlusher(): NodeJS.Timeout;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`;busHygiene?.maybeRotateEventFile(
|
|
1
|
+
"use strict";var D=Object.defineProperty;var l=(s,d)=>D(s,"name",{value:d,configurable:!0});var b=Object.defineProperty,o=l((s,d)=>b(s,"name",{value:d,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.LENS_DASHBOARD_PROJECT=void 0,exports.createObservability=createObservability;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),redact_1=require("@lensmcp/core/redact"),busHygiene=(()=>{try{return require("@lensmcp/node-instrumentation")}catch{return}})();exports.LENS_DASHBOARD_PROJECT="(lens-dashboard)";function ownerOf(s){return s?.svc?{root:s.svc.root,wsKey:s.svc.wsKey}:s?.lens?.workspaceKey?{wsKey:s.lens.workspaceKey}:{}}l(ownerOf,"ownerOf"),o(ownerOf,"ownerOf");function busFileFor(s,d){const{root:u}=ownerOf(s);if(!u)return;const p=path.join(u,".lensmcp","events.jsonl");return p===d?void 0:p}l(busFileFor,"busFileFor"),o(busFileFor,"busFileFor");const hdr=o(s=>typeof s=="string"&&s!==""?s:Array.isArray(s)?s[0]:void 0,"hdr");function createObservability(s,d){const u=o(()=>Date.now().toString(36)+Math.random().toString(36).slice(2,12),"eid"),p=busHygiene?.createEventFloodGuard({burst:30,perSec:5}),h=o((e,r,t,n,a,c)=>{const i=c??s;try{const y=p?p.admit(t):{ok:!0,suppressed:0};if(!y.ok)return;fs.mkdirSync(path.dirname(i),{recursive:!0});const w=n&&(0,redact_1.redactDeep)(n),g=JSON.stringify({id:u(),sessionId:"pending",timestamp:Date.now(),source:"gateway",category:"cluster",severity:e,context:(0,redact_1.redactDeep)({sessionId:"pending",...a}),fingerprint:t,title:(0,redact_1.safeTitle)(r),raw:y.suppressed>0?{...w,floodSuppressed:y.suppressed}:w})+`
|
|
2
|
+
`;busHygiene?.maybeRotateEventFile(i,g.length),fs.appendFileSync(i,g)}catch{}},"emit"),m=o(e=>{const r=hdr(e.headers["x-lensmcp-flow-id"]);let t=hdr(e.headers["x-request-id"]);if(!(!r&&!t&&!hdr(e.headers.traceparent)))return t||(t=u(),e.headers["x-request-id"]=t),{startedAt:Date.now(),method:(e.method??"GET").toUpperCase(),host:(e.headers.host||"").split(":")[0],url:e.url??"/",steps:[],...r?{flowId:r}:{},...t?{requestId:t}:{},...hdr(e.headers["x-lensmcp-origin-node-id"])?{originNodeId:hdr(e.headers["x-lensmcp-origin-node-id"])}:{}}},"beginTrace"),v=o((e,r,t)=>{e&&e.steps.push({step:r,atMs:Date.now()-e.startedAt,...t})},"traceStep"),S=o((e,r,t,n,a)=>{if(!e||e.done||(e.done=!0,r===exports.LENS_DASHBOARD_PROJECT&&t<500))return;const c=Date.now()-e.startedAt;h(t>=500?"error":"info",`gateway ${e.method} ${e.host}${e.url} \u2192 ${t} (${c}ms)`,`gateway-request:${r}`,{kind:"gateway-request",project:r,host:e.host,method:e.method,url:e.url,status:t,durationMs:c,startedAt:e.startedAt,steps:e.steps,...ownerOf(a).wsKey?{wsKey:ownerOf(a).wsKey}:{},...n},{...e.flowId?{flowId:e.flowId}:{},...e.requestId?{requestId:e.requestId}:{},...e.originNodeId?{originNodeId:e.originNodeId}:{}},busFileFor(a,s))},"finishTrace"),f=new Map;return{emit:h,eid:u,beginTrace:m,traceStep:v,finishTrace:S,recordEdge:o((e,r,t,n)=>{if(e.project===exports.LENS_DASHBOARD_PROJECT&&t<500&&t!==0)return;const a=e.host??"(default)",c=(n?n+"\u21D2":"")+a+"\u2192"+e.project,i=f.get(c)??{host:a,project:e.project,caller:n,count:0,totalMs:0,errors:0,...busFileFor(e,s)?{busFile:busFileFor(e,s)}:{},...ownerOf(e).wsKey?{wsKey:ownerOf(e).wsKey}:{}};i.count+=1,i.totalMs+=r,(t>=500||t===0)&&(i.errors+=1),f.set(c,i)},"recordEdge"),startEdgeFlusher:o(()=>{const e=setInterval(()=>{for(const r of f.values()){const t=Math.round(r.totalMs/r.count);h(r.errors?"warning":"info",`traffic ${r.host} \u2192 ${r.project}: ${r.count} req, ~${t}ms${r.errors?`, ${r.errors} errors`:""}`,`gateway-traffic:${r.host}->${r.project}`,{kind:"gateway-traffic",host:r.host,project:r.project,caller:r.caller,count:r.count,avgMs:t,errors:r.errors,...r.wsKey?{wsKey:r.wsKey}:{}},void 0,r.busFile)}f.clear()},d.trafficFlushMs);return e.unref(),e},"startEdgeFlusher")}}l(createObservability,"createObservability"),o(createObservability,"createObservability");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var L=Object.defineProperty;var T=(n,i)=>L(n,"name",{value:i,configurable:!0});var H=Object.defineProperty,r=T((n,i)=>H(n,"name",{value:i,configurable:!0}),"r");Object.defineProperty(exports,"__esModule",{value:!0}),exports.isReplayableRequest=isReplayableRequest,exports.createProxy=createProxy;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery"),edge_1=require("./edge"),gateway_errors_1=require("../gateway-errors"),body_stall_1=require("../body-stall"),types_1=require("./types"),pod_probe_1=require("./pod-probe"),service_keys_1=require("./service-keys");function isConnError(n){const i=n.code??"";return i==="ECONNREFUSED"||i==="ECONNRESET"||i==="ETIMEDOUT"||i==="EPIPE"||i==="EHOSTUNREACH"||i==="ENOENT"||/ECONNREFUSED|ECONNRESET|socket hang up|EPIPE/i.test(n.message)}T(isConnError,"isConnError"),r(isConnError,"isConnError");const IDEMPOTENT_METHODS=new Set(["GET","HEAD","OPTIONS","PUT","DELETE","TRACE"]);function isReplayableRequest(n){if(!IDEMPOTENT_METHODS.has((n.method??"GET").toUpperCase())||n.headers["transfer-encoding"]!==void 0)return!1;const i=n.headers["content-length"];return!(i!==void 0&&String(i).trim()!=="0")}T(isReplayableRequest,"isReplayableRequest"),r(isReplayableRequest,"isReplayableRequest");function edgeGone(n,i){return i.headersSent||i.writableEnded||i.destroyed===!0||n.aborted===!0}T(edgeGone,"edgeGone"),r(edgeGone,"edgeGone");const HOP_BY_HOP=new Set(["connection","keep-alive","proxy-connection","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),UPSTREAM_CONNECTION_HEADERS=["connection","keep-alive","proxy-connection"],httpProxy=require("http-proxy");function declaredFirstByteMs(n){const i=Number(n);return Number.isFinite(i)&&i>0?i:types_1.POD_RESPONSE_TIMEOUT_MS}T(declaredFirstByteMs,"declaredFirstByteMs"),r(declaredFirstByteMs,"declaredFirstByteMs");function armFirstByteGuard(n){let i,m=0,$=!1,O=!1;const P=r(b=>{i=setTimeout(x,b),i.unref?.()},"arm"),x=r(()=>{if(!($||!n.pending())){if(m+=n.firstByteMs,n.ceilingMs<=0||m>=n.ceilingMs){n.onWedged(`no response within ${m}ms (wedged socket) \u2014 evicted + respawning`);return}(0,pod_probe_1.probePodAlive)(n.socketPath,void 0,n.serviceKey).then(b=>{if(!($||!n.pending())){if(!b){n.onWedged(`no response within ${m}ms and no answer on ${types_1.POD_ALIVE_PATH} (wedged socket) \u2014 evicted + respawning`);return}O||(O=!0,n.onSlow(m,b)),P(Math.max(n.firstByteMs,types_1.POD_ALIVE_PROBE_TIMEOUT_MS))}})}},"onDeadline");return P(n.firstByteMs),{clear:r(()=>{$=!0,i&&clearTimeout(i)},"clear")}}T(armFirstByteGuard,"armFirstByteGuard"),r(armFirstByteGuard,"armFirstByteGuard");function createProxy(n,i,m){const{recordEdge:$,finishTrace:O,traceStep:P,emit:x}=i,b=r(async e=>{if(!e.lens||!n.lensSupervisor)return"unsupported";let t;const o=new Promise(p=>{t=setTimeout(()=>p("timeout"),types_1.LENS_REQUEST_RECOVERY_WAIT_MS),t.unref?.()}),u=n.lensSupervisor.recover(e.lens,"proxy-connection").then(p=>p?"ready":"failed").catch(()=>"failed"),y=await Promise.race([u,o]);t&&clearTimeout(t);const _=n.lensSupervisor.status(e.lens);return y==="failed"&&(!_||_.state==="ready")?"unsupported":y},"waitForManagedRecovery"),j=r((e,t,o,u,y,_)=>{if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}const p=e.lens?n.lensSupervisor?.status(e.lens):void 0,h=_==="timeout"||p?.state==="starting"||p?.state==="replacing",f=(0,gateway_errors_1.ensureRequestId)(t),c=e.lens?{project:e.project,reason:p?.reason??"connection-failure",generation:p?.generation??0,...h?{retryAfterMs:2e3}:{}}:void 0,s=h?503:502,d=h?"starting":"unavailable";console.error(`[gateway] ${new Date().toISOString()} upstream-error traceId=${f} project=${e.project} target=${String(u)} state=${p?.state??"unmanaged"} generation=${p?.generation??0}: ${y.message}`),x("warning",`upstream unavailable: ${e.project}`,`cluster-upstream:${e.project}`,{kind:"upstream-error",traceId:f,project:e.project,target:String(u),state:p?.state??"unmanaged",generation:p?.generation,error:y.message}),(0,edge_1.sendError)(o,t,s,d,h?`Upstream ${e.project} is restarting.`:`Upstream ${e.project} unavailable.`,c,h?{"retry-after":"2"}:void 0)},"sendTerminalUpstreamError"),k=(0,body_stall_1.createBodyStallGuard)(n.bodyStallMs??types_1.BODY_STALL_MS),D={keepAlive:!0,keepAliveMsecs:3e4,maxSockets:1024,maxFreeSockets:256},R=new http.Agent(D),U=new(require("node:https")).Agent(D),A=r(e=>typeof e=="string"&&e.startsWith("https:")?U:R,"agentForTarget"),M=httpProxy.createProxyServer({xfwd:!1,secure:!1,proxyTimeout:12e4,agent:R}),C=r((e,t,o)=>{const u=r(()=>{o()||e.destroyed||e.destroy()},"onEdgeClose");t.once("close",u),e.once("close",()=>t.removeListener("close",u))},"propagateEdgeCancel");M.on("proxyReq",(e,t,o)=>{for(const u of UPSTREAM_CONNECTION_HEADERS)e.removeHeader(u);e.setHeader("connection","keep-alive"),C(e,o,()=>o.writableEnded)}),M.on("proxyRes",(e,t,o)=>{const u=t.__lensmcpPod;u&&(e.headers["x-lensmcp-pod"]=u),delete e.headers["x-powered-by"],e.headers.server="LensMCP",o&&C(e,o,()=>o.writableEnded),o&&k.armResponse(o)});const N=r((e,t,o,u)=>{(0,edge_1.stampForwardingHeaders)(n,t),k.arm(t);const y={},_=t.method==="GET"||t.method==="HEAD"||t.method==="OPTIONS";for(const[a,l]of Object.entries(t.headers))l===void 0||a.startsWith(":")||HOP_BY_HOP.has(a)||_&&(a==="content-length"||a==="transfer-encoding")||(y[a]=l);let p=http,h;if(typeof e=="object")h={socketPath:e.socketPath,path:t.url,method:t.method,headers:y,agent:R};else{const a=new URL(e),l=a.protocol==="https:";p=l?require("node:https"):http,y.host=a.host,h={protocol:a.protocol,hostname:a.hostname,port:a.port||(l?443:80),path:t.url,method:t.method,headers:y,agent:A(e)}}const f=o;f.__lensmcpErrContained||(f.__lensmcpErrContained=!0,o.on("error",()=>{}));let c;const s=r(()=>{o.writableEnded||E.destroy()},"onReqGone"),d=r(()=>{c&&!c.destroyed&&c.destroy()},"onEdgeClose"),g=r(()=>{t.removeListener("error",s),t.removeListener("aborted",s),t.removeListener("close",s),o.removeListener("close",d)},"detach"),E=p.request(h,a=>{if(c=a,u.onResponse(),o.headersSent||o.writableEnded||o.destroyed){a.destroy();return}const l={};for(const[v,I]of Object.entries(a.headers))I===void 0||HOP_BY_HOP.has(v)||(l[v]=I);delete l["x-powered-by"],l.server="LensMCP";const w=t.__lensmcpPod;w&&(l["x-lensmcp-pod"]=w);try{o.writeHead(a.statusCode??502,l)}catch(v){console.error("[gateway] h2 relay: bad response header, resetting stream:",v.message),a.destroy(),o.destroyed||o.destroy(v);return}a.pipe(o),k.armResponse(o);const S=r(()=>{!o.writableEnded&&!o.destroyed&&o.destroy()},"abortEdge");a.once("error",S),a.once("close",S),o.on("close",d)});E.once("error",a=>u.onError(a)),E.once("close",g),t.on("error",s),t.on("aborted",s),t.on("close",s),_?E.end():t.pipe(E)},"pipeUpstream"),q=r(async(e,t,o)=>{const u=t.__lensmcpTrace;if(e.pool){const c=e.svc;c&&(c.lastUsed=Date.now(),c.inflight+=1,o.on("close",()=>{c.inflight-=1,c.lastUsed=Date.now()}));let s=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs);if(!s&&c&&(P(u,"cold-start",{project:e.project}),await m.ensureUp(c)&&(s=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs))),c&&s&&m.markUp(c,"observed"),c&&m.scaleUp(c),!s){const l=c?.lastErrors?.length?` Service errors: ${c.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,t,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${l} The watcher recovers and the next request retries automatically.`);return}const d=path.basename(s,".sock");t.__lensmcpPod=d,P(u,"pod-select",{pod:d,pods:e.pool.socks.length,strategy:"round-robin"});let g=!1;const E=r(l=>{e.pool.socks=e.pool.socks.filter(w=>w!==s),e.pool.idx=-1;try{fs.unlinkSync(s)}catch{}console.error(`[gateway] pod ${d} of ${e.project}: ${l}`)},"evictPod"),a=armFirstByteGuard({socketPath:s,serviceKey:n.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:r(()=>!g&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:r(l=>{g=!0,E(l),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${d} of ${e.project} was unresponsive \u2014 evicted, retry.`)},"onWedged"),onSlow:r((l,w)=>{console.log(`[gateway] pod ${d} of ${e.project}: slow first byte, pod alive (${l}ms, ${w.inflight} in-flight, ${w.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>a.clear()),N({socketPath:s},t,o,{onResponse:r(()=>{a.clear(),g=!0},"onResponse"),onError:r(l=>{a.clear(),!g&&(g=!0,E(l.message),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${d} of ${e.project} unavailable \u2014 retry.`))},"onError")});return}const y=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let _=0,p=!1;const h=isReplayableRequest(t),f=r(()=>{if(edgeGone(t,o))return;const c=e.target;N(c,t,o,{onResponse:r(()=>{},"onResponse"),onError:r(s=>{if(isConnError(s)&&(_+=1),e.lens&&isConnError(s)&&h&&_>=2&&!p&&!edgeGone(t,o)){p=!0,b(e).then(d=>{if(!edgeGone(t,o)){if(d==="ready"){f();return}if(d==="unsupported"&&Date.now()<y){setTimeout(f,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}j(e,t,o,e.target,s,d)}});return}if(isConnError(s)&&h&&Date.now()<y&&!edgeGone(t,o)){setTimeout(f,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}j(e,t,o,c,s)},"onError")})},"tryTcp");f()},"forwardNative");return{proxy:M,forward:r(async(e,t,o)=>{const u=Date.now(),y=t.__lensmcpTrace;if(o.on("close",()=>{$(e,Date.now()-u,o.statusCode??0,t.__lensmcpCaller),O(y,e.project,o.statusCode??0,{...t.__lensmcpPod?{pod:t.__lensmcpPod}:{},...t.__lensmcpCaller?{caller:t.__lensmcpCaller}:{}})}),e.prependPrefix&&!t.url?.startsWith(e.prependPrefix)&&(t.url=e.prependPrefix+(t.url??"/")),(0,edge_1.stampForwardingHeaders)(n,t),k.arm(t),(t.httpVersionMajor??1)>=2){await q(e,t,o);return}if(e.pool){const s=e.svc;s&&(s.lastUsed=Date.now(),s.inflight+=1,o.on("close",()=>{s.inflight-=1,s.lastUsed=Date.now()}));let d=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs);if(!d&&s&&(P(y,"cold-start",{project:e.project}),await m.ensureUp(s)&&(d=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs))),s&&d&&m.markUp(s,"observed"),s&&m.scaleUp(s),!d){const S=s?.lastErrors?.length?` Service errors: ${s.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,t,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${S} The watcher recovers and the next request retries automatically.`);return}const g=path.basename(d,".sock");t.__lensmcpPod=g,P(y,"pod-select",{pod:g,pods:e.pool.socks.length,strategy:"round-robin"});let E=!1;const a=r(S=>{e.pool.socks=e.pool.socks.filter(v=>v!==d),e.pool.idx=-1;try{fs.unlinkSync(d)}catch{}console.error(`[gateway] pod ${g} of ${e.project}: ${S}`)},"evictPod"),l=armFirstByteGuard({socketPath:d,serviceKey:n.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:r(()=>!E&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:r(S=>{E=!0,a(S),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${g} of ${e.project} was unresponsive \u2014 evicted, retry.`)},"onWedged"),onSlow:r((S,v)=>{console.log(`[gateway] pod ${g} of ${e.project}: slow first byte, pod alive (${S}ms, ${v.inflight} in-flight, ${v.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>l.clear());const w=Math.max(12e4,types_1.POD_SLOW_FIRST_BYTE_MS+6e4);M.web(t,o,{target:{socketPath:d},agent:R,proxyTimeout:w},S=>{l.clear(),!E&&(E=!0,a(S.message),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${g} of ${e.project} unavailable \u2014 retry.`))});return}const _=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let p=0,h=!1;const f=isReplayableRequest(t),c=r(()=>{if(edgeGone(t,o))return;const s=e.target,d={target:s,autoRewrite:typeof s=="string",changeOrigin:!0,agent:A(s)};M.web(t,o,d,g=>{if(isConnError(g)&&(p+=1),e.lens&&isConnError(g)&&f&&p>=2&&!h&&!edgeGone(t,o)){h=!0,b(e).then(E=>{if(!edgeGone(t,o)){if(E==="ready"){c();return}if(E==="unsupported"&&Date.now()<_){setTimeout(c,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}j(e,t,o,e.target,g,E)}});return}if(isConnError(g)&&f&&Date.now()<_&&!edgeGone(t,o)){setTimeout(c,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}o.destroyed||j(e,t,o,s,g)})},"tryTcp");c()},"forward"),agentForTarget:A,destroyAgents:r(()=>{k.stop(),R.destroy(),U.destroy()},"destroyAgents"),closeProxy:r(()=>{try{M.close?.()}catch{}},"closeProxy")}}T(createProxy,"createProxy"),r(createProxy,"createProxy");
|
|
1
|
+
"use strict";var L=Object.defineProperty;var T=(n,i)=>L(n,"name",{value:i,configurable:!0});var H=Object.defineProperty,r=T((n,i)=>H(n,"name",{value:i,configurable:!0}),"r");Object.defineProperty(exports,"__esModule",{value:!0}),exports.isReplayableRequest=isReplayableRequest,exports.createProxy=createProxy;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery"),edge_1=require("./edge"),gateway_errors_1=require("../gateway-errors"),body_stall_1=require("../body-stall"),types_1=require("./types"),pod_probe_1=require("./pod-probe"),service_keys_1=require("./service-keys");function isConnError(n){const i=n.code??"";return i==="ECONNREFUSED"||i==="ECONNRESET"||i==="ETIMEDOUT"||i==="EPIPE"||i==="EHOSTUNREACH"||i==="ENOENT"||/ECONNREFUSED|ECONNRESET|socket hang up|EPIPE/i.test(n.message)}T(isConnError,"isConnError"),r(isConnError,"isConnError");const IDEMPOTENT_METHODS=new Set(["GET","HEAD","OPTIONS","PUT","DELETE","TRACE"]);function isReplayableRequest(n){if(!IDEMPOTENT_METHODS.has((n.method??"GET").toUpperCase())||n.headers["transfer-encoding"]!==void 0)return!1;const i=n.headers["content-length"];return!(i!==void 0&&String(i).trim()!=="0")}T(isReplayableRequest,"isReplayableRequest"),r(isReplayableRequest,"isReplayableRequest");function edgeGone(n,i){return i.headersSent||i.writableEnded||i.destroyed===!0||n.aborted===!0}T(edgeGone,"edgeGone"),r(edgeGone,"edgeGone");const HOP_BY_HOP=new Set(["connection","keep-alive","proxy-connection","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),UPSTREAM_CONNECTION_HEADERS=["connection","keep-alive","proxy-connection"],httpProxy=require("http-proxy");function declaredFirstByteMs(n){const i=Number(n);return Number.isFinite(i)&&i>0?i:types_1.POD_RESPONSE_TIMEOUT_MS}T(declaredFirstByteMs,"declaredFirstByteMs"),r(declaredFirstByteMs,"declaredFirstByteMs");function armFirstByteGuard(n){let i,m=0,$=!1,O=!1;const P=r(b=>{i=setTimeout(x,b),i.unref?.()},"arm"),x=r(()=>{if(!($||!n.pending())){if(m+=n.firstByteMs,n.ceilingMs<=0||m>=n.ceilingMs){n.onWedged(`no response within ${m}ms (wedged socket) \u2014 evicted + respawning`);return}(0,pod_probe_1.probePodAlive)(n.socketPath,void 0,n.serviceKey).then(b=>{if(!($||!n.pending())){if(!b){n.onWedged(`no response within ${m}ms and no answer on ${types_1.POD_ALIVE_PATH} (wedged socket) \u2014 evicted + respawning`);return}O||(O=!0,n.onSlow(m,b)),P(Math.max(n.firstByteMs,types_1.POD_ALIVE_PROBE_TIMEOUT_MS))}})}},"onDeadline");return P(n.firstByteMs),{clear:r(()=>{$=!0,i&&clearTimeout(i)},"clear")}}T(armFirstByteGuard,"armFirstByteGuard"),r(armFirstByteGuard,"armFirstByteGuard");function createProxy(n,i,m){const{recordEdge:$,finishTrace:O,traceStep:P,emit:x}=i,b=r(async e=>{if(!e.lens||!n.lensSupervisor)return"unsupported";let t;const o=new Promise(p=>{t=setTimeout(()=>p("timeout"),types_1.LENS_REQUEST_RECOVERY_WAIT_MS),t.unref?.()}),u=n.lensSupervisor.recover(e.lens,"proxy-connection").then(p=>p?"ready":"failed").catch(()=>"failed"),y=await Promise.race([u,o]);t&&clearTimeout(t);const _=n.lensSupervisor.status(e.lens);return y==="failed"&&(!_||_.state==="ready")?"unsupported":y},"waitForManagedRecovery"),j=r((e,t,o,u,y,_)=>{if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}const p=e.lens?n.lensSupervisor?.status(e.lens):void 0,h=_==="timeout"||p?.state==="starting"||p?.state==="replacing",f=(0,gateway_errors_1.ensureRequestId)(t),c=e.lens?{project:e.project,reason:p?.reason??"connection-failure",generation:p?.generation??0,...h?{retryAfterMs:2e3}:{}}:void 0,s=h?503:502,d=h?"starting":"unavailable";console.error(`[gateway] ${new Date().toISOString()} upstream-error traceId=${f} project=${e.project} target=${String(u)} state=${p?.state??"unmanaged"} generation=${p?.generation??0}: ${y.message}`),x("warning",`upstream unavailable: ${e.project}`,`cluster-upstream:${e.project}`,{kind:"upstream-error",traceId:f,project:e.project,target:String(u),state:p?.state??"unmanaged",generation:p?.generation,error:y.message}),(0,edge_1.sendError)(o,t,s,d,h?`Upstream ${e.project} is restarting.`:`Upstream ${e.project} unavailable.`,c,h?{"retry-after":"2"}:void 0)},"sendTerminalUpstreamError"),k=(0,body_stall_1.createBodyStallGuard)(n.bodyStallMs??types_1.BODY_STALL_MS),D={keepAlive:!0,keepAliveMsecs:3e4,maxSockets:1024,maxFreeSockets:256},R=new http.Agent(D),U=new(require("node:https")).Agent(D),A=r(e=>typeof e=="string"&&e.startsWith("https:")?U:R,"agentForTarget"),M=httpProxy.createProxyServer({xfwd:!1,secure:!1,proxyTimeout:12e4,agent:R}),C=r((e,t,o)=>{const u=r(()=>{o()||e.destroyed||e.destroy()},"onEdgeClose");t.once("close",u),e.once("close",()=>t.removeListener("close",u))},"propagateEdgeCancel");M.on("proxyReq",(e,t,o)=>{for(const u of UPSTREAM_CONNECTION_HEADERS)e.removeHeader(u);e.setHeader("connection","keep-alive"),C(e,o,()=>o.writableEnded)}),M.on("proxyRes",(e,t,o)=>{const u=t.__lensmcpPod;u&&(e.headers["x-lensmcp-pod"]=u),delete e.headers["x-powered-by"],e.headers.server="LensMCP",o&&C(e,o,()=>o.writableEnded),o&&k.armResponse(o)});const N=r((e,t,o,u)=>{(0,edge_1.stampForwardingHeaders)(n,t),k.arm(t);const y={},_=t.method==="GET"||t.method==="HEAD"||t.method==="OPTIONS";for(const[a,l]of Object.entries(t.headers))l===void 0||a.startsWith(":")||HOP_BY_HOP.has(a)||_&&(a==="content-length"||a==="transfer-encoding")||(y[a]=l);let p=http,h;if(typeof e=="object")h={socketPath:e.socketPath,path:t.url,method:t.method,headers:y,agent:R};else{const a=new URL(e),l=a.protocol==="https:";p=l?require("node:https"):http,y.host=a.host,h={protocol:a.protocol,hostname:a.hostname,port:a.port||(l?443:80),path:t.url,method:t.method,headers:y,agent:A(e)}}const f=o;f.__lensmcpErrContained||(f.__lensmcpErrContained=!0,o.on("error",()=>{}));let c;const s=r(()=>{o.writableEnded||E.destroy()},"onReqGone"),d=r(()=>{c&&!c.destroyed&&c.destroy()},"onEdgeClose"),g=r(()=>{t.removeListener("error",s),t.removeListener("aborted",s),t.removeListener("close",s),o.removeListener("close",d)},"detach"),E=p.request(h,a=>{if(c=a,u.onResponse(),o.headersSent||o.writableEnded||o.destroyed){a.destroy();return}const l={};for(const[v,I]of Object.entries(a.headers))I===void 0||HOP_BY_HOP.has(v)||(l[v]=I);delete l["x-powered-by"],l.server="LensMCP";const w=t.__lensmcpPod;w&&(l["x-lensmcp-pod"]=w);try{o.writeHead(a.statusCode??502,l)}catch(v){console.error("[gateway] h2 relay: bad response header, resetting stream:",v.message),a.destroy(),o.destroyed||o.destroy(v);return}a.pipe(o),k.armResponse(o);const S=r(()=>{!o.writableEnded&&!o.destroyed&&o.destroy()},"abortEdge");a.once("error",S),a.once("close",S),o.on("close",d)});E.once("error",a=>u.onError(a)),E.once("close",g),t.on("error",s),t.on("aborted",s),t.on("close",s),_?E.end():t.pipe(E)},"pipeUpstream"),q=r(async(e,t,o)=>{const u=t.__lensmcpTrace;if(e.pool){const c=e.svc;c&&(c.lastUsed=Date.now(),c.inflight+=1,o.on("close",()=>{c.inflight-=1,c.lastUsed=Date.now()}));let s=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs);if(!s&&c&&(P(u,"cold-start",{project:e.project}),await m.ensureUp(c)&&(s=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs))),c&&s&&m.markUp(c,"observed"),c&&m.scaleUp(c),!s){const l=c?.lastErrors?.length?` Service errors: ${c.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,t,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${l} The watcher recovers and the next request retries automatically.`);return}const d=path.basename(s,".sock");t.__lensmcpPod=d,P(u,"pod-select",{pod:d,pods:e.pool.socks.length,strategy:"round-robin"});let g=!1;const E=r(l=>{e.pool.socks=e.pool.socks.filter(w=>w!==s),e.pool.idx=-1;try{fs.unlinkSync(s)}catch{}console.error(`[gateway] pod ${d} of ${e.project}: ${l}`)},"evictPod"),a=armFirstByteGuard({socketPath:s,serviceKey:n.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:r(()=>!g&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:r(l=>{g=!0,E(l),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${d} of ${e.project} was unresponsive \u2014 evicted, retry.`)},"onWedged"),onSlow:r((l,w)=>{console.log(`[gateway] pod ${d} of ${e.project}: slow first byte, pod alive (${l}ms, ${w.inflight} in-flight, ${w.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>a.clear()),N({socketPath:s},t,o,{onResponse:r(()=>{a.clear(),g=!0},"onResponse"),onError:r(l=>{a.clear(),!g&&(g=!0,E(l.message),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${d} of ${e.project} unavailable \u2014 retry.`))},"onError")});return}const y=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let _=0,p=!1;const h=isReplayableRequest(t),f=r(()=>{if(edgeGone(t,o))return;const c=e.target;N(c,t,o,{onResponse:r(()=>{},"onResponse"),onError:r(s=>{if(isConnError(s)&&(_+=1),e.lens&&isConnError(s)&&h&&_>=2&&!p&&!edgeGone(t,o)){p=!0,b(e).then(d=>{if(!edgeGone(t,o)){if(d==="ready"){f();return}if(d==="unsupported"&&Date.now()<y){setTimeout(f,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}j(e,t,o,e.target,s,d)}});return}if(isConnError(s)&&h&&Date.now()<y&&!edgeGone(t,o)){setTimeout(f,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}j(e,t,o,c,s)},"onError")})},"tryTcp");f()},"forwardNative");return{proxy:M,forward:r(async(e,t,o)=>{const u=Date.now(),y=t.__lensmcpTrace;if(o.on("close",()=>{$(e,Date.now()-u,o.statusCode??0,t.__lensmcpCaller),O(y,e.project,o.statusCode??0,{...t.__lensmcpPod?{pod:t.__lensmcpPod}:{},...t.__lensmcpCaller?{caller:t.__lensmcpCaller}:{}},e)}),e.prependPrefix&&!t.url?.startsWith(e.prependPrefix)&&(t.url=e.prependPrefix+(t.url??"/")),(0,edge_1.stampForwardingHeaders)(n,t),k.arm(t),(t.httpVersionMajor??1)>=2){await q(e,t,o);return}if(e.pool){const s=e.svc;s&&(s.lastUsed=Date.now(),s.inflight+=1,o.on("close",()=>{s.inflight-=1,s.lastUsed=Date.now()}));let d=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs);if(!d&&s&&(P(y,"cold-start",{project:e.project}),await m.ensureUp(s)&&(d=(0,discovery_1.pickSock)(e.pool,n.scanTtlMs))),s&&d&&m.markUp(s,"observed"),s&&m.scaleUp(s),!d){const S=s?.lastErrors?.length?` Service errors: ${s.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,t,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${S} The watcher recovers and the next request retries automatically.`);return}const g=path.basename(d,".sock");t.__lensmcpPod=g,P(y,"pod-select",{pod:g,pods:e.pool.socks.length,strategy:"round-robin"});let E=!1;const a=r(S=>{e.pool.socks=e.pool.socks.filter(v=>v!==d),e.pool.idx=-1;try{fs.unlinkSync(d)}catch{}console.error(`[gateway] pod ${g} of ${e.project}: ${S}`)},"evictPod"),l=armFirstByteGuard({socketPath:d,serviceKey:n.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:r(()=>!E&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:r(S=>{E=!0,a(S),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${g} of ${e.project} was unresponsive \u2014 evicted, retry.`)},"onWedged"),onSlow:r((S,v)=>{console.log(`[gateway] pod ${g} of ${e.project}: slow first byte, pod alive (${S}ms, ${v.inflight} in-flight, ${v.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>l.clear());const w=Math.max(12e4,types_1.POD_SLOW_FIRST_BYTE_MS+6e4);M.web(t,o,{target:{socketPath:d},agent:R,proxyTimeout:w},S=>{l.clear(),!E&&(E=!0,a(S.message),(0,edge_1.sendError)(o,t,502,"unavailable",`Pod ${g} of ${e.project} unavailable \u2014 retry.`))});return}const _=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let p=0,h=!1;const f=isReplayableRequest(t),c=r(()=>{if(edgeGone(t,o))return;const s=e.target,d={target:s,autoRewrite:typeof s=="string",changeOrigin:!0,agent:A(s)};M.web(t,o,d,g=>{if(isConnError(g)&&(p+=1),e.lens&&isConnError(g)&&f&&p>=2&&!h&&!edgeGone(t,o)){h=!0,b(e).then(E=>{if(!edgeGone(t,o)){if(E==="ready"){c();return}if(E==="unsupported"&&Date.now()<_){setTimeout(c,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}j(e,t,o,e.target,g,E)}});return}if(isConnError(g)&&f&&Date.now()<_&&!edgeGone(t,o)){setTimeout(c,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}o.destroyed||j(e,t,o,s,g)})},"tryTcp");c()},"forward"),agentForTarget:A,destroyAgents:r(()=>{k.stop(),R.destroy(),U.destroy()},"destroyAgents"),closeProxy:r(()=>{try{M.close?.()}catch{}},"closeProxy")}}T(createProxy,"createProxy"),r(createProxy,"createProxy");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/cluster",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.3",
|
|
4
4
|
"description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@lensmcp/core": "1.22.
|
|
61
|
-
"@lensmcp/node-instrumentation": "1.22.
|
|
62
|
-
"@lensmcp/nx-plugin": "1.22.
|
|
60
|
+
"@lensmcp/core": "1.22.3",
|
|
61
|
+
"@lensmcp/node-instrumentation": "1.22.3",
|
|
62
|
+
"@lensmcp/nx-plugin": "1.22.3",
|
|
63
63
|
"fastify": "^5.8.5",
|
|
64
64
|
"@fastify/reply-from": "^12.6.2",
|
|
65
65
|
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
|