@lensmcp/cluster 1.21.3 → 1.21.4
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/create-webpack-dev.d.ts +45 -0
- package/create-webpack-dev.js +1 -1
- package/executors/gateway/body-stall.d.ts +37 -0
- package/executors/gateway/body-stall.js +1 -0
- package/executors/gateway/canonical-path.d.ts +65 -0
- package/executors/gateway/canonical-path.js +1 -0
- package/executors/gateway/client-ip.d.ts +44 -0
- package/executors/gateway/client-ip.js +1 -0
- package/executors/gateway/jwks-verify.js +1 -1
- package/executors/gateway/main.prod-gateway.js +2 -2
- package/executors/gateway/manifest.d.ts +27 -3
- package/executors/gateway/manifest.js +1 -1
- package/executors/gateway/prod-config.d.ts +74 -0
- package/executors/gateway/prod-config.js +1 -0
- package/executors/gateway/prod-runtime/access-log.js +1 -1
- package/executors/gateway/prod-runtime/app.d.ts +6 -0
- package/executors/gateway/prod-runtime/app.js +1 -1
- package/executors/gateway/prod-runtime/auth.d.ts +1 -1
- package/executors/gateway/prod-runtime/auth.js +1 -1
- package/executors/gateway/prod-runtime/body-stall.d.ts +5 -0
- package/executors/gateway/prod-runtime/body-stall.js +1 -0
- package/executors/gateway/prod-runtime/cors.js +1 -1
- package/executors/gateway/prod-runtime/edge.d.ts +19 -3
- package/executors/gateway/prod-runtime/edge.js +1 -1
- package/executors/gateway/prod-runtime/handler.js +1 -1
- package/executors/gateway/prod-runtime/rollout.d.ts +11 -4
- package/executors/gateway/prod-runtime/rollout.js +1 -1
- package/executors/gateway/prod-runtime/routing.js +1 -1
- package/executors/gateway/prod-runtime/server.js +1 -1
- package/executors/gateway/prod-runtime/types.d.ts +30 -3
- package/executors/gateway/prod-runtime/upgrade.d.ts +25 -0
- package/executors/gateway/prod-runtime/upgrade.js +10 -2
- package/executors/gateway/prod-runtime/upstream.d.ts +3 -1
- package/executors/gateway/prod-runtime/upstream.js +1 -1
- package/executors/gateway/providers-prod.d.ts +13 -4
- package/executors/gateway/providers-prod.js +1 -1
- package/executors/gateway/registry-source.js +1 -1
- package/executors/gateway/rollout-ops.js +2 -2
- package/executors/gateway/runtime/auth.d.ts +6 -1
- package/executors/gateway/runtime/auth.js +1 -1
- package/executors/gateway/runtime/control.js +2 -1
- package/executors/gateway/runtime/dev-auth.d.ts +6 -2
- package/executors/gateway/runtime/dev-auth.js +1 -1
- package/executors/gateway/runtime/discovery.js +1 -1
- package/executors/gateway/runtime/edge.d.ts +19 -0
- package/executors/gateway/runtime/edge.js +1 -1
- package/executors/gateway/runtime/handler.js +1 -1
- package/executors/gateway/runtime/lens-children.d.ts +7 -0
- package/executors/gateway/runtime/lens-children.js +1 -1
- package/executors/gateway/runtime/lifecycle.d.ts +69 -7
- package/executors/gateway/runtime/lifecycle.js +3 -3
- package/executors/gateway/runtime/pod-probe.d.ts +37 -0
- package/executors/gateway/runtime/pod-probe.js +1 -0
- package/executors/gateway/runtime/proxy.js +1 -1
- package/executors/gateway/runtime/scope.d.ts +15 -0
- package/executors/gateway/runtime/scope.js +1 -1
- package/executors/gateway/runtime/server.js +2 -2
- package/executors/gateway/runtime/service-keys.d.ts +42 -1
- package/executors/gateway/runtime/service-keys.js +1 -1
- package/executors/gateway/runtime/types.d.ts +193 -1
- package/executors/gateway/runtime/types.js +1 -1
- package/executors/gateway/runtime/upgrade.js +1 -1
- package/executors/gateway/runtime/workspace-log.d.ts +7 -0
- package/executors/gateway/runtime/workspace-log.js +1 -0
- package/main.devserver.js +7 -7
- package/package.json +4 -4
package/main.devserver.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var pe=Object.defineProperty;var h=(o,d)=>pe(o,"name",{value:d,configurable:!0});var ie=Object.defineProperty,a=h((o,d)=>ie(o,"name",{value:d,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),node_child_process_1=require("node:child_process"),http=tslib_1.__importStar(require("node:http")),readline=tslib_1.__importStar(require("node:readline")),inspector=tslib_1.__importStar(require("node:inspector")),util=tslib_1.__importStar(require("node:util")),httpProxy=require("http-proxy"),{prettyFactory}=require("pino-pretty"),BUNDLE_PATH=process.env.BUNDLE_PATH?.trim()||"./main.js",SERVICE_NAME=process.env.SERVICE_NAME||"",SERVICE_PREFIX=process.env.SERVE_PREFIX?`/${process.env.SERVE_PREFIX}`:"",GATEWAY_MIDDLEWARE_PATH=process.env.GATEWAY_MIDDLEWARE||"",GATEWAY_CONFIG=(()=>{const o=process.env.GATEWAY_CONFIG;if(!o)return{};try{return JSON.parse(o)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const o=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof o=="function"?o:typeof o.default=="function"?o.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(o){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,o)}function applyGatewayMiddleware(o){if(gatewayMiddleware)try{gatewayMiddleware(o,GATEWAY_CONFIG)}catch(d){console.error("[gateway] Middleware error:",d)}}h(applyGatewayMiddleware,"applyGatewayMiddleware"),a(applyGatewayMiddleware,"applyGatewayMiddleware");const GATEWAY_ROUTES=(()=>{try{return JSON.parse(process.env.GATEWAY_ROUTES||"[]")}catch{return[]}})(),GATEWAY_HTTPS=process.env.GATEWAY_HTTPS==="1";function hostMatches(o,d){if(o.startsWith("*.")){const g=o.slice(1);return d.endsWith(g)||d===o.slice(2)}return d===o}h(hostMatches,"hostMatches"),a(hostMatches,"hostMatches");function matchGatewayRoute(o){if(GATEWAY_ROUTES.length===0)return;const d=(o.headers.host||"").split(":")[0],g=o.url||"/";for(const f of GATEWAY_ROUTES)if(!(f.host&&!hostMatches(f.host,d))&&!(f.prefix&&!g.startsWith(f.prefix)))return f}h(matchGatewayRoute,"matchGatewayRoute"),a(matchGatewayRoute,"matchGatewayRoute");const WORKER_NAMES=(()=>{try{return JSON.parse(process.env.WORKERS||"[]")}catch{return[]}})(),LENSMCP_WS_KEY=process.env.LENSMCP_WS_KEY||"",SOCK_DIR=LENSMCP_WS_KEY?path.join(os.tmpdir(),LENSMCP_WS_KEY,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`);function childSockPath(o){return path.join(SOCK_DIR,`child-${o}.sock`)}h(childSockPath,"childSockPath"),a(childSockPath,"childSockPath");function sockOwnerPath(o){return`${o}.owner`}h(sockOwnerPath,"sockOwnerPath"),a(sockOwnerPath,"sockOwnerPath");function claimSock(o,d=process.pid){try{fs.mkdirSync(path.dirname(o),{recursive:!0})}catch{}try{fs.writeFileSync(sockOwnerPath(o),String(d))}catch{}try{fs.unlinkSync(o)}catch{}}h(claimSock,"claimSock"),a(claimSock,"claimSock");function cleanupSock(o,d=process.pid){if(o){try{const g=fs.readFileSync(sockOwnerPath(o),"utf8").trim();if(g&&g!==String(d))return}catch{}try{fs.unlinkSync(o)}catch{}try{fs.unlinkSync(sockOwnerPath(o))}catch{}}}h(cleanupSock,"cleanupSock"),a(cleanupSock,"cleanupSock");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const resolved=path.isAbsolute(spec)?spec:path.join(__dirname,spec);return eval(`delete require.cache["${resolved}"]`),eval(`require("${resolved}")`)}h(importFresh,"importFresh"),a(importFresh,"importFresh");function debounce(o,d){let g=null;return(...f)=>{g&&clearTimeout(g),g=setTimeout(()=>o(...f),d)}}if(h(debounce,"debounce"),a(debounce,"debounce"),process.env.APP_RUNNER==="1"){const o=a(g=>{if(!inspector.url())try{inspector.open(g,"127.0.0.1",!1)}catch(m){console.warn(`[child] inspector.open(${g}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,m.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const f=inspector.url();f&&process.send&&process.send({type:"inspector-url",url:f})},"openInspector"),d=Number(process.env.CHILD_DEBUG_PORT||"");Number.isInteger(d)&&d>1024&&o(d);try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let g=null,f=!1,m=!1,H=a((p,u)=>{u.statusCode=503,u.end("starting")},"delegate");async function P(){if(f)return m=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),g;if(f=!0,process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(w,k)=>{console.error(`[child] uncaughtException (${k}):`,w)}),process.on("unhandledRejection",w=>{const k=w instanceof Error?w.stack||w.message:String(w);k.includes("FronteggContextScope")||k.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",w)}),await importFresh(BUNDLE_PATH),typeof global.createChildApp!="function")throw f=!1,new Error(`Bundle '${BUNDLE_PATH}' does not export createChildApp() and the zero-touch bridge is not active. Either install @lensmcp/node-instrumentation (a plain NestFactory.create + app.listen main.ts then just works) or set global.createChildApp in main.ts when DEVSERVER_MODE === '1'.`);const p=await global.createChildApp(),u=g;return g=p,H=p.handler,console.log("[child] Swapped to fresh app (previous closing in background)"),u&&u.close().catch(w=>console.error("[child] background close error:",w)),f=!1,m?(m=!1,P()):p}h(P,"I"),a(P,"swapNow");const B=debounce(async()=>{try{console.log("[child] /webpack/reload"),await P()}catch(p){console.error("[child] reload failed:",p)}},250),C=http.createServer((p,u)=>{if(p.method==="POST"&&p.url==="/webpack/reload"){u.writeHead(200,{"content-type":"application/json"}),u.end(JSON.stringify({ok:!0})),B();return}H(p,u)}),_=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(_),process.exit(0)});try{await P();const p=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);console.log(`[child] Using bundle: ${p}`),claimSock(_),C.listen(_,()=>{console.log(`[child] Listening on ${_}`),process.send&&process.send({type:"ready",socketPath:_})})}catch(p){console.error("[child] Startup error:",p),process.send&&process.send({type:"boot-error",error:String(p?.stack||p)}),process.exitCode=1;return}process.on("message",async p=>{if(!(!p||typeof p!="object")){if(p.type==="debug-open"){const u=Number(p.port);o(Number.isInteger(u)&&u>1024?u:0);return}if(p.type==="reload")try{await P(),process.send&&process.send({type:"reloaded",socketPath:_})}catch(u){console.error("[child] reload error:",u),process.send&&process.send({type:"reload-error",error:String(u?.stack||u)})}}}),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const x=a(async p=>{console.log(`[child ${p}] shutting down\u2026`);const u=g?.close().catch(k=>console.error("[child] close error:",k)),w=new Promise(k=>C.close(()=>k()));cleanupSock(_),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([u,w]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(p=>{process.on(p,()=>{inspector?.close?.(),x(p)})})})()}else{let o=h(function(i){x.push(i),x.length>_&&x.splice(0,x.length-_),B?.write(i.replace(C,""));for(const y of p)try{y.res.write(y.plain?i.replace(C,""):i)}catch{p.delete(y)}},"p");a(o,"emitLogLine");const d=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],g="\x1B[0m",f=Math.max(1,Number(process.env.CHILD_COUNT||1));let m=(()=>{const i=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(i)&&i>1024?i:0})();const H=20,P=process.env.LENSMCP_LOG_FILE||"",B=P?(()=>{try{return fs.mkdirSync(path.dirname(P),{recursive:!0}),fs.createWriteStream(P,{flags:"a"})}catch(i){return console.warn(`[parent] cannot open log file ${P}:`,i.message),null}})():null,C=/\x1b\[[0-9;]*m/g,_=2e3,x=[],p=new Set;for(const i of["log","info","warn","error"]){const y=console[i].bind(console);console[i]=(...E)=>{y(...E),o(E.map(A=>typeof A=="string"?A:util.inspect(A)).join(" ")+`
|
|
2
|
-
`)}}const
|
|
3
|
-
`+String(
|
|
4
|
-
`;
|
|
5
|
-
`);return}const
|
|
1
|
+
"use strict";var ge=Object.defineProperty;var g=(s,f)=>ge(s,"name",{value:f,configurable:!0});var he=Object.defineProperty,l=g((s,f)=>he(s,"name",{value:f,configurable:!0}),"l");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),node_child_process_1=require("node:child_process"),http=tslib_1.__importStar(require("node:http")),readline=tslib_1.__importStar(require("node:readline")),inspector=tslib_1.__importStar(require("node:inspector")),util=tslib_1.__importStar(require("node:util")),httpProxy=require("http-proxy"),{prettyFactory}=require("pino-pretty"),BUNDLE_PATH=process.env.BUNDLE_PATH?.trim()||"./main.js",SERVICE_NAME=process.env.SERVICE_NAME||"",SERVICE_PREFIX=process.env.SERVE_PREFIX?`/${process.env.SERVE_PREFIX}`:"",GATEWAY_MIDDLEWARE_PATH=process.env.GATEWAY_MIDDLEWARE||"",GATEWAY_CONFIG=(()=>{const s=process.env.GATEWAY_CONFIG;if(!s)return{};try{return JSON.parse(s)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const s=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof s=="function"?s:typeof s.default=="function"?s.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(s){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,s)}function applyGatewayMiddleware(s){if(gatewayMiddleware)try{gatewayMiddleware(s,GATEWAY_CONFIG)}catch(f){console.error("[gateway] Middleware error:",f)}}g(applyGatewayMiddleware,"applyGatewayMiddleware"),l(applyGatewayMiddleware,"applyGatewayMiddleware");const GATEWAY_ROUTES=(()=>{try{return JSON.parse(process.env.GATEWAY_ROUTES||"[]")}catch{return[]}})(),GATEWAY_HTTPS=process.env.GATEWAY_HTTPS==="1";function hostMatches(s,f){if(s.startsWith("*.")){const S=s.slice(1);return f.endsWith(S)||f===s.slice(2)}return f===s}g(hostMatches,"hostMatches"),l(hostMatches,"hostMatches");function matchGatewayRoute(s){if(GATEWAY_ROUTES.length===0)return;const f=(s.headers.host||"").split(":")[0],S=s.url||"/";for(const y of GATEWAY_ROUTES)if(!(y.host&&!hostMatches(y.host,f))&&!(y.prefix&&!S.startsWith(y.prefix)))return y}g(matchGatewayRoute,"matchGatewayRoute"),l(matchGatewayRoute,"matchGatewayRoute");const WORKER_NAMES=(()=>{try{return JSON.parse(process.env.WORKERS||"[]")}catch{return[]}})(),LENSMCP_WS_KEY=process.env.LENSMCP_WS_KEY||"",SOCK_DIR=LENSMCP_WS_KEY?path.join(os.tmpdir(),LENSMCP_WS_KEY,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`),POD_GENERATION=(()=>{const s=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(s)&&s>1?Math.floor(s):1})();function childSockPath(s){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${s}-g${POD_GENERATION}.sock`:`child-${s}.sock`)}g(childSockPath,"childSockPath"),l(childSockPath,"childSockPath");function sockOwnerPath(s){return`${s}.owner`}g(sockOwnerPath,"sockOwnerPath"),l(sockOwnerPath,"sockOwnerPath");function claimSock(s,f=process.pid){try{fs.mkdirSync(path.dirname(s),{recursive:!0})}catch{}try{fs.writeFileSync(sockOwnerPath(s),String(f))}catch{}try{fs.unlinkSync(s)}catch{}}g(claimSock,"claimSock"),l(claimSock,"claimSock");function cleanupSock(s,f=process.pid){if(s){try{const S=fs.readFileSync(sockOwnerPath(s),"utf8").trim();if(S&&S!==String(f))return}catch{}try{fs.unlinkSync(s)}catch{}try{fs.unlinkSync(sockOwnerPath(s))}catch{}}}g(cleanupSock,"cleanupSock"),l(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const s=process.env.LENSMCP_SERVICE_KEY;if(!s)return;const{createHash:f}=require("node:crypto");return f("sha256").update(s).digest("hex").slice(0,8)}g(serviceKeyFingerprint,"serviceKeyFingerprint"),l(serviceKeyFingerprint,"serviceKeyFingerprint");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const resolved=path.isAbsolute(spec)?spec:path.join(__dirname,spec);return eval(`delete require.cache["${resolved}"]`),eval(`require("${resolved}")`)}g(importFresh,"importFresh"),l(importFresh,"importFresh");function debounce(s,f){let S=null;return(...y)=>{S&&clearTimeout(S),S=setTimeout(()=>s(...y),f)}}if(g(debounce,"debounce"),l(debounce,"debounce"),process.env.APP_RUNNER==="1"){const s=l(S=>{if(!inspector.url())try{inspector.open(S,"127.0.0.1",!1)}catch(_){console.warn(`[child] inspector.open(${S}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,_.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const y=inspector.url();y&&process.send&&process.send({type:"inspector-url",url:y})},"openInspector"),f=Number(process.env.CHILD_DEBUG_PORT||"");Number.isInteger(f)&&f>1024&&s(f);try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let S=null,y=!1,_,x=!1,k={app:null,handler:l((r,n)=>{n.statusCode=503,n.end("starting")},"handler"),inflight:0};const M=(()=>{const r=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(r)&&r>=0?r:15*6e4})(),O=l(r=>{const n=globalThis.__lensmcpActiveWork;if(typeof n!="function")return{count:0};try{const u=n(r);return{count:Number(u?.count??0),...u?.oldestStartedAt?{oldestStartedAt:u.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),G=(()=>{const r=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(r)&&r>=1?Math.floor(r):1})(),P=new Set;async function v(r,n){if(!r.closed&&(r.closed=!0,P.delete(r),n&&console.error(`[child] ${n}`),!!r.app))try{await r.app.close()}catch(u){console.error("[child] error closing a previous app:",u)}}g(v,"G"),l(v,"closeGeneration");async function F(r){for(;P.size>=G;){const h=P.values().next().value;if(!h)break;await v(h,`closing a still-draining generation early \u2014 ${P.size} were pending and the cap is ${G} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}P.add(r);const n=Date.now(),u=l(()=>({inflight:r.inflight,work:O(r.workGen).count}),"busy");for(;M>0&&Date.now()-n<M;){if(r.closed)return;const h=u();if(h.inflight===0&&h.work===0)break;await new Promise(m=>{setTimeout(m,250).unref?.()})}if(r.closed)return;const E=u();E.inflight>0||E.work>0?console.error(`[child] swap drain expired after ${Date.now()-n}ms \u2014 closing the previous app with ${E.inflight} in-flight request(s) and ${E.work} background job(s) still running. A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${M}ms) if this is legitimate work.`):Date.now()-n>250&&console.log(`[child] previous app drained in ${Date.now()-n}ms \u2014 closing it`),await v(r)}g(F,"V"),l(F,"drainThenClose");async function I(){if(y)return x=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),S;y=!0,process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(h,m)=>{console.error(`[child] uncaughtException (${m}):`,h)}),process.on("unhandledRejection",h=>{const m=h instanceof Error?h.stack||h.message:String(h);m.includes("FronteggContextScope")||m.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",h)});try{await importFresh(BUNDLE_PATH)}catch(h){throw y=!1,h}if(typeof global.createChildApp!="function")throw y=!1,new Error(`Bundle '${BUNDLE_PATH}' does not export createChildApp() and the zero-touch bridge is not active. Either install @lensmcp/node-instrumentation (a plain NestFactory.create + app.listen main.ts then just works) or set global.createChildApp in main.ts when DEVSERVER_MODE === '1'.`);const r=global.__lensmcpBeginWorkGeneration,n=typeof r=="function"?r():void 0;let u;try{u=await global.createChildApp()}catch(h){throw y=!1,h}const E=k;return S=u,k={app:u,handler:u.handler,inflight:0,...n===void 0?{}:{workGen:n}},console.log("[child] Swapped to fresh app (previous draining in background)"),E.app&&F(E).catch(h=>console.error("[child] background close error:",h)),y=!1,x?(x=!1,I()):u}g(I,"O"),l(I,"swapNow");const J=debounce(async()=>{try{console.log("[child] /webpack/reload"),await I()}catch(r){console.error("[child] reload failed:",r)}},250),W=http.createServer((r,n)=>{if(r.method==="POST"&&r.url==="/webpack/reload"){n.writeHead(200,{"content-type":"application/json"}),n.end(JSON.stringify({ok:!0})),J();return}if(r.method==="GET"&&r.url==="/__lensmcp/alive"){const E=r.headers["x-internal-token"],h=process.env.LENSMCP_SERVICE_KEY;if(h&&E!==h){n.writeHead(404,{"content-type":"text/plain"}),n.end("not found");return}const m=O();n.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),n.end(JSON.stringify({ok:!0,pid:process.pid,swapping:y,inflight:k.inflight,oldestInflightAt:k.oldestAt,activeWork:m.count,oldestWorkAt:m.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),..._?{watcher:_}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const u=k;u.inflight+=1,u.oldestAt===void 0&&(u.oldestAt=Date.now()),n.on("close",()=>{u.inflight-=1,u.inflight===0&&(u.oldestAt=void 0)}),u.handler(r,n)}),A=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(A),process.exit(0)});try{await I();const r=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);console.log(`[child] Using bundle: ${r}`),claimSock(A),W.listen(A,()=>{console.log(`[child] Listening on ${A}`),process.send&&process.send({type:"ready",socketPath:A})})}catch(r){console.error("[child] Startup error:",r),process.send&&process.send({type:"boot-error",error:String(r?.stack||r)}),process.exitCode=1;return}process.on("message",async r=>{if(!(!r||typeof r!="object")){if(r.type==="debug-open"){const n=Number(r.port);s(Number.isInteger(n)&&n>1024?n:0);return}if(r.type==="watcher"){const n=r.testimony;n&&typeof n=="object"&&(_=n);return}if(r.type==="reload")try{await I(),process.send&&process.send({type:"reloaded",socketPath:A})}catch(n){console.error("[child] reload error:",n),process.send&&process.send({type:"reload-error",error:String(n?.stack||n)})}}}),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const d=l(async r=>{console.log(`[child ${r}] shutting down\u2026`);const n=S?.close().catch(E=>console.error("[child] close error:",E)),u=new Promise(E=>W.close(()=>E()));cleanupSock(A),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([n,u]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(r=>{process.on(r,()=>{inspector?.close?.(),d(r)})})})()}else{let s=g(function(d){P.push(d),P.length>G&&P.splice(0,P.length-G),M?.write(d.replace(O,""));for(const r of v)try{r.res.write(r.plain?d.replace(O,""):d)}catch{v.delete(r)}},"G");l(s,"emitLogLine");const f=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],S="\x1B[0m",y=Math.max(1,Number(process.env.CHILD_COUNT||1));let _=(()=>{const d=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(d)&&d>1024?d:0})();const x=20,k=process.env.LENSMCP_LOG_FILE||"",M=k?(()=>{try{return fs.mkdirSync(path.dirname(k),{recursive:!0}),fs.createWriteStream(k,{flags:"a"})}catch(d){return console.warn(`[parent] cannot open log file ${k}:`,d.message),null}})():null,O=/\x1b\[[0-9;]*m/g,G=2e3,P=[],v=new Set;for(const d of["log","info","warn","error"]){const r=console[d].bind(console);console[d]=(...n)=>{r(...n),s(n.map(u=>typeof u=="string"?u:util.inspect(u)).join(" ")+`
|
|
2
|
+
`)}}const F=l(d=>f[(d-1)%f.length],"colorFor"),I=l(d=>{if(y===1)return"";const r=`[child#${d}] `;return`${F(d)}${r}${S}`},"tagFor"),J=l(d=>d.startsWith("{")&&d.endsWith("}"),"isJsonLog"),W=l((d,r)=>{const n=prettyFactory({sync:!0,colorize:!0,crlf:!0,messageKey:"message",errorLikeObjectKeys:["err","error"],errorProps:"type,message,stack",ignore:["logContext","context","hostname","req","res","err.driverError","module","cloudEnvironment","frontegg-application-id","frontegg-tenant-id","frontegg-trace-id","frontegg-vendor-id","host","service","version","err","error"].join(","),messageFormat:"{if module}[{module}] {end}{if context}[{context}] {end}{if logContext}[{logContext}] {end}{message}",customPrettifiers:{stack:l(h=>`
|
|
3
|
+
`+String(h),"stack")}}),u=l((h,m)=>{const b=h==="stdout"?process.stdout:process.stderr,T=J(m)?n(m):`${m}
|
|
4
|
+
`;b.write(`${r}${T}`),s(`${r}${T}`)},"write"),E=l((h,m)=>{if(!h)return;const b=readline.createInterface({input:h});b.on("line",T=>u(m,T)),b.on("close",()=>{})},"attach");E(d.stdout,"stdout"),E(d.stderr,"stderr")},"wireProcLogging"),A=l(d=>W(d.proc,I(d.id)),"wireChildLogging");(async()=>{fs.mkdirSync(SOCK_DIR,{recursive:!0});const d=httpProxy.createProxyServer({});let r=Number(process.env.PORT)||0;r||(r=9090);const n=[],u=new Set;function E(){let e=0;for(;u.has(e);)e++;return u.add(e),e}g(E,"E"),l(E,"allocDebugSlot");let h=1,m=0,b=!1,T=y,D=[];function X(){return n.filter(e=>e.healthy&&typeof e.socketPath=="string")}g(X,"q"),l(X,"healthyChildren");let j;function z(e){if(!e||typeof e!="object")return;const t=e;if(t.schemaVersion===1){j=t;for(const p of n)try{p.proc.send?.({type:"watcher",testimony:t})}catch{}}}g(z,"ee"),l(z,"recordWatcherTestimony");function Q(e,t=256*1024){return new Promise(p=>{let c="",a=!1;e.on("data",o=>{a||(c+=o,c.length>t&&(a=!0))}),e.on("end",()=>{if(a||!c)return p(void 0);try{p(JSON.parse(c))}catch{p(void 0)}}),e.on("error",()=>p(void 0))})}g(Q,"te"),l(Q,"readJsonBody");function L(){const e=X();if(e.length===0)return null;const t=m%e.length;return m=(m+1)%e.length,e[t]}g(L,"F"),l(L,"pickChild");async function Y(){const e=h++,t=childSockPath(e);cleanupSock(t);const p=["--enable-source-maps"];__filename.endsWith(".ts")&&p.unshift("--require","@swc-node/register");const c=_?E():-1,a=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:t,...c>=0&&{CHILD_DEBUG_PORT:String(_+c)}},stdio:["inherit","pipe","pipe","ipc"],execArgv:p}),o={id:e,proc:a,healthy:!1,lastError:null,debugSlot:c};return a.on("message",w=>{if(!w||typeof w!="object")return;const i=w;if(i.type==="ready"){if(o.socketPath=String(i.socketPath),o.healthy=!0,console.log(`[parent] Child#${o.id} ready on socket ${o.socketPath}`),j)try{a.send?.({type:"watcher",testimony:j})}catch{}}else i.type==="inspector-url"?(o.inspectorUrl=String(i.url),console.log(`[parent] Child#${o.id} Debugger listening on ${i.url}`)):i.type==="reloaded"?(o.socketPath=String(i.socketPath),o.healthy=!0,console.log(`[parent] Child#${o.id} hot-swapped on socket ${o.socketPath}`)):i.type==="reload-error"?(o.lastError=String(i.error||"unknown reload error"),console.error(`[parent] Child#${o.id} reload error: ${o.lastError}`),o.healthy=!1):i.type==="boot-error"&&(o.lastError=String(i.error||"unknown boot error"),console.error(`[parent] Child#${o.id} boot error: ${o.lastError}`),o.healthy=!1)}),a.on("exit",(w,i)=>{o.debugSlot>=0&&(u.delete(o.debugSlot),o.debugSlot=-1);const N=i==="SIGINT"||i==="SIGTERM"||w===0;if(N?console.log(`[parent] Child#${o.id} exited cleanly (code=${w}, signal=${i??"none"})`):(console.error(`[parent] Child#${o.id} crashed (code=${w}, signal=${i??"none"})`),o.lastError||(o.lastError=`Child exited abnormally (code=${w}, signal=${i??"none"})`)),o.healthy=!1,cleanupSock(o.socketPath,o.proc.pid),o.socketPath=void 0,!N&&!b){const $=n.indexOf(o);$>=0&&n.splice($,1);const pe=Date.now();if(D=D.filter(V=>pe-V<3e4),D.length>=5){console.error(`[parent] crash-loop detected (${D.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}D.push(pe),setTimeout(()=>{const V=n.filter(fe=>fe.proc.killed===!1).length;!b&&V<T&&(console.log(`[parent] respawning a crashed pod (${V}/${T} live)`),Y().catch(()=>{}))},1500).unref?.()}}),n.push(o),A(o),o}g(Y,"J"),l(Y,"spawnChild");async function Z(e){T=Math.max(T,e);const t=n.filter(c=>c.proc.killed===!1),p=e-t.length;for(let c=0;c<p;c++)await Y()}g(Z,"re"),l(Z,"ensurePoolSize");async function ne(){const e=n.filter(t=>!t.healthy||typeof t.socketPath!="string");await Promise.all(e.map(async t=>{try{t.proc.kill("SIGTERM")}catch{}cleanupSock(t.socketPath,t.proc.pid),t.socketPath=void 0;const p=n.indexOf(t);p>=0&&n.splice(p,1),await Y()}))}g(ne,"ae"),l(ne,"respawnUnhealthy");const R=[];function ee(e){const t=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),p=path.join(t,`${e}.js`);if(!fs.existsSync(p))return console.error(`[parent] Worker "${e}" bundle not found: ${p}`),null;const c=["--enable-source-maps"];__filename.endsWith(".ts")&&c.unshift("--require","@swc-node/register");try{c.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const a=(_||9229)+x+Math.max(0,WORKER_NAMES.indexOf(e));c.push(_?`--inspect=127.0.0.1:${a}`:`--inspect-port=127.0.0.1:${a}`);const o=(0,node_child_process_1.fork)(p,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:c}),w={name:e,proc:o,debugPort:a};return R.push(w),W(o,`\x1B[35m[worker:${e}] \x1B[0m`),o.on("exit",(i,N)=>{N==="SIGINT"||N==="SIGTERM"||i===0||console.error(`[parent] Worker "${e}" crashed (code=${i}, signal=${N??"none"})`);const $=R.indexOf(w);$>=0&&R.splice($,1)}),console.log(`[parent] Worker "${e}" started (pid=${o.pid})`),w}g(ee,"oe"),l(ee,"spawnWorkerProcess");function se(){for(const e of[...R])try{e.proc.kill("SIGTERM")}catch{}R.length=0;for(const e of WORKER_NAMES)ee(e)}g(se,"le"),l(se,"restartWorkers");const de=debounce(async()=>{const e=X();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const t of e)t.proc.send?.({type:"reload"})}await ne(),WORKER_NAMES.length>0&&se()},200),H=l(async(e,t)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0})),Q(e).then(a=>z(a?.watcher)),de();return}if(e.method==="POST"&&e.url==="/webpack/watching"){const a=await Q(e);z(a),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,relayed:j!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){let a="";e.on("data",o=>{a+=o}),e.on("end",async()=>{let o=0;try{o=Number(JSON.parse(a||"{}").pods)||0}catch{}o=Math.max(1,Math.min(o,16)),await Z(o),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,pods:o}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const a=new URL(e.url,"http://localhost").searchParams,o=Math.min(Math.max(Number(a.get("tail")??"100")||0,0),G),w=a.get("follow")!=="0",i=a.get("plain")==="1";t.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const N=o===0?"":P.slice(-o).join("");if(t.write(i?N.replace(O,""):N),!w){t.end();return}const $={res:t,plain:i};v.add($),e.on("close",()=>v.delete($));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){const a=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(a)&&a>1024&&(_=a),_||(_=9229);const o=n.filter(i=>i.proc.exitCode===null&&i.proc.signalCode===null);for(const i of o){i.debugSlot<0&&(i.debugSlot=E());try{i.proc.send?.({type:"debug-open",port:_+i.debugSlot})}catch{}}for(const i of R)try{i.proc.kill("SIGUSR1")}catch{}const w=Date.now()+2e3;for(;Date.now()<w&&o.some(i=>!i.inspectorUrl);)await new Promise(i=>setTimeout(i,50));t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,basePort:_,pods:o.map(i=>({id:i.id,inspectorUrl:i.inspectorUrl??null})),workers:R.map(i=>({name:i.name,port:i.debugPort}))},null,2)+`
|
|
5
|
+
`);return}const p=matchGatewayRoute(e);if(p?.prependPrefix&&!e.url?.startsWith(p.prependPrefix)&&(e.url=p.prependPrefix+(e.url??"/")),p?.target){applyGatewayMiddleware(e),d.web(e,t,{target:p.target,autoRewrite:typeof p.target=="string",changeOrigin:!0},a=>{console.error(`[parent] proxy error to ${p.target}:`,a),t.statusCode=502,t.setHeader("content-type","text/plain; charset=utf-8"),t.end(`Upstream error from ${p.target}.`)});return}if(SERVICE_PREFIX){if(!e.url?.startsWith(SERVICE_PREFIX)){t.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),t.end(`Not found. This service is mounted at ${SERVICE_PREFIX}/`);return}e.url=e.url.slice(SERVICE_PREFIX.length)||"/"}applyGatewayMiddleware(e);const c=L();if(!c){t.statusCode=503,t.setHeader("content-type","text/plain; charset=utf-8");const a=n.map(o=>`#${o.id}: ${o.lastError??"no error recorded"}`).join(`
|
|
6
6
|
`);t.end(`No healthy children available. POST /webpack/reload to recover.
|
|
7
7
|
|
|
8
8
|
Last errors:
|
|
9
|
-
${
|
|
10
|
-
${
|
|
9
|
+
${a}`);return}d.web(e,t,{target:{socketPath:c.socketPath},headers:{"x-proxy-child-id":String(c.id)}},a=>{console.error(`[parent] proxy error to Child#${c.id}:`,a),c.healthy=!1,c.lastError=String(a?.stack||a),t.statusCode=502,t.setHeader("content-type","text/plain; charset=utf-8"),t.end(`Upstream error from Child#${c.id}. Try /webpack/reload.
|
|
10
|
+
${c.lastError}`)})},"requestHandler");let C,U,te;const q=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),t=GATEWAY_ROUTES.map(c=>c.host).filter(c=>!!c),p=path.join(process.cwd(),"node_modules",".cache","davnx-webpack");U=e.getCertificateSync(p,SERVICE_NAME||"lensmcp.dev",t),te=e.caCertPath(),C=require("node:https").createServer({key:U,cert:U},H)}else C=http.createServer(H);const ce=l((e,t,p)=>{const c=matchGatewayRoute(e);if(c?.prependPrefix&&!e.url?.startsWith(c.prependPrefix)&&(e.url=c.prependPrefix+(e.url??"/")),c?.target){d.ws(e,t,p,{target:c.target,changeOrigin:!0},()=>t.destroy());return}const a=L();if(!a){t.destroy();return}d.ws(e,t,p,{target:{socketPath:a.socketPath}},()=>t.destroy())},"upgradeHandler");C.on("upgrade",ce);const ae=2e3;let ie=!1;C.on("error",e=>{if(e.code==="EADDRINUSE"){ie||(ie=!0,console.warn(`[parent] public port ${r} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${ae/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!b&&!C.listening&&C.listen(r)},ae).unref?.();return}console.error("[parent] public server error:",e)}),await Z(y);for(const e of WORKER_NAMES)ee(e);C.listen(r,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${r}`),SERVICE_PREFIX&&console.log(`[parent] Service prefix: "${SERVICE_PREFIX}" (enforced \u2014 requests without it will get 404)`),gatewayMiddleware&&console.log(`[parent] Gateway middleware: ACTIVE (${GATEWAY_MIDDLEWARE_PATH})`),GATEWAY_ROUTES.length>0&&console.log(`[parent] Gateway routes: ${GATEWAY_ROUTES.map(t=>`${t.host??"*"}${t.prefix??"/"}\u2192${t.target??"children"}`).join(" ")}`),te&&(console.log("[parent] HTTPS dev CA (trust ONCE, then rotations/new hostnames stay green):"),console.log(`[parent] sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${te}`)),console.log(`POST ${e}://localhost:${r}/webpack/reload to trigger rolling swap/respawn`)});const B=Number(process.env.INTERNAL_PORT||0);if(B>0){const e=l((p,c)=>{const a=L();if(!a){c.statusCode=503,c.setHeader("content-type","text/plain; charset=utf-8"),c.end("No healthy children available.");return}d.web(p,c,{target:{socketPath:a.socketPath}},o=>{a.healthy=!1,a.lastError=String(o?.stack||o),c.statusCode=502,c.end(`Upstream error from Child#${a.id}.`)})},"internalHandler"),t=http.createServer(e);t.on("upgrade",(p,c,a)=>{const o=L();if(!o){c.destroy();return}d.ws(p,c,a,{target:{socketPath:o.socketPath}},()=>c.destroy())}),t.on("error",p=>{console.warn(`[parent] internal port ${B} unavailable (${p.code}).`)}),t.listen(B,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${B}`)}),q.push(t)}const ue=(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of ue){if(!Number.isInteger(e)||e===Number(r))continue;const t=GATEWAY_HTTPS?require("node:https").createServer({key:U,cert:U},H):http.createServer(H);t.on("upgrade",ce),t.on("error",p=>{console.warn(`[parent] extra port ${e} unavailable (${p.code}) \u2014 main port still up.`)}),t.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),q.push(t)}const re=path.join(SOCK_DIR,"parent.sock");claimSock(re);const oe=http.createServer(H);oe.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),oe.listen(re),q.push(oe);const le=l(async e=>{b=!0,console.log(`[${e}] [parent] shutting down\u2026`),cleanupSock(re);for(const t of q)try{t.close()}catch{}for(const t of R)try{t.proc.kill("SIGTERM")}catch{}for(const t of n){try{t.proc.kill("SIGTERM")}catch{}t.socketPath&&cleanupSock(t.socketPath,t.proc.pid)}try{fs.rmdirSync(SOCK_DIR)}catch{}setTimeout(()=>process.exit(0),700).unref()},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(e=>process.on(e,()=>{le(e)}));const K=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(K)&&K>0){const e=setInterval(()=>{try{process.kill(K,0)}catch(t){if(t.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${K}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),le("gateway-gone")}},5e3);e.unref()}})()}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/cluster",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.4",
|
|
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.21.
|
|
61
|
-
"@lensmcp/node-instrumentation": "1.21.
|
|
62
|
-
"@lensmcp/nx-plugin": "1.21.
|
|
60
|
+
"@lensmcp/core": "1.21.4",
|
|
61
|
+
"@lensmcp/node-instrumentation": "1.21.4",
|
|
62
|
+
"@lensmcp/nx-plugin": "1.21.4",
|
|
63
63
|
"fastify": "^5.0.0",
|
|
64
64
|
"@fastify/reply-from": "^12.0.0",
|
|
65
65
|
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
|