@lensmcp/cluster 1.23.13 → 1.24.0

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.
Files changed (105) hide show
  1. package/build-host.d.ts +1 -0
  2. package/build-host.js +1 -0
  3. package/create-webpack-dev.d.ts +74 -40
  4. package/create-webpack-dev.js +1 -1
  5. package/dev-devtool.d.ts +45 -0
  6. package/dev-devtool.js +1 -0
  7. package/devserver-notifier.d.ts +57 -0
  8. package/devserver-notifier.js +1 -0
  9. package/executors/gateway/body-stall.d.ts +13 -1
  10. package/executors/gateway/body-stall.js +1 -1
  11. package/executors/gateway/canonical-path.d.ts +1 -1
  12. package/executors/gateway/client-ip.d.ts +1 -1
  13. package/executors/gateway/gateway.impl.js +1 -1
  14. package/executors/gateway/jwt-claims.d.ts +1 -1
  15. package/executors/gateway/prod-config.d.ts +1 -1
  16. package/executors/gateway/prod-runtime/body-stall.d.ts +1 -1
  17. package/executors/gateway/rate-limit.d.ts +1 -1
  18. package/executors/gateway/runtime/auth.d.ts +1 -1
  19. package/executors/gateway/runtime/chooser.d.ts +2 -2
  20. package/executors/gateway/runtime/control.d.ts +3 -3
  21. package/executors/gateway/runtime/control.js +2 -2
  22. package/executors/gateway/runtime/dev-auth.d.ts +1 -1
  23. package/executors/gateway/runtime/edge.d.ts +3 -3
  24. package/executors/gateway/runtime/guest-graph.d.ts +113 -0
  25. package/executors/gateway/runtime/guest-graph.js +2 -0
  26. package/executors/gateway/runtime/lens-children.d.ts +11 -1
  27. package/executors/gateway/runtime/lens-children.js +1 -1
  28. package/executors/gateway/runtime/lifecycle.d.ts +54 -8
  29. package/executors/gateway/runtime/lifecycle.js +3 -3
  30. package/executors/gateway/runtime/process-names.d.ts +18 -0
  31. package/executors/gateway/runtime/process-names.js +1 -0
  32. package/executors/gateway/runtime/project-graph-store.d.ts +74 -0
  33. package/executors/gateway/runtime/project-graph-store.js +1 -0
  34. package/executors/gateway/runtime/proxy.d.ts +2 -2
  35. package/executors/gateway/runtime/proxy.js +1 -1
  36. package/executors/gateway/runtime/scope.d.ts +1 -1
  37. package/executors/gateway/runtime/serve-launch.d.ts +118 -0
  38. package/executors/gateway/runtime/serve-launch.js +3 -0
  39. package/executors/gateway/runtime/server.d.ts +3 -3
  40. package/executors/gateway/runtime/server.js +2 -2
  41. package/executors/gateway/runtime/shared-build.d.ts +65 -0
  42. package/executors/gateway/runtime/shared-build.js +4 -0
  43. package/executors/gateway/runtime/types.d.ts +35 -9
  44. package/executors/gateway/runtime/types.js +1 -1
  45. package/executors/gateway/runtime/workspace-log.js +1 -1
  46. package/executors/gateway/schema.d.ts +3 -0
  47. package/executors/gateway/schema.json +4 -0
  48. package/executors/serve/schema.d.ts +2 -0
  49. package/executors/serve/schema.json +4 -0
  50. package/executors/serve/serve.impl.d.ts +12 -6
  51. package/executors/serve/serve.impl.js +1 -1
  52. package/executors/trust/hostname.d.ts +1 -1
  53. package/exit-reason.d.ts +1 -1
  54. package/fork-ts-check-reporter.d.ts +12 -0
  55. package/fork-ts-check-reporter.js +1 -1
  56. package/graph-snapshot.d.ts +1 -0
  57. package/graph-snapshot.js +1 -0
  58. package/heap-snapshot.d.ts +66 -0
  59. package/heap-snapshot.js +1 -0
  60. package/hot-swap-registries.d.ts +1 -1
  61. package/inspector-secret.d.ts +39 -0
  62. package/inspector-secret.js +1 -0
  63. package/main.devserver.d.ts +33 -5
  64. package/main.devserver.js +11 -9
  65. package/package.json +4 -4
  66. package/perf/heavy-barrel-import.d.ts +135 -0
  67. package/perf/heavy-barrel-import.js +1 -0
  68. package/perf/perf-config.d.ts +29 -0
  69. package/perf/perf-config.js +1 -0
  70. package/perf/perf-rule-events.d.ts +71 -0
  71. package/perf/perf-rule-events.js +2 -0
  72. package/perf/perf-rules-analysis.d.ts +28 -0
  73. package/perf/perf-rules-analysis.js +1 -0
  74. package/perf/perf-rules-plugin.d.ts +71 -0
  75. package/perf/perf-rules-plugin.js +1 -0
  76. package/perf/perf-rules-worker.d.ts +1 -0
  77. package/perf/perf-rules-worker.js +1 -0
  78. package/perf/source-scan.d.ts +59 -0
  79. package/perf/source-scan.js +5 -0
  80. package/perf/workspace-libraries.d.ts +52 -0
  81. package/perf/workspace-libraries.js +2 -0
  82. package/pod-memory-events.d.ts +71 -0
  83. package/pod-memory-events.js +2 -0
  84. package/pod-memory.d.ts +258 -0
  85. package/pod-memory.js +1 -0
  86. package/process-name.d.ts +45 -0
  87. package/process-name.js +1 -1
  88. package/serve-runner.d.ts +2 -0
  89. package/serve-runner.js +1 -0
  90. package/shared-build/attach.d.ts +56 -0
  91. package/shared-build/attach.js +3 -0
  92. package/shared-build/host.d.ts +52 -0
  93. package/shared-build/host.js +4 -0
  94. package/shared-build/protocol.d.ts +118 -0
  95. package/shared-build/protocol.js +1 -0
  96. package/shared-build/shared-compilation.d.ts +91 -0
  97. package/shared-build/shared-compilation.js +3 -0
  98. package/shared-build/signature.d.ts +52 -0
  99. package/shared-build/signature.js +1 -0
  100. package/stdio-guard.d.ts +1 -1
  101. package/swap-recycle.d.ts +21 -0
  102. package/swap-recycle.js +1 -1
  103. package/tsgo-check-plugin.d.ts +1 -1
  104. package/workspace-paths.d.ts +76 -0
  105. package/workspace-paths.js +1 -0
package/main.devserver.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var Oe=Object.defineProperty;var h=(o,p)=>Oe(o,"name",{value:p,configurable:!0});var Ce=Object.defineProperty,s=h((o,p)=>Ce(o,"name",{value:p,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DEBUG_FIXED_PORTS_ENV=void 0,exports.isOwnerOnlyTransport=isOwnerOnlyTransport,exports.gatewayManaged=gatewayManaged,exports.mayDriveThePool=mayDriveThePool,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor,exports.redactInspectorUrls=redactInspectorUrls,exports.inspectorPortOf=inspectorPortOf;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")),v8=tslib_1.__importStar(require("node:v8")),vm=tslib_1.__importStar(require("node:vm")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),schema_first_instance_1=require("./schema-first-instance"),redis_disconnect_fix_1=require("./redis-disconnect-fix"),error_stack_release_1=require("./error-stack-release"),swap_recycle_1=require("./swap-recycle"),stdio_guard_1=require("./stdio-guard"),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(p){console.error("[gateway] Middleware error:",p)}}h(applyGatewayMiddleware,"applyGatewayMiddleware"),s(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,p){if(o.startsWith("*.")){const g=o.slice(1);return p.endsWith(g)||p===o.slice(2)}return p===o}h(hostMatches,"hostMatches"),s(hostMatches,"hostMatches");function matchGatewayRoute(o){if(GATEWAY_ROUTES.length===0)return;const p=(o.headers.host||"").split(":")[0],g=o.url||"/";for(const _ of GATEWAY_ROUTES)if(!(_.host&&!hostMatches(_.host,p))&&!(_.prefix&&!g.startsWith(_.prefix)))return _}h(matchGatewayRoute,"matchGatewayRoute"),s(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 o=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(o)&&o>1?Math.floor(o):1})();function childSockPath(o){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${o}-g${POD_GENERATION}.sock`:`child-${o}.sock`)}h(childSockPath,"childSockPath"),s(childSockPath,"childSockPath");function sockOwnerPath(o){return`${o}.owner`}h(sockOwnerPath,"sockOwnerPath"),s(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(o){return`${o}.retiring`}h(sockRetiringPath,"sockRetiringPath"),s(sockRetiringPath,"sockRetiringPath");function isOwnerOnlyTransport(o){try{return typeof o.socket?.server?.address?.()=="string"}catch{return!1}}h(isOwnerOnlyTransport,"isOwnerOnlyTransport"),s(isOwnerOnlyTransport,"isOwnerOnlyTransport");function gatewayManaged(o=process.env){return Number(o.LENSMCP_GATEWAY_PID??0)>0}h(gatewayManaged,"gatewayManaged"),s(gatewayManaged,"gatewayManaged");function mayDriveThePool(o,p=gatewayManaged()){return!p||isOwnerOnlyTransport(o)}h(mayDriveThePool,"mayDriveThePool"),s(mayDriveThePool,"mayDriveThePool");function refusePoolCommand(o,p){console.warn(`[devserver] refused ${o.method} ${o.url} on the public port \u2014 under a gateway the pool is driven on the control socket only.`),p.writeHead(404,{"content-type":"application/json"}),p.end(JSON.stringify({error:"not found"}))}h(refusePoolCommand,"refusePoolCommand"),s(refusePoolCommand,"refusePoolCommand"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(o=process.env){const p=(o[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return p==="1"||p==="true"||p==="yes"||p==="on"}h(usesFixedInspectorPorts,"usesFixedInspectorPorts"),s(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(o,p,g=process.env){if(!usesFixedInspectorPorts(g))return 0;const _=Math.trunc(o)+Math.trunc(p);return Number.isInteger(_)&&_>1024&&_<=65535?_:0}h(inspectorPortFor,"inspectorPortFor"),s(inspectorPortFor,"inspectorPortFor");const INSPECTOR_TOKEN_RE=/((?:wss?:\/\/[^\s/]+\/|ws=[^\s/]+\/))([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi;function redactInspectorUrls(o){return o.replace(INSPECTOR_TOKEN_RE,"$1<inspector-token-redacted>")}h(redactInspectorUrls,"redactInspectorUrls"),s(redactInspectorUrls,"redactInspectorUrls");function inspectorPortOf(o){const p=/^wss?:\/\/[^/]*?:(\d+)\//.exec(o??""),g=p?Number(p[1]):NaN;return Number.isInteger(g)&&g>0?g:null}h(inspectorPortOf,"inspectorPortOf"),s(inspectorPortOf,"inspectorPortOf");function ensureOwnerOnlyDir(o){try{fs.mkdirSync(o,{recursive:!0,mode:448})}catch{}try{const p=fs.lstatSync(o);p.isDirectory()&&(p.mode&63)!==0&&fs.chmodSync(o,448)}catch{}}h(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),s(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(o,p=process.pid){ensureOwnerOnlyDir(path.dirname(o));try{const g=sockOwnerPath(o);try{fs.unlinkSync(g)}catch{}fs.writeFileSync(g,String(p),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(o)}catch{}try{fs.unlinkSync(sockRetiringPath(o))}catch{}}h(claimSock,"claimSock"),s(claimSock,"claimSock");function cleanupSock(o,p=process.pid){if(o){try{const g=fs.readFileSync(sockOwnerPath(o),"utf8").trim();if(g&&g!==String(p))return}catch{}try{fs.unlinkSync(o)}catch{}try{fs.unlinkSync(sockOwnerPath(o))}catch{}try{fs.unlinkSync(sockRetiringPath(o))}catch{}}}h(cleanupSock,"cleanupSock"),s(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const o=process.env.LENSMCP_SERVICE_KEY;if(!o)return;const{createHash:p}=require("node:crypto");return p("sha256").update(o).digest("hex").slice(0,8)}h(serviceKeyFingerprint,"serviceKeyFingerprint"),s(serviceKeyFingerprint,"serviceKeyFingerprint");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const requested=path.isAbsolute(spec)?spec:path.join(__dirname,spec);let resolved=requested;try{resolved=fs.realpathSync(requested)}catch{}return eval(`(() => {
1
+ "use strict";var je=Object.defineProperty;var h=(s,u)=>je(s,"name",{value:u,configurable:!0});var Be=Object.defineProperty,n=h((s,u)=>Be(s,"name",{value:u,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactInspectorUrls=exports.DEBUG_FIXED_PORTS_ENV=void 0,exports.isOwnerOnlyTransport=isOwnerOnlyTransport,exports.tell=tell,exports.gatewayManaged=gatewayManaged,exports.mayDriveThePool=mayDriveThePool,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor,exports.inspectorPortOf=inspectorPortOf;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")),v8=tslib_1.__importStar(require("node:v8")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),inspector_secret_1=require("./inspector-secret");Object.defineProperty(exports,"redactInspectorUrls",{enumerable:!0,get:n(function(){return inspector_secret_1.redactInspectorUrls},"get")});const schema_first_instance_1=require("./schema-first-instance"),redis_disconnect_fix_1=require("./redis-disconnect-fix"),error_stack_release_1=require("./error-stack-release"),swap_recycle_1=require("./swap-recycle"),stdio_guard_1=require("./stdio-guard"),dev_devtool_1=require("./dev-devtool"),pod_memory_1=require("./pod-memory"),pod_memory_events_1=require("./pod-memory-events"),heap_snapshot_1=require("./heap-snapshot"),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(u){console.error("[gateway] Middleware error:",u)}}h(applyGatewayMiddleware,"applyGatewayMiddleware"),n(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,u){if(s.startsWith("*.")){const f=s.slice(1);return u.endsWith(f)||u===s.slice(2)}return u===s}h(hostMatches,"hostMatches"),n(hostMatches,"hostMatches");function matchGatewayRoute(s){if(GATEWAY_ROUTES.length===0)return;const u=(s.headers.host||"").split(":")[0],f=s.url||"/";for(const w of GATEWAY_ROUTES)if(!(w.host&&!hostMatches(w.host,u))&&!(w.prefix&&!f.startsWith(w.prefix)))return w}h(matchGatewayRoute,"matchGatewayRoute"),n(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`)}h(childSockPath,"childSockPath"),n(childSockPath,"childSockPath");function sockOwnerPath(s){return`${s}.owner`}h(sockOwnerPath,"sockOwnerPath"),n(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(s){return`${s}.retiring`}h(sockRetiringPath,"sockRetiringPath"),n(sockRetiringPath,"sockRetiringPath");function isOwnerOnlyTransport(s){try{return typeof s.socket?.server?.address?.()=="string"}catch{return!1}}h(isOwnerOnlyTransport,"isOwnerOnlyTransport"),n(isOwnerOnlyTransport,"isOwnerOnlyTransport");function tell(s,u){if(!s.connected||typeof s.send!="function")return!1;try{return s.send(u,()=>{})}catch{return!1}}h(tell,"tell"),n(tell,"tell");function gatewayManaged(s=process.env){return Number(s.LENSMCP_GATEWAY_PID??0)>0}h(gatewayManaged,"gatewayManaged"),n(gatewayManaged,"gatewayManaged");function mayDriveThePool(s,u=gatewayManaged()){return!u||isOwnerOnlyTransport(s)}h(mayDriveThePool,"mayDriveThePool"),n(mayDriveThePool,"mayDriveThePool");function refusePoolCommand(s,u){console.warn(`[devserver] refused ${s.method} ${s.url} on the public port \u2014 under a gateway the pool is driven on the control socket only.`),u.writeHead(404,{"content-type":"application/json"}),u.end(JSON.stringify({error:"not found"}))}h(refusePoolCommand,"refusePoolCommand"),n(refusePoolCommand,"refusePoolCommand"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(s=process.env){const u=(s[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return u==="1"||u==="true"||u==="yes"||u==="on"}h(usesFixedInspectorPorts,"usesFixedInspectorPorts"),n(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(s,u,f=process.env){if(!usesFixedInspectorPorts(f))return 0;const w=Math.trunc(s)+Math.trunc(u);return Number.isInteger(w)&&w>1024&&w<=65535?w:0}h(inspectorPortFor,"inspectorPortFor"),n(inspectorPortFor,"inspectorPortFor");function inspectorPortOf(s){const u=/^wss?:\/\/[^/]*?:(\d+)\//.exec(s??""),f=u?Number(u[1]):NaN;return Number.isInteger(f)&&f>0?f:null}h(inspectorPortOf,"inspectorPortOf"),n(inspectorPortOf,"inspectorPortOf");function ensureOwnerOnlyDir(s){try{fs.mkdirSync(s,{recursive:!0,mode:448})}catch{}try{const u=fs.lstatSync(s);u.isDirectory()&&(u.mode&63)!==0&&fs.chmodSync(s,448)}catch{}}h(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),n(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(s,u=process.pid){ensureOwnerOnlyDir(path.dirname(s));try{const f=sockOwnerPath(s);try{fs.unlinkSync(f)}catch{}fs.writeFileSync(f,String(u),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(s)}catch{}try{fs.unlinkSync(sockRetiringPath(s))}catch{}}h(claimSock,"claimSock"),n(claimSock,"claimSock");function sockOwnedByAnother(s,u=process.pid){try{const f=fs.readFileSync(sockOwnerPath(s),"utf8").trim();return!!f&&f!==String(u)}catch{return!1}}h(sockOwnedByAnother,"sockOwnedByAnother"),n(sockOwnedByAnother,"sockOwnedByAnother");function cleanupSock(s,u=process.pid){if(s){try{const f=fs.readFileSync(sockOwnerPath(s),"utf8").trim();if(f&&f!==String(u))return}catch{}try{fs.unlinkSync(s)}catch{}try{fs.unlinkSync(sockOwnerPath(s))}catch{}try{fs.unlinkSync(sockRetiringPath(s))}catch{}}}h(cleanupSock,"cleanupSock"),n(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const s=process.env.LENSMCP_SERVICE_KEY;if(!s)return;const{createHash:u}=require("node:crypto");return u("sha256").update(s).digest("hex").slice(0,8)}h(serviceKeyFingerprint,"serviceKeyFingerprint"),n(serviceKeyFingerprint,"serviceKeyFingerprint");function rootModuleOf(s){let u;for(const f of s.getModules?.()?.values()??[])typeof f?.metatype!="function"||typeof f.distance!="number"||!Number.isFinite(f.distance)||(!u||f.distance<u.distance)&&(u=f);return u?.metatype}h(rootModuleOf,"rootModuleOf"),n(rootModuleOf,"rootModuleOf");let lastImportedModule;function takeImportedModule(){const s=lastImportedModule;return lastImportedModule=void 0,s}h(takeImportedModule,"takeImportedModule"),n(takeImportedModule,"takeImportedModule");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const requested=path.isAbsolute(spec)?spec:path.join(__dirname,spec);let resolved=requested;try{resolved=fs.realpathSync(requested)}catch{}eval(`(() => {
2
2
  const key = ${JSON.stringify(resolved)};
3
3
  const previous = require.cache[key];
4
4
  delete require.cache[key]; // delete from CJS cache
@@ -9,13 +9,15 @@
9
9
  const at = owner && Array.isArray(owner.children) ? owner.children.indexOf(previous) : -1;
10
10
  if (at >= 0) owner.children.splice(at, 1);
11
11
  }
12
- })()`),eval(`require(${JSON.stringify(resolved)})`)}h(importFresh,"importFresh"),s(importFresh,"importFresh");function debounce(o,p){let g=null;return(..._)=>{g&&clearTimeout(g),g=setTimeout(()=>o(..._),p)}}if(h(debounce,"debounce"),s(debounce,"debounce"),process.env.APP_RUNNER==="1"){(0,stdio_guard_1.guardBrokenStdio)();const o=s(g=>{if(!inspector.url())try{inspector.open(g,"127.0.0.1",!1)}catch(B){console.warn(`[child] inspector.open(${g}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,B.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const _=inspector.url();_&&process.send&&process.send({type:"inspector-url",url:_})},"openInspector"),p=process.env.CHILD_DEBUG_PORT;if(p!==void 0&&p!==""){const g=Number(p);Number.isInteger(g)&&(g===0||g>1024)&&o(g)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let g=null,_=!1,B,j=!1;const N=(0,hot_swap_registries_1.registryPruningEnabled)(),Z=s((r,i)=>{const f=(0,hot_swap_registries_1.describePrune)(r,i);f&&console.log(f)},"logPrune");let $={app:null,handler:s((r,i)=>{i.statusCode=503,i.end("starting")},"handler"),inflight:0};const ee=(()=>{const r=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(r)&&r>=0?r:15*6e4})(),Y=s(r=>{const i=globalThis.__lensmcpActiveWork;if(typeof i!="function")return{count:0};try{const f=i(r);return{count:Number(f?.count??0),...f?.oldestStartedAt?{oldestStartedAt:f.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),te=(()=>{const r=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(r)&&r>=1?Math.floor(r):1})(),C=new Set,P=(0,swap_recycle_1.swapRecyclePolicy)();let oe,le=0;const ge=(()=>{const r=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(r)&&r>=0?r:3500})();let K=!1,V=!1,u;const m=s(()=>{if(u===void 0)try{v8.setFlagsFromString("--expose-gc");const r=vm.runInNewContext("gc");u=typeof r=="function"?r:null}catch{u=null}try{u?.()}catch{}return process.memoryUsage().heapUsed},"liveHeap"),w=(()=>{if(!(0,error_stack_release_1.errorFrameReleaseEnabled)())return null;try{const r=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH))+path.sep,i=`${path.sep}node_modules${path.sep}`,f=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:s(y=>f.has(y)||y.startsWith(r)&&!y.slice(r.length).includes(i)&&!y.slice(r.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let O=!1;function W(){if(!(!w||O))try{const r=w();r.errors>0&&console.log(`[child] read the stack of ${r.errors} Error object(s) that libraries created while loading and keep for good \u2014 unread, each held the functions that were on its stack, this bundle's included`)}catch(r){console.error("[child] could not release library error frames:",r),O=!0}}h(W,"z"),s(W,"releaseLibraryErrorFrames");const D=new Map;let b=Date.now();const T=s(()=>P.settleMs<=0||!_&&!j&&D.size===0&&Date.now()-b>=P.settleMs,"settled");let A;function R(r){if(!(!P.enabled||K||V)){if(P.settleMs<=0){F(r);return}A&&clearTimeout(A),A=setTimeout(()=>{A=void 0,T()?F(r):R(r)},P.settleMs),A.unref?.()}}h(R,"N"),s(R,"recycleCheckWhenSettled");const ne=s(()=>{if(P.stuckCloseMs<=0)return 0;const r=Date.now();let i=0;for(const f of D.values())r-f>=P.stuckCloseMs&&(i+=1);return i},"stuckCloses");function F(r){if(!P.enabled||K||V||!process.send)return;const i=v8.getHeapStatistics().heap_size_limit,f=s(E=>({swaps:le,usedBytes:E,limitBytes:i,stuckCloses:ne(),settled:T(),...oe===void 0?{}:{baselineBytes:oe}}),"state");if(!(0,swap_recycle_1.recycleReason)(P,f(process.memoryUsage().heapUsed)))return;const y=(0,swap_recycle_1.recycleReason)(P,f(m()));y&&(K=!0,console.log(`[child] asking for a fresh pod ${r} \u2014 ${y}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),process.send({type:"recycle-request",reason:y}))}h(F,"K"),s(F,"considerRecycle");async function J(r,i){if(r.closed||(r.closed=!0,C.delete(r),i&&console.error(`[child] ${i}`),!r.app))return;D.set(r,Date.now());const f=P.enabled&&P.stuckCloseMs>0?setTimeout(()=>{D.has(r)&&(console.error(`[child] a previous app has been closing for ${Math.round(P.stuckCloseMs/1e3)}s and its close() has not returned \u2014 one of its shutdown hooks is waiting on something that does not end (a queue worker, a connection, a stream). It stays in memory until this pod is replaced.`),F("with a previous app stuck in close()"))},P.stuckCloseMs):void 0;f?.unref?.();try{await r.app.close()}catch(y){console.error("[child] error closing a previous app:",y)}finally{D.delete(r),b=Date.now(),f&&clearTimeout(f),r.app=null,r.handler=(y,E)=>{E.statusCode=503,E.end("closed")}}if(r.superseded){try{Z((0,hot_swap_registries_1.pruneRegistries)(r.superseded,"serve"),"after closing the previous app")}catch(y){console.error("[child] could not release the previous app's class registrations:",y)}r.superseded=void 0}R("after closing the previous app")}h(J,"Q"),s(J,"closeGeneration");async function de(r){for(;C.size>=te;){const E=C.values().next().value;if(!E)break;await J(E,`closing a still-draining generation early \u2014 ${C.size} were pending and the cap is ${te} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}C.add(r);const i=Date.now(),f=s(()=>({inflight:r.inflight,work:Y(r.workGen).count}),"busy");for(;ee>0&&Date.now()-i<ee;){if(r.closed)return;const E=f();if(E.inflight===0&&E.work===0)break;await new Promise(x=>{setTimeout(x,250).unref?.()})}if(r.closed)return;const y=f();y.inflight>0||y.work>0?console.error(`[child] swap drain expired after ${Date.now()-i}ms \u2014 closing the previous app with ${y.inflight} in-flight request(s) and ${y.work} background job(s) still running. A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${ee}ms) if this is legitimate work.`):Date.now()-i>250&&console.log(`[child] previous app drained in ${Date.now()-i}ms \u2014 closing it`),await J(r)}h(de,"he"),s(de,"drainThenClose");async function M(){if(_)return j=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),g;_=!0,b=Date.now(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(S,L)=>{console.error(`[child] uncaughtException (${L}):`,S)}),process.on("unhandledRejection",S=>{const L=S instanceof Error?S.stack||S.message:String(S);L.includes("FronteggContextScope")||L.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",S)});let r;if(N)try{r=(0,hot_swap_registries_1.captureRegistries)()}catch{r=void 0}try{try{await importFresh(BUNDLE_PATH)}catch(S){if(!(S instanceof SyntaxError))throw S;console.warn(`[child] bundle was unreadable mid-write (${S.message}) \u2014 retrying the swap once`),await new Promise(L=>setTimeout(L,400)),await importFresh(BUNDLE_PATH)}}catch(S){throw _=!1,b=Date.now(),S}if(typeof global.createChildApp!="function")throw _=!1,b=Date.now(),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'.`);let i;if(r)try{i=(0,hot_swap_registries_1.supersededSince)(r),Z((0,hot_swap_registries_1.pruneRegistries)(i,"build"),"at the swap")}catch(S){console.error("[child] could not release superseded class registrations:",S)}const f=global.__lensmcpBeginWorkGeneration,y=typeof f=="function"?f():void 0;let E;try{E=await global.createChildApp()}catch(S){throw _=!1,b=Date.now(),S}const x=$;return i?.size&&(x.app?x.superseded=i:Z((0,hot_swap_registries_1.pruneRegistries)(i,"serve"),"at the swap")),g=E,$={app:E,handler:E.handler,inflight:0,...y===void 0?{}:{workGen:y}},console.log("[child] Swapped to fresh app (previous draining in background)"),W(),x.app&&(le+=1,de(x).catch(S=>console.error("[child] background close error:",S)),setTimeout(()=>F("30s after a hot swap"),3e4).unref?.()),_=!1,b=Date.now(),j?(j=!1,M()):E}h(M,"V"),s(M,"swapNow");const re=debounce(async()=>{try{console.log("[child] /webpack/reload"),await M()}catch(r){console.error("[child] reload failed:",r)}},250),G=http.createServer((r,i)=>{if(r.method==="POST"&&r.url==="/webpack/reload"){i.writeHead(200,{"content-type":"application/json"}),i.end(JSON.stringify({ok:!0})),re();return}if(r.method==="GET"&&r.url==="/__lensmcp/alive"){const y=r.headers["x-internal-token"],E=process.env.LENSMCP_SERVICE_KEY;if(E&&y!==E){i.writeHead(404,{"content-type":"text/plain"}),i.end("not found");return}const x=Y();i.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),i.end(JSON.stringify({ok:!0,pid:process.pid,swapping:_,inflight:$.inflight,oldestInflightAt:$.oldestAt,activeWork:x.count,oldestWorkAt:x.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...B?{watcher:B}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const f=$;f.inflight+=1,f.oldestAt===void 0&&(f.oldestAt=Date.now()),i.on("close",()=>{f.inflight-=1,f.inflight===0&&(f.oldestAt=void 0)}),f.handler(r,i)});G.keepAliveTimeout=0;const I=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(I),process.exit(0)});try{const r=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);if((0,schema_first_instance_1.schemaPrewarmEnabled)())try{const i=(0,schema_first_instance_1.prewarmZodFor)(r);i?.created&&console.log(`[child] zod${i.version?` ${i.version}`:""}: built ${i.created} throwaway schemas first, so none of the bundle's own is the first of its class`)}catch{}if((0,redis_disconnect_fix_1.redisDisconnectFixEnabled)())try{const i=(0,redis_disconnect_fix_1.finishStrandedDisconnectsFor)(r);i?.patched&&console.log(`[child] ioredis${i.versions.length?` ${i.versions.join(", ")}`:""}: a disconnect() between reconnect attempts now ends the client \u2014 it used to leave a queue worker's close() waiting for good`)}catch{}await M(),P.enabled&&P.heapFactor>0&&(oe=m()),console.log(`[child] Using bundle: ${r}`),claimSock(I),G.listen(I,()=>{console.log(`[child] Listening on ${I}`),process.send&&process.send({type:"ready",socketPath:I})})}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 i=Number(r.port);o(Number.isInteger(i)&&i>1024?i:0);return}if(r.type==="watcher"){const i=r.testimony;i&&typeof i=="object"&&(B=i);return}if(r.type==="recycle-declined"){K=!1;return}if(r.type==="retire"){U();return}if(r.type==="reload"){if(V)return;try{await M(),process.send&&process.send({type:"reloaded",socketPath:I})}catch(i){console.error("[child] reload error:",i),process.send&&process.send({type:"reload-error",error:String(i?.stack||i)})}}}});async function U(){if(V)return;V=!0,console.log("[child] retiring \u2014 a fresh pod has taken over; finishing in-flight work, then exiting");try{fs.writeFileSync(sockRetiringPath(I),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(i=>{setTimeout(i,ge).unref?.()}),cleanupSock(I),G.close();const r=Date.now();for(;Date.now()-r<ee&&(G.closeIdleConnections?.(),!($.inflight===0&&Y().count===0));)await new Promise(i=>{setTimeout(i,250).unref?.()});setTimeout(()=>process.exit(0),3e4).unref?.();try{await g?.close()}catch(i){console.error("[child] close error while retiring:",i)}process.exit(0)}h(U,"ee"),s(U,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const X=s(async r=>{console.log(`[child ${r}] shutting down\u2026`);const i=g?.close().catch(y=>console.error("[child] close error:",y)),f=new Promise(y=>G.close(()=>y()));cleanupSock(I),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([i,f]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(r=>{process.on(r,()=>{inspector?.close?.(),X(r)})})})()}else{let o=h(function(u){C.push(u),C.length>te&&C.splice(0,C.length-te),ee?.write(u.replace(Y,""));for(const m of P)try{const w=m.plain?u.replace(Y,""):u;m.res.write(m.redact?redactInspectorUrls(w):w)}catch{P.delete(m)}},"P");s(o,"emitLogLine");const p=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],g="\x1B[0m",_=Math.max(1,Number(process.env.CHILD_COUNT||1)),B=LENSMCP_WS_KEY||path.basename(process.cwd()),j=s(u=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(B,SERVICE_NAME||"service",u)),"execPathFor");let N=(()=>{const u=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(u)&&u>1024?u:0})();const Z=20;N&&(console.warn("[parent] DEBUGGER ENABLED \u2014 pods and workers open a Node inspector. Node's inspector has NO authentication: the ws:// URL's UUID is the whole credential, and any account on this host that obtains it can run code as you. Do not enable this on a shared machine. (issues/codex-security/06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${N}+slot, workers ${N}+${Z}+i). Any account on this host can connect to 127.0.0.1:${N} and read the session UUID from /json/list. Unset it to use ephemeral ports.`:`[parent] Inspectors bind an EPHEMERAL loopback port; attach via the printed "Debugger listening" URL, or \`lensmcp debug <service>\` (control socket). Set ${exports.DEBUG_FIXED_PORTS_ENV}=1 for fixed ports if your IDE needs a pre-configured attach port \u2014 any account on this host can then reach them.`));const $=process.env.LENSMCP_LOG_FILE||"",ee=$?(()=>{try{fs.mkdirSync(path.dirname($),{recursive:!0});const u=fs.createWriteStream($,{flags:"a",mode:384});try{fs.chmodSync($,384)}catch{}return u}catch(u){return console.warn(`[parent] cannot open log file ${$}:`,u.message),null}})():null,Y=/\x1b\[[0-9;]*m/g,te=2e3,C=[],P=new Set;for(const u of["log","info","warn","error"]){const m=console[u].bind(console);console[u]=(...w)=>{m(...w),o(w.map(O=>typeof O=="string"?O:util.inspect(O)).join(" ")+`
13
- `)}}const oe=s(u=>p[(u-1)%p.length],"colorFor"),le=s(u=>{if(_===1)return"";const m=`[child#${u}] `;return`${oe(u)}${m}${g}`},"tagFor"),ge=s(u=>u.startsWith("{")&&u.endsWith("}"),"isJsonLog"),K=s((u,m,w)=>{const O=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:s(b=>`
14
- `+String(b),"stack")}}),W=s((b,T)=>{const A=b==="stdout"?process.stdout:process.stderr;try{w?.(T)}catch{}const R=ge(T)?O(T):`${T}
15
- `;A.write(`${m}${R}`),o(`${m}${R}`)},"write"),D=s((b,T)=>{if(!b)return;const A=readline.createInterface({input:b});A.on("line",R=>W(T,R)),A.on("close",()=>{})},"attach");D(u.stdout,"stdout"),D(u.stderr,"stderr")},"wireProcLogging"),V=s(u=>K(u.proc,le(u.id),m=>u.tail.push(m)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const u=httpProxy.createProxyServer({});let m=Number(process.env.PORT)||0;m||(m=9090);const w=[],O=new Set;function W(){let e=0;for(;O.has(e);)e++;return O.add(e),e}h(W,"z"),s(W,"allocDebugSlot");let D=1,b=0,T=!1,A=_,R=[],ne=0;const F=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),J=s(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),de=s(e=>e.booting&&!J(e)&&Date.now()-e.spawnedAt<F,"stillBooting");function M(){return w.filter(e=>e.healthy&&typeof e.socketPath=="string")}h(M,"V"),s(M,"healthyChildren");let re;function G(e){if(!e||typeof e!="object")return;const t=e;if(t.schemaVersion===1){re=t;for(const d of w)try{d.proc.send?.({type:"watcher",testimony:t})}catch{}}}h(G,"U"),s(G,"recordWatcherTestimony");function I(e,t=256*1024){return new Promise(d=>{let a="",c=!1;e.on("data",n=>{c||(a+=n,a.length>t&&(c=!0))}),e.on("end",()=>{if(c||!a)return d(void 0);try{d(JSON.parse(a))}catch{d(void 0)}}),e.on("error",()=>d(void 0))})}h(I,"I"),s(I,"readJsonBody");function U(){const e=M();if(e.length===0)return null;const t=b%e.length;return b=(b+1)%e.length,e[t]}h(U,"ee"),s(U,"pickChild");async function X(){const e=D++,t=childSockPath(e);cleanupSock(t);const d=["--enable-source-maps"];__filename.endsWith(".ts")&&d.unshift("--require","@swc-node/register");const a=N?W():-1,c=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:t,...a>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(N,a))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:d,execPath:j("pod")}),n={id:e,proc:c,healthy:!1,lastError:null,debugSlot:a,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:ne};return c.on("message",k=>{if(!k||typeof k!="object")return;const l=k;if(l.type==="ready"){if(n.socketPath=String(l.socketPath),n.healthy=!0,n.booting=!1,console.log(`[parent] Child#${n.id} ready on socket ${n.socketPath}`),re)try{c.send?.({type:"watcher",testimony:re})}catch{}if(ne!==n.bootReloadSeq)try{c.send?.({type:"reload"})}catch{}}else l.type==="recycle-request"?L(n,String(l.reason||"it has outgrown its bound")):l.type==="inspector-url"?(n.inspectorUrl=String(l.url),console.log(`[parent] Child#${n.id} Debugger listening on ${l.url}`)):l.type==="reloaded"?(n.socketPath=String(l.socketPath),n.healthy=!0,console.log(`[parent] Child#${n.id} hot-swapped on socket ${n.socketPath}`)):l.type==="reload-error"?(n.lastError=String(l.error||"unknown reload error"),console.error(`[parent] Child#${n.id} reload error: ${n.lastError}`),n.healthy=!1):l.type==="boot-error"&&(n.lastError=String(l.error||"unknown boot error"),console.error(`[parent] Child#${n.id} boot error: ${n.lastError}`),n.healthy=!1,n.booting=!1)}),c.on("exit",(k,l)=>{n.booting=!1,n.debugSlot>=0&&(O.delete(n.debugSlot),n.debugSlot=-1);const v=l==="SIGINT"||l==="SIGTERM"||k===0;if(v?console.log(`[parent] Child#${n.id} exited cleanly (code=${k}, signal=${l??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${n.id} crashed (code=${k}, signal=${l??"none"}, pid=${n.proc.pid}, ${n.healthy?"was serving":"was not ready"})`,k,l,n.tail.snapshot())),n.lastError||(n.lastError=`Child exited abnormally (code=${k}, signal=${l??"none"})`)),n.healthy=!1,cleanupSock(n.socketPath,n.proc.pid),n.socketPath=void 0,!v&&!T){const q=w.indexOf(n);q>=0&&w.splice(q,1);const ae=Date.now();if(R=R.filter(Q=>ae-Q<3e4),R.length>=5){console.error(`[parent] crash-loop detected (${R.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}R.push(ae),setTimeout(()=>{const Q=w.filter($e=>$e.proc.killed===!1).length;!T&&Q<A&&(console.log(`[parent] respawning a crashed pod (${Q}/${A} live)`),X().catch(()=>{}))},1500).unref?.()}}),w.push(n),V(n),n}h(X,"te"),s(X,"spawnChild");async function r(e){A=Math.max(A,e);const t=w.filter(a=>a.proc.killed===!1),d=e-t.length;for(let a=0;a<d;a++)await X()}h(r,"r"),s(r,"ensurePoolSize");async function i(){const e=w.filter(t=>(!t.healthy||typeof t.socketPath!="string")&&!de(t));await Promise.all(e.map(async t=>{try{t.proc.kill("SIGTERM")}catch{}cleanupSock(t.socketPath,t.proc.pid),t.socketPath=void 0;const d=w.indexOf(t);d>=0&&w.splice(d,1),await X()}))}h(i,"c"),s(i,"respawnUnhealthy");const f=new Set;let y=!1,E=0;const x=3e4,S=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function L(e,t){const d=s(c=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${t}) \u2014 not now: ${c}`);try{e.proc.send?.({type:"recycle-declined"})}catch{}},"decline");if(T||!w.includes(e))return;if(y)return d("another pod is already being replaced");const a=Date.now()-E;if(a<x)return d(`a pod was replaced ${Math.round(a/1e3)}s ago`);y=!0,console.log(`[parent] Child#${e.id} asked for a fresh pod \u2014 ${t}. Starting one; Child#${e.id} keeps serving until it is ready.`);try{const c=await X(),n=Date.now()+F;for(;!c.healthy&&!c.lastError&&!J(c)&&Date.now()<n;)await new Promise(v=>{setTimeout(v,100).unref?.()});if(T)return;if(!c.healthy){const v=w.indexOf(c);v>=0&&w.splice(v,1);try{c.proc.kill("SIGTERM")}catch{}return d(`its successor Child#${c.id} did not come up (${c.lastError??"no answer in time"})`)}const k=w.indexOf(e);k>=0&&w.splice(k,1),e.healthy=!1,f.add(e);const l=setTimeout(()=>{if(!J(e)){console.error(`[parent] Child#${e.id} was still running ${S}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},S);l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),f.delete(e)});try{e.proc.send?.({type:"retire"})}catch{try{e.proc.kill("SIGTERM")}catch{}}E=Date.now(),console.log(`[parent] Child#${c.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${c.proc.pid})`)}catch(c){d(`could not start a successor: ${c?.message??String(c)}`)}finally{y=!1}}h(L,"D"),s(L,"recyclePod");const H=[];function ye(e){const t=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),d=path.join(t,`${e}.js`);if(!fs.existsSync(d))return console.error(`[parent] Worker "${e}" bundle not found: ${d}`),null;const a=["--enable-source-maps"];__filename.endsWith(".ts")&&a.unshift("--require","@swc-node/register");try{a.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const c=inspectorPortFor(N||9229,Z+Math.max(0,WORKER_NAMES.indexOf(e)));a.push(N?`--inspect=127.0.0.1:${c}`:`--inspect-port=127.0.0.1:${c}`);const n=(0,node_child_process_1.fork)(d,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:a,execPath:j(`w-${e}`)}),k={name:e,proc:n,debugPort:c};return H.push(k),K(n,`\x1B[35m[worker:${e}] \x1B[0m`,l=>{const v=/Debugger listening on (wss?:\/\/\S+)/.exec(l);v&&(k.inspectorUrl=v[1])}),n.on("exit",(l,v)=>{v==="SIGINT"||v==="SIGTERM"||l===0||console.error(`[parent] Worker "${e}" crashed (code=${l}, signal=${v??"none"})`);const q=H.indexOf(k);q>=0&&H.splice(q,1)}),console.log(`[parent] Worker "${e}" started (pid=${n.pid})`),k}h(ye,"we"),s(ye,"spawnWorkerProcess");const Ee=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),Se=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),se=new Set,Pe=s(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function pe(e,t){if(se.delete(e),!Pe(e)){console.error(`[parent] Worker "${e.name}" (pid=${e.proc.pid}) ${t} \u2014 killing it. A worker that returns from its SIGTERM handler but leaves a handle open (a Redis client handed to BullMQ is not closed by worker.close()) never exits on its own: close what it opened, or call process.exit().`);try{e.proc.kill("SIGKILL")}catch{}}}h(pe,"fe"),s(pe,"killDrainingWorker");function ke(e){try{e.proc.kill("SIGTERM")}catch{}if(Pe(e))return;const t=[...se].filter(a=>a.name===e.name);for(;t.length>=Se;)pe(t.shift(),`was still running when a later reload replaced its successor (cap ${Se})`);se.add(e);const d=setTimeout(()=>pe(e,`was still running ${Ee}ms after SIGTERM`),Ee);d.unref?.(),e.proc.once("exit",()=>{clearTimeout(d),se.delete(e)})}h(ke,"ke"),s(ke,"retireWorker");function be(){for(const e of[...H])ke(e);H.length=0;for(const e of WORKER_NAMES)ye(e)}h(be,"Ae"),s(be,"restartWorkers");const Re=debounce(async()=>{ne+=1;const e=M();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 i(),WORKER_NAMES.length>0&&be()},200),ie=s(async(e,t)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){if(!mayDriveThePool(e))return refusePoolCommand(e,t);t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0})),I(e).then(c=>G(c?.watcher)),Re();return}if(e.method==="POST"&&e.url==="/webpack/watching"){if(!mayDriveThePool(e))return refusePoolCommand(e,t);const c=await I(e);G(c),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,relayed:re!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){if(!mayDriveThePool(e))return refusePoolCommand(e,t);let c="";e.on("data",n=>{c+=n}),e.on("end",async()=>{let n=0;try{n=Number(JSON.parse(c||"{}").pods)||0}catch{}n=Math.max(1,Math.min(n,16)),await r(n),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,pods:n}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const c=new URL(e.url,"http://localhost").searchParams,n=Math.min(Math.max(Number(c.get("tail")??"100")||0,0),te),k=c.get("follow")!=="0",l=c.get("plain")==="1",v=!isOwnerOnlyTransport(e);t.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const q=n===0?"":C.slice(-n).join(""),ae=l?q.replace(Y,""):q;if(t.write(v?redactInspectorUrls(ae):ae),!k){t.end();return}const Q={res:t,plain:l,redact:v};P.add(Q),e.on("close",()=>P.delete(Q));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!isOwnerOnlyTransport(e)){t.writeHead(404,{"content-type":"application/json"}),t.end(JSON.stringify({error:"not found"}));return}const c=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(c)&&c>1024&&(N=c),N||(N=9229);const n=w.filter(l=>l.proc.exitCode===null&&l.proc.signalCode===null);for(const l of n){l.debugSlot<0&&(l.debugSlot=W());try{l.proc.send?.({type:"debug-open",port:inspectorPortFor(N,l.debugSlot)})}catch{}}for(const l of H)try{l.proc.kill("SIGUSR1")}catch{}const k=Date.now()+2e3;for(;Date.now()<k&&n.some(l=>!l.inspectorUrl);)await new Promise(l=>setTimeout(l,50));t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,basePort:N,fixedPorts:usesFixedInspectorPorts(),pods:n.map(l=>({id:l.id,inspectorUrl:l.inspectorUrl??null})),workers:H.map(l=>({name:l.name,port:l.debugPort||inspectorPortOf(l.inspectorUrl),inspectorUrl:l.inspectorUrl??null}))},null,2)+`
16
- `);return}const d=matchGatewayRoute(e);if(d?.prependPrefix&&!e.url?.startsWith(d.prependPrefix)&&(e.url=d.prependPrefix+(e.url??"/")),d?.target){applyGatewayMiddleware(e),u.web(e,t,{target:d.target,autoRewrite:typeof d.target=="string",changeOrigin:!0},c=>{console.error(`[parent] proxy error to ${d.target}:`,c),t.statusCode=502,t.setHeader("content-type","text/plain; charset=utf-8"),t.end(`Upstream error from ${d.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 a=U();if(!a){t.statusCode=503,t.setHeader("content-type","text/plain; charset=utf-8");const c=w.map(n=>`#${n.id}: ${n.lastError??"no error recorded"}`).join(`
17
- `);t.end(`No healthy children available. POST /webpack/reload to recover.
12
+ })()`);const exports=eval(`require(${JSON.stringify(resolved)})`);try{const mod=eval("require.cache")[resolved];lastImportedModule=mod&&typeof mod=="object"?mod:void 0}catch{}return exports}h(importFresh,"importFresh"),n(importFresh,"importFresh");function debounce(s,u){let f=null;return(...w)=>{f&&clearTimeout(f),f=setTimeout(()=>s(...w),u)}}if(h(debounce,"debounce"),n(debounce,"debounce"),process.env.APP_RUNNER==="1"){(0,stdio_guard_1.guardBrokenStdio)();const s=n(f=>{if(!inspector.url())try{inspector.open(f,"127.0.0.1",!1)}catch(F){console.warn(`[child] inspector.open(${f}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,F.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const w=inspector.url();w&&tell(process,{type:"inspector-url",url:w})},"openInspector"),u=process.env.CHILD_DEBUG_PORT;if(u!==void 0&&u!==""){const f=Number(u);Number.isInteger(f)&&(f===0||f>1024)&&s(f)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let f=null,w=!1,F,Q=!1;const Re=(0,hot_swap_registries_1.registryPruningEnabled)(),oe=n((t,i)=>{const p=(0,hot_swap_registries_1.describePrune)(t,i);p&&console.log(p)},"logPrune");let j={app:null,handler:n((t,i)=>{i.statusCode=503,i.end("starting")},"handler"),inflight:0};const ne=(()=>{const t=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(t)&&t>=0?t:15*6e4})(),I=n(t=>{const i=globalThis.__lensmcpActiveWork;if(typeof i!="function")return{count:0};try{const p=i(t);return{count:Number(p?.count??0),...p?.oldestStartedAt?{oldestStartedAt:p.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),ue=n(t=>{const i=globalThis.__lensmcpStopWorkIntake;if(typeof i=="function")try{return i(t)}catch{return}},"stopWorkIntake"),se=n(t=>{const i=globalThis.__lensmcpWorkIntakeReport;if(!(t===void 0||typeof i!="function"))try{return i(t)??void 0}catch{return}},"workIntakeReport"),we=(()=>{const t=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(t)&&t>=1?Math.floor(t):1})(),Y=new Set,A=(0,swap_recycle_1.swapRecyclePolicy)();let H,K=0;const Ee=(()=>{const t=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(t)&&t>=0?t:3500})();let ie=!1,Z=!1;const $e=(0,pod_memory_1.podMemoryPolicy)();let Se=0;const C=(0,pod_memory_1.createPodMemoryMonitor)({policy:$e,state:n(()=>({gen:j.gen??0,swaps:K,...H===void 0?{}:{baseline:H},busy:w||Q||N.size>0,lastBusyAt:O}),"state"),send:n(t=>{tell(process,t)},"send"),counter:(0,pod_memory_1.createLiveObjectCounter)(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH)),onRetained:n(()=>ce(),"onRetained")}),g=n(()=>((0,pod_memory_1.fullGc)()&&C.tracker.noteGc(),process.memoryUsage().heapUsed),"liveHeap"),P=(()=>{if(!(0,error_stack_release_1.errorFrameReleaseEnabled)())return null;try{const t=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH))+path.sep,i=`${path.sep}node_modules${path.sep}`,p=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:n(m=>p.has(m)||m.startsWith(t)&&!m.slice(t.length).includes(i)&&!m.slice(t.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let E=!1;function B(){if(!(!P||E))try{const t=P();t.errors>0&&console.log(`[child] read the stack of ${t.errors} Error object(s) that libraries created while loading and keep for good \u2014 unread, each held the functions that were on its stack, this bundle's included`)}catch(t){console.error("[child] could not release library error frames:",t),E=!0}}h(B,"B"),n(B,"releaseLibraryErrorFrames");const N=new Map;let O=Date.now();const D=n(()=>A.settleMs<=0||!w&&!Q&&N.size===0&&Date.now()-O>=A.settleMs,"settled");let T;function $(t){if(!(!A.enabled||ie||Z)){if(A.settleMs<=0){V(t);return}T&&clearTimeout(T),T=setTimeout(()=>{T=void 0,D()?V(t):$(t)},A.settleMs),T.unref?.()}}h($,"D"),n($,"recycleCheckWhenSettled");const x=n(()=>{if(A.stuckCloseMs<=0)return 0;const t=Date.now();let i=0;for(const p of N.values())t-p>=A.stuckCloseMs&&(i+=1);return i},"stuckCloses");function V(t){if(!A.enabled||ie||Z||!process.send)return;const i=v8.getHeapStatistics().heap_size_limit,p=n(_=>{const S=C.oldestRetained();return{swaps:K,usedBytes:_,limitBytes:i,stuckCloses:x(),settled:D(),...H===void 0?{}:{baselineBytes:H},...S?{retained:{generation:S.gen,ageMs:Date.now()-S.closedAt,gcs:S.gcs}}:{}}},"state");if(!(0,swap_recycle_1.recycleReason)(A,p(process.memoryUsage().heapUsed)))return;const m=(0,swap_recycle_1.recycleReason)(A,p(g()));m&&(ie=!0,console.log(`[child] asking for a fresh pod ${t} \u2014 ${m}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB / _RETAINED_MS, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),tell(process,{type:"recycle-request",reason:m}))}h(V,"V"),n(V,"considerRecycle");let ae;function ce(){if(!A.enabled||A.retainedMs<=0||ie||Z)return;const t=C.oldestRetained();if(!t)return;const i=t.closedAt+A.retainedMs-Date.now();if(i<=0){V("with a closed generation still in memory");return}ae||(ae=setTimeout(()=>{ae=void 0,C.measure("retention-check").catch(()=>{})},i),ae.unref?.())}h(ce,"ce"),n(ce,"retainedRecycleCheck");function Pe(t,i){const p={};i&&(p.module=i),t&&(p.app=t);try{const m=t?.app;if(m&&typeof m=="object"&&(p.nestApp=m,m.container&&typeof m.container=="object")){p.container=m.container;const _=rootModuleOf(m.container);_&&(p.rootModule=_)}}catch{}return p}h(Pe,"Te"),n(Pe,"generationTargets");async function ee(t,i){if(t.closed||(t.closed=!0,Y.delete(t),i&&console.error(`[child] ${i}`),!t.app))return;N.set(t,Date.now());const p=A.enabled&&A.stuckCloseMs>0?setTimeout(()=>{N.has(t)&&(console.error(`[child] a previous app has been closing for ${Math.round(A.stuckCloseMs/1e3)}s and its close() has not returned \u2014 one of its shutdown hooks is waiting on something that does not end (a queue worker, a connection, a stream). It stays in memory until this pod is replaced.`),V("with a previous app stuck in close()"))},A.stuckCloseMs):void 0;p?.unref?.();const m=t.app;try{await t.app.close()}catch(_){console.error("[child] error closing a previous app:",_)}finally{N.delete(t),O=Date.now(),p&&clearTimeout(p),t.app=null,t.handler=(_,S)=>{S.statusCode=503,S.end("closed")},t.gen!==void 0&&C.closed(t.gen,K,Pe(m,t.module)),t.module=void 0}if(t.superseded){try{oe((0,hot_swap_registries_1.pruneRegistries)(t.superseded,"serve"),"after closing the previous app")}catch(_){console.error("[child] could not release the previous app's class registrations:",_)}t.superseded=void 0}$("after closing the previous app")}h(ee,"ae"),n(ee,"closeGeneration");async function le(t){for(;Y.size>=we;){const S=Y.values().next().value;if(!S)break;await ee(S,`closing a still-draining generation early \u2014 ${Y.size} were pending and the cap is ${we} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}Y.add(t);const i=Date.now(),p=n(()=>({inflight:t.inflight,work:I(t.workGen).count}),"busy");for(;ne>0&&Date.now()-i<ne;){if(t.closed)return;const S=p();if(S.inflight===0&&S.work===0)break;await new Promise(X=>{setTimeout(X,250).unref?.()})}if(t.closed)return;const m=p(),_=se(t.workGen);m.inflight>0||m.work>0?console.error(`[child] swap drain expired after ${Date.now()-i}ms \u2014 closing the previous app with ${m.inflight} in-flight request(s) and ${m.work} background job(s) still running. `+(_?`Previous app still processing ${_.running} queue job(s); ${_.takenAfterStop} were taken after the swap. `:"")+`A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${ne}ms) if this is legitimate work.`):(Date.now()-i>250||_?.runningAtStop||_?.takenAfterStop)&&console.log(`[child] previous app drained in ${Date.now()-i}ms \u2014 closing it`+(_?` (it finished ${_.finishedAfterStop} queue job(s) after the swap; ${_.takenAfterStop} were taken after the swap)`:"")),await ee(t)}h(le,"le"),n(le,"drainThenClose");async function J(){if(w)return Q=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),f;w=!0,O=Date.now(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(k,z)=>{console.error(`[child] uncaughtException (${z}):`,k)}),process.on("unhandledRejection",k=>{const z=k instanceof Error?k.stack||k.message:String(k);z.includes("FronteggContextScope")||z.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",k)});let t;if(Re)try{t=(0,hot_swap_registries_1.captureRegistries)()}catch{t=void 0}const i=global.__lensmcpBeginWorkGeneration,p=typeof i=="function"?i():void 0;try{try{await importFresh(BUNDLE_PATH)}catch(k){if(!(k instanceof SyntaxError))throw k;console.warn(`[child] bundle was unreadable mid-write (${k.message}) \u2014 retrying the swap once`),await new Promise(z=>setTimeout(z,400)),await importFresh(BUNDLE_PATH)}}catch(k){throw w=!1,O=Date.now(),k}if(typeof global.createChildApp!="function")throw w=!1,O=Date.now(),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'.`);let m;if(t)try{m=(0,hot_swap_registries_1.supersededSince)(t),oe((0,hot_swap_registries_1.pruneRegistries)(m,"build"),"at the swap")}catch(k){console.error("[child] could not release superseded class registrations:",k)}let _;try{_=await global.createChildApp()}catch(k){throw w=!1,O=Date.now(),takeImportedModule(),k}const S=j;m?.size&&(S.app?S.superseded=m:oe((0,hot_swap_registries_1.pruneRegistries)(m,"serve"),"at the swap")),f=_;const X=takeImportedModule();j={app:_,handler:_.handler,inflight:0,gen:++Se,...X?{module:X}:{},...p===void 0?{}:{workGen:p}};const L=S.app&&p!==void 0?ue(p):void 0;return console.log("[child] Swapped to fresh app (previous draining in background)"+(L?.workers?`; its ${L.workers} queue worker(s) stopped taking new jobs, previous app still processing ${L.runningAtStop} job(s)`:"")),B(),S.app&&(K+=1,le(S).catch(k=>console.error("[child] background close error:",k)),setTimeout(()=>V("30s after a hot swap"),3e4).unref?.(),C.swapped(S.gen,()=>S.closed===!0&&!N.has(S))),w=!1,O=Date.now(),Q?(Q=!1,J()):_}h(J,"re"),n(J,"swapNow");const he=debounce(async()=>{try{console.log("[child] /webpack/reload"),await J()}catch(t){console.error("[child] reload failed:",t)}},250),q=http.createServer((t,i)=>{if(t.method==="POST"&&t.url==="/webpack/reload"){i.writeHead(200,{"content-type":"application/json"}),i.end(JSON.stringify({ok:!0})),he();return}if(t.method==="GET"&&t.url==="/__lensmcp/alive"){const m=t.headers["x-internal-token"],_=process.env.LENSMCP_SERVICE_KEY;if(_&&m!==_){i.writeHead(404,{"content-type":"text/plain"}),i.end("not found");return}const S=I();i.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),i.end(JSON.stringify({ok:!0,pid:process.pid,swapping:w,inflight:j.inflight,oldestInflightAt:j.oldestAt,activeWork:S.count,oldestWorkAt:S.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...F?{watcher:F}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const p=j;p.inflight+=1,p.oldestAt===void 0&&(p.oldestAt=Date.now()),i.on("close",()=>{p.inflight-=1,p.inflight===0&&(p.oldestAt=void 0),C.activity()}),p.handler(t,i)});q.keepAliveTimeout=0;const G=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(G),process.exit(0)});try{const t=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);if((0,schema_first_instance_1.schemaPrewarmEnabled)())try{const i=(0,schema_first_instance_1.prewarmZodFor)(t);i?.created&&console.log(`[child] zod${i.version?` ${i.version}`:""}: built ${i.created} throwaway schemas first, so none of the bundle's own is the first of its class`)}catch{}if((0,redis_disconnect_fix_1.redisDisconnectFixEnabled)())try{const i=(0,redis_disconnect_fix_1.finishStrandedDisconnectsFor)(t);i?.patched&&console.log(`[child] ioredis${i.versions.length?` ${i.versions.join(", ")}`:""}: a disconnect() between reconnect attempts now ends the client \u2014 it used to leave a queue worker's close() waiting for good`)}catch{}await J(),H=(await C.booted()).live,console.log(`[child] Using bundle: ${t}`),claimSock(G),q.listen(G,()=>{console.log(`[child] Listening on ${G}`),tell(process,{type:"ready",socketPath:G})})}catch(t){console.error("[child] Startup error:",t),tell(process,{type:"boot-error",error:String(t?.stack||t)}),process.exitCode=1;return}process.on("message",async t=>{if(!(!t||typeof t!="object")){if(t.type==="debug-open"){const i=Number(t.port);s(Number.isInteger(i)&&i>1024?i:0);return}if(t.type==="watcher"){const i=t.testimony;i&&typeof i=="object"&&(F=i);return}if(t.type==="heap-snapshot"){ke(t);return}if(t.type==="recycle-declined"){ie=!1;return}if(t.type==="retire"){re();return}if(t.type==="reload"){if(Z)return;try{await J(),tell(process,{type:"reloaded",socketPath:G})}catch(i){console.error("[child] reload error:",i),tell(process,{type:"reload-error",error:String(i?.stack||i)})}}}});let te=!1;async function ke(t){const i=n(p=>{tell(process,{type:"heap-snapshot-result",id:t.id,...p})},"reply");if(te)return i({ok:!1,refused:"a snapshot of this pod is already being written"});te=!0;try{const p=g(),m=C.tracker.views().map(X=>X.gen),_=(0,heap_snapshot_1.heapSnapshotRefusal)((0,heap_snapshot_1.heapSnapshotPolicy)(),{live:p,...t.available?{available:t.available}:{}});if(_)return i({ok:!1,refused:_,live:p});console.log(`[child] writing a heap snapshot (${Math.round(p/1048576)} MB live) \u2014 the pod pauses while V8 serialises it`);const S=await(0,heap_snapshot_1.writeHeapSnapshot)(t.file);i({ok:!0,file:t.file,bytes:S.bytes,ms:S.ms,live:p,gen:j.gen??0,closedAlive:m})}catch(p){i({ok:!1,error:p?.message??String(p)})}finally{te=!1}}h(ke,"Ne"),n(ke,"takeHeapSnapshot");async function re(){if(Z)return;Z=!0,C.stop(),console.log("[child] retiring \u2014 a fresh pod has taken over; finishing in-flight work, then exiting");const t=ue(Number.POSITIVE_INFINITY);t?.workers&&console.log(`[child] its ${t.workers} queue worker(s) stopped taking new jobs, still processing ${t.runningAtStop} job(s)`);try{fs.writeFileSync(sockRetiringPath(G),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(p=>{setTimeout(p,Ee).unref?.()}),cleanupSock(G),q.close();const i=Date.now();for(;Date.now()-i<ne&&(q.closeIdleConnections?.(),!(j.inflight===0&&I().count===0));)await new Promise(p=>{setTimeout(p,250).unref?.()});setTimeout(()=>process.exit(0),3e4).unref?.();try{await f?.close()}catch(p){console.error("[child] close error while retiring:",p)}process.exit(0)}h(re,"de"),n(re,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const fe=n(async t=>{console.log(`[child ${t}] shutting down\u2026`);const i=f?.close().catch(m=>console.error("[child] close error:",m)),p=new Promise(m=>q.close(()=>m()));cleanupSock(G),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([i,p]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(t=>{process.on(t,()=>{inspector?.close?.(),fe(t)})})})()}else{let s=h(function(g){H.push(g),H.length>A&&H.splice(0,H.length-A),we?.write((0,inspector_secret_1.redactInspectorUrls)(g.replace(Y,"")));for(const P of K)try{const E=P.plain?g.replace(Y,""):g;P.res.write(P.redact?(0,inspector_secret_1.redactInspectorUrls)(E):E)}catch{K.delete(P)}},"Z");n(s,"emitLogLine");const u=12,f=process.env.LENSMCP_PROJECT||SERVICE_NAME||"service",w=(0,pod_memory_events_1.createPodMemoryPublisher)(process.env.LENSMCP_EVENT_FILE),F=n(g=>({project:f,service:SERVICE_NAME||f,pod:g.id,pid:g.proc.pid??0,podGeneration:POD_GENERATION}),"podIdentity"),Q=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],Re="\x1B[0m",oe=Math.max(1,Number(process.env.CHILD_COUNT||1)),j=LENSMCP_WS_KEY||path.basename(process.cwd()),ne=n(g=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(j,SERVICE_NAME||"service",g)),"execPathFor");let I=(()=>{const g=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(g)&&g>1024?g:0})();const ue=20;I&&(console.warn("[parent] DEBUGGER ENABLED \u2014 pods and workers open a Node inspector. Node's inspector has NO authentication: the ws:// URL's UUID is the whole credential, and any account on this host that obtains it can run code as you. Do not enable this on a shared machine. (issues/closed/codex-security-06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${I}+slot, workers ${I}+${ue}+i). Any account on this host can connect to 127.0.0.1:${I} and read the session UUID from /json/list. Unset it to use ephemeral ports.`:`[parent] Inspectors bind an EPHEMERAL loopback port; attach via the printed "Debugger listening" URL, or \`lensmcp debug <service>\` (control socket). Set ${exports.DEBUG_FIXED_PORTS_ENV}=1 for fixed ports if your IDE needs a pre-configured attach port \u2014 any account on this host can then reach them.`));const se=process.env.LENSMCP_LOG_FILE||"",we=se?(()=>{try{fs.mkdirSync(path.dirname(se),{recursive:!0});const g=fs.createWriteStream(se,{flags:"a",mode:384});try{fs.chmodSync(se,384)}catch{}return g}catch(g){return console.warn(`[parent] cannot open log file ${se}:`,g.message),null}})():null,Y=/\x1b\[[0-9;]*m/g,A=2e3,H=[],K=new Set,Ee=gatewayManaged();for(const g of["log","info","warn","error"]){const P=console[g].bind(console),E=g==="log"||g==="info"?process.stdout:process.stderr;console[g]=(...B)=>{P(...B.map(N=>typeof N=="string"?(0,inspector_secret_1.forStream)(E,Ee,N):N)),s(B.map(N=>typeof N=="string"?N:util.inspect(N)).join(" ")+`
13
+ `)}}const ie=n(g=>Q[(g-1)%Q.length],"colorFor"),Z=n(g=>{if(oe===1)return"";const P=`[child#${g}] `;return`${ie(g)}${P}${Re}`},"tagFor"),$e=n(g=>g.startsWith("{")&&g.endsWith("}"),"isJsonLog"),Se=n((g,P,E)=>{const B=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:n(D=>`
14
+ `+String(D),"stack")}}),N=n((D,T)=>{const $=D==="stdout"?process.stdout:process.stderr;try{E?.(T)}catch{}const x=$e(T)?B(T):`${T}
15
+ `;$.write((0,inspector_secret_1.forStream)($,Ee,`${P}${x}`)),s(`${P}${x}`)},"write"),O=n((D,T)=>{if(!D)return;const $=readline.createInterface({input:D});$.on("line",x=>N(T,x)),$.on("close",()=>{})},"attach");O(g.stdout,"stdout"),O(g.stderr,"stderr")},"wireProcLogging"),C=n(g=>Se(g.proc,Z(g.id),P=>g.tail.push(P)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const g=httpProxy.createProxyServer({});let P=Number(process.env.PORT)||0;P||(P=9090);const E=[],B=new Set;function N(){let e=0;for(;B.has(e);)e++;return B.add(e),e}h(N,"R"),n(N,"allocDebugSlot");let O=1,D=0,T=!1,$=oe,x=[],V=0;const ae=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),ce=n(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),Pe=n(e=>e.booting&&!ce(e)&&Date.now()-e.spawnedAt<ae,"stillBooting");function ee(){return E.filter(e=>e.healthy&&typeof e.socketPath=="string")}h(ee,"ae"),n(ee,"healthyChildren");let le;function J(e){if(!e||typeof e!="object")return;const o=e;if(o.schemaVersion===1){le=o;for(const c of E)tell(c.proc,{type:"watcher",testimony:o})}}h(J,"re"),n(J,"recordWatcherTestimony");function he(e,o=256*1024){return new Promise(c=>{let d="",a=!1;e.on("data",r=>{a||(d+=r,d.length>o&&(a=!0))}),e.on("end",()=>{if(a||!d)return c(void 0);try{c(JSON.parse(d))}catch{c(void 0)}}),e.on("error",()=>c(void 0))})}h(he,"we"),n(he,"readJsonBody");function q(){const e=ee();if(e.length===0)return null;const o=D%e.length;return D=(D+1)%e.length,e[o]}h(q,"j"),n(q,"pickChild");function G(e,o){const c=`Child#${e.id}`,d=SERVICE_NAME||f,a=Date.now();if(o.type==="pod-memory"){const r=o.report;if(!r||typeof r.live!="number")return;e.memory.latest=r,e.memory.history.push(r),e.memory.history.length>12&&e.memory.history.splice(0,e.memory.history.length-12),console.log(`[parent] ${(0,pod_memory_1.describePodMemory)(c,r)}`),w.publish((0,pod_memory_events_1.buildPodMemoryEvent)(F(e),r))}else if(o.type==="pod-memory-retained"){const r=o.retained;if(!r||typeof r.gen!="number")return;e.memory.retained.set(r.gen,{...r,at:a}),console.warn(`[parent] ${(0,pod_memory_1.describeRetained)(c,d,r)}`),w.publish((0,pod_memory_events_1.buildPodRetainedEvent)(F(e),r,a))}else{const r=o.released;if(!r||typeof r.gen!="number")return;e.memory.retained.delete(r.gen),console.log(`[parent] ${(0,pod_memory_1.describeReleased)(c,d,r)}`),w.publish((0,pod_memory_events_1.buildPodReleasedEvent)(F(e),r,a))}}h(G,"U"),n(G,"recordPodMemory");const te=new Map;let ke=0;async function re(){const e=O++,o=childSockPath(e);cleanupSock(o);const c=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&c.unshift("--require","@swc-node/register"),c.push(...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const d=I?N():-1,a=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:o,...d>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(I,d))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:c,execPath:ne("pod")}),r={id:e,proc:a,healthy:!1,lastError:null,debugSlot:d,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:V,memory:{history:[],retained:new Map}};return a.on("error",y=>console.error(`[parent] Child#${e}: ${y.message}`)),a.on("message",y=>{if(!y||typeof y!="object")return;const l=y;l.type==="ready"?(r.socketPath=String(l.socketPath),r.healthy=!0,r.booting=!1,console.log(`[parent] Child#${r.id} ready on socket ${r.socketPath}`),le&&tell(a,{type:"watcher",testimony:le}),V!==r.bootReloadSeq&&tell(a,{type:"reload"})):l.type==="recycle-request"?X(r,String(l.reason||"it has outgrown its bound")):l.type==="inspector-url"?(r.inspectorUrl=String(l.url),console.log(`[parent] Child#${r.id} Debugger listening on ${l.url}`)):l.type==="reloaded"?(r.socketPath=String(l.socketPath),r.healthy=!0,console.log(`[parent] Child#${r.id} hot-swapped on socket ${r.socketPath}`)):l.type==="reload-error"?(r.lastError=String(l.error||"unknown reload error"),console.error(`[parent] Child#${r.id} reload error: ${r.lastError}`),r.healthy=!1):l.type==="boot-error"?(r.lastError=String(l.error||"unknown boot error"),console.error(`[parent] Child#${r.id} boot error: ${r.lastError}`),r.healthy=!1,r.booting=!1):l.type==="pod-memory"||l.type==="pod-memory-retained"||l.type==="pod-memory-released"?G(r,l):l.type==="heap-snapshot-result"&&te.get(Number(l.id))?.(l)}),a.on("exit",(y,l)=>{r.booting=!1;for(const R of r.memory.retained.values()){const W={gen:R.gen,closedAt:R.closedAt,ageMs:Date.now()-R.closedAt,reason:"pod-exited"};console.log(`[parent] ${(0,pod_memory_1.describeReleased)(`Child#${r.id}`,SERVICE_NAME||f,W)}`),w.publish((0,pod_memory_events_1.buildPodReleasedEvent)(F(r),W,Date.now()))}r.memory.retained.clear(),r.memory.latest&&w.publish((0,pod_memory_events_1.buildPodExitedEvent)(F(r),{code:y,signal:l},Date.now())),r.debugSlot>=0&&(B.delete(r.debugSlot),r.debugSlot=-1);const v=l==="SIGINT"||l==="SIGTERM"||y===0;if(v?console.log(`[parent] Child#${r.id} exited cleanly (code=${y}, signal=${l??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${r.id} crashed (code=${y}, signal=${l??"none"}, pid=${r.proc.pid}, ${r.healthy?"was serving":"was not ready"})`,y,l,r.tail.snapshot())),r.lastError||(r.lastError=`Child exited abnormally (code=${y}, signal=${l??"none"})`)),r.healthy=!1,cleanupSock(r.socketPath,r.proc.pid),r.socketPath=void 0,!v&&!T){const R=E.indexOf(r);R>=0&&E.splice(R,1);const W=Date.now();if(x=x.filter(U=>W-U<3e4),x.length>=5){console.error(`[parent] crash-loop detected (${x.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}x.push(W),setTimeout(()=>{const U=E.filter(de=>de.proc.killed===!1).length;!T&&U<$&&(console.log(`[parent] respawning a crashed pod (${U}/${$} live)`),re().catch(()=>{}))},1500).unref?.()}}),E.push(r),C(r),r}h(re,"de"),n(re,"spawnChild");async function fe(e){$=Math.max($,e);const o=E.filter(d=>d.proc.killed===!1),c=e-o.length;for(let d=0;d<c;d++)await re()}h(fe,"Ee"),n(fe,"ensurePoolSize");async function t(){const e=E.filter(o=>(!o.healthy||typeof o.socketPath!="string")&&!Pe(o));await Promise.all(e.map(async o=>{try{o.proc.kill("SIGTERM")}catch{}cleanupSock(o.socketPath,o.proc.pid),o.socketPath=void 0;const c=E.indexOf(o);c>=0&&E.splice(c,1),await re()}))}h(t,"t"),n(t,"respawnUnhealthy");const i=new Set;let p=!1,m=0;const _=3e4,S=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function X(e,o){const c=n(a=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${o}) \u2014 not now: ${a}`),tell(e.proc,{type:"recycle-declined"})},"decline");if(T||!E.includes(e))return;if(p)return c("another pod is already being replaced");const d=Date.now()-m;if(d<_)return c(`a pod was replaced ${Math.round(d/1e3)}s ago`);p=!0,console.log(`[parent] Child#${e.id} asked for a fresh pod \u2014 ${o}. Starting one; Child#${e.id} keeps serving until it is ready.`);try{const a=await re(),r=Date.now()+ae;for(;!a.healthy&&!a.lastError&&!ce(a)&&Date.now()<r;)await new Promise(v=>{setTimeout(v,100).unref?.()});if(T)return;if(!a.healthy){const v=E.indexOf(a);v>=0&&E.splice(v,1);try{a.proc.kill("SIGTERM")}catch{}return c(`its successor Child#${a.id} did not come up (${a.lastError??"no answer in time"})`)}const y=E.indexOf(e);y>=0&&E.splice(y,1),e.healthy=!1,i.add(e);const l=setTimeout(()=>{if(!ce(e)){console.error(`[parent] Child#${e.id} was still running ${S}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},S);if(l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),i.delete(e)}),!tell(e.proc,{type:"retire"}))try{e.proc.kill("SIGTERM")}catch{}m=Date.now(),console.log(`[parent] Child#${a.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${a.proc.pid})`)}catch(a){c(`could not start a successor: ${a?.message??String(a)}`)}finally{p=!1}}h(X,"q"),n(X,"recyclePod");const L=[];function k(e){const o=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),c=path.join(o,`${e}.js`);if(!fs.existsSync(c))return console.error(`[parent] Worker "${e}" bundle not found: ${c}`),null;const d=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&d.unshift("--require","@swc-node/register");try{d.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const a=inspectorPortFor(I||9229,ue+Math.max(0,WORKER_NAMES.indexOf(e)));d.push(I?`--inspect=127.0.0.1:${a}`:`--inspect-port=127.0.0.1:${a}`,...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const r=(0,node_child_process_1.fork)(c,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:d,execPath:ne(`w-${e}`)}),y={name:e,proc:r,debugPort:a};return L.push(y),r.on("error",l=>console.error(`[parent] Worker "${e}": ${l.message}`)),Se(r,`\x1B[35m[worker:${e}] \x1B[0m`,l=>{const v=/Debugger listening on (wss?:\/\/\S+)/.exec(l);v&&(y.inspectorUrl=v[1])}),r.on("exit",(l,v)=>{v==="SIGINT"||v==="SIGTERM"||l===0||console.error(`[parent] Worker "${e}" crashed (code=${l}, signal=${v??"none"})`);const R=L.indexOf(y);R>=0&&L.splice(R,1)}),console.log(`[parent] Worker "${e}" started (pid=${r.pid})`),y}h(k,"A"),n(k,"spawnWorkerProcess");const z=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),Oe=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),ge=new Set,De=n(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function ve(e,o){if(ge.delete(e),!De(e)){console.error(`[parent] Worker "${e.name}" (pid=${e.proc.pid}) ${o} \u2014 killing it. A worker that returns from its SIGTERM handler but leaves a handle open (a Redis client handed to BullMQ is not closed by worker.close()) never exits on its own: close what it opened, or call process.exit().`);try{e.proc.kill("SIGKILL")}catch{}}}h(ve,"Ie"),n(ve,"killDrainingWorker");function Ce(e){try{e.proc.kill("SIGTERM")}catch{}if(De(e))return;const o=[...ge].filter(d=>d.name===e.name);for(;o.length>=Oe;)ve(o.shift(),`was still running when a later reload replaced its successor (cap ${Oe})`);ge.add(e);const c=setTimeout(()=>ve(e,`was still running ${z}ms after SIGTERM`),z);c.unref?.(),e.proc.once("exit",()=>{clearTimeout(c),ge.delete(e)})}h(Ce,"Ge"),n(Ce,"retireWorker");function xe(){for(const e of[...L])Ce(e);L.length=0;for(const e of WORKER_NAMES)k(e)}h(xe,"We"),n(xe,"restartWorkers");const Fe=debounce(async()=>{V+=1;const e=ee();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const o of e)tell(o.proc,{type:"reload"})}await t(),WORKER_NAMES.length>0&&xe()},200),ye=n(async(e,o)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){if(!mayDriveThePool(e))return refusePoolCommand(e,o);o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0})),he(e).then(a=>J(a?.watcher)),Fe();return}if(e.method==="POST"&&e.url==="/webpack/watching"){if(!mayDriveThePool(e))return refusePoolCommand(e,o);const a=await he(e);J(a),o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,relayed:le!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){if(!mayDriveThePool(e))return refusePoolCommand(e,o);let a="";e.on("data",r=>{a+=r}),e.on("end",async()=>{let r=0;try{r=Number(JSON.parse(a||"{}").pods)||0}catch{}r=Math.max(1,Math.min(r,16)),await fe(r),o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,pods:r}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const a=new URL(e.url,"http://localhost").searchParams,r=Math.min(Math.max(Number(a.get("tail")??"100")||0,0),A),y=a.get("follow")!=="0",l=a.get("plain")==="1",v=!isOwnerOnlyTransport(e);o.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const R=r===0?"":H.slice(-r).join(""),W=l?R.replace(Y,""):R;if(o.write(v?(0,inspector_secret_1.redactInspectorUrls)(W):W),!y){o.end();return}const U={res:o,plain:l,redact:v};K.add(U),e.on("close",()=>K.delete(U));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!isOwnerOnlyTransport(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(a)&&a>1024&&(I=a),I||(I=9229);const r=E.filter(l=>l.proc.exitCode===null&&l.proc.signalCode===null);for(const l of r)l.debugSlot<0&&(l.debugSlot=N()),tell(l.proc,{type:"debug-open",port:inspectorPortFor(I,l.debugSlot)});for(const l of L)try{l.proc.kill("SIGUSR1")}catch{}const y=Date.now()+2e3;for(;Date.now()<y&&r.some(l=>!l.inspectorUrl);)await new Promise(l=>setTimeout(l,50));o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,basePort:I,fixedPorts:usesFixedInspectorPorts(),pods:r.map(l=>({id:l.id,inspectorUrl:l.inspectorUrl??null})),workers:L.map(l=>({name:l.name,port:l.debugPort||inspectorPortOf(l.inspectorUrl),inspectorUrl:l.inspectorUrl??null}))},null,2)+`
16
+ `);return}if(e.method==="GET"&&e.url==="/webpack/memory"){if(!isOwnerOnlyTransport(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),r=[...E,...i].map(y=>({id:y.id,pid:y.proc.pid??0,podGeneration:POD_GENERATION,healthy:y.healthy,retiring:i.has(y),latest:y.memory.latest??null,history:y.memory.history,retained:[...y.memory.retained.values()]}));o.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),o.end(JSON.stringify({ok:!0,service:SERVICE_NAME,project:f,bundle:{path:a},pods:r},null,2)+`
17
+ `);return}if(e.method==="POST"&&e.url?.startsWith("/webpack/heap")){if(!isOwnerOnlyTransport(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=n((M,_e)=>{o.writeHead(M,{"content-type":"application/json"}),o.end(JSON.stringify(_e,null,2)+`
18
+ `)},"reply"),r=Number(new URL(e.url,"http://localhost").searchParams.get("pod")||""),y=Number.isInteger(r)&&r>0?E.find(M=>M.id===r):ee()[0];if(!y||ce(y)){a(404,{ok:!1,error:r?`no pod #${r} \u2014 running: ${E.map(M=>`#${M.id}`).join(", ")||"none"}`:"no pod is serving"});return}if(te.size>0){a(409,{ok:!1,refused:"a heap snapshot is already being written for this service"});return}const l=(0,heap_snapshot_1.heapSnapshotPolicy)(),v=(0,heap_snapshot_1.heapSnapshotDir)();let R;try{(0,heap_snapshot_1.ensureHeapDir)(v);const M=(0,heap_snapshot_1.pruneHeapSnapshots)(v,l);M.length&&console.log(`[parent] removed ${M.length} older heap snapshot(s) from ${v}`),R=path.join(v,(0,heap_snapshot_1.heapSnapshotFileName)(SERVICE_NAME||f,y.id,y.proc.pid??0,y.memory.latest?.gen??0,Date.now()))}catch(M){a(500,{ok:!1,error:`cannot prepare ${v}: ${M.message}`});return}const W=++ke,U=(0,heap_snapshot_1.availableSystemMemory)();let de;const b=await new Promise(M=>{const _e=setTimeout(()=>M(void 0),6e5);_e.unref?.(),te.set(W,qe=>{clearTimeout(_e),M(qe)}),de=n(()=>{clearTimeout(_e),M(void 0)},"onExit"),y.proc.once("exit",de),tell(y.proc,{type:"heap-snapshot",id:W,file:R,...U?{available:U}:{}})||de()}).finally(()=>{te.delete(W),de&&y.proc.off("exit",de)});if(!b){a(504,{ok:!1,pod:y.id,error:"the pod did not finish the snapshot (it exited, or took longer than 10 minutes)"});return}b.ok?console.log(`[parent] Child#${y.id} wrote a ${Math.round((b.bytes??0)/1048576)} MB heap snapshot in ${b.ms} ms \u2192 ${b.file}`):console.warn(`[parent] Child#${y.id} heap snapshot not taken: ${b.refused??b.error}`),a(b.ok?200:b.refused?409:500,{ok:b.ok,pod:y.id,pid:y.proc.pid??0,service:SERVICE_NAME,bundle:path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),...U?{available:U}:{},...b.file?{file:b.file,bytes:b.bytes,ms:b.ms}:{},...b.live!==void 0?{live:b.live}:{},...b.gen!==void 0?{gen:b.gen,closedAlive:b.closedAlive??[]}:{},...b.refused?{refused:b.refused}:{},...b.error?{error:b.error}:{}});return}const c=matchGatewayRoute(e);if(c?.prependPrefix&&!e.url?.startsWith(c.prependPrefix)&&(e.url=c.prependPrefix+(e.url??"/")),c?.target){applyGatewayMiddleware(e),g.web(e,o,{target:c.target,autoRewrite:typeof c.target=="string",changeOrigin:!0},a=>{console.error(`[parent] proxy error to ${c.target}:`,a),o.statusCode=502,o.setHeader("content-type","text/plain; charset=utf-8"),o.end(`Upstream error from ${c.target}.`)});return}if(SERVICE_PREFIX){if(!e.url?.startsWith(SERVICE_PREFIX)){o.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),o.end(`Not found. This service is mounted at ${SERVICE_PREFIX}/`);return}e.url=e.url.slice(SERVICE_PREFIX.length)||"/"}applyGatewayMiddleware(e);const d=q();if(!d){o.statusCode=503,o.setHeader("content-type","text/plain; charset=utf-8");const a=E.map(r=>`#${r.id}: ${r.lastError??"no error recorded"}`).join(`
19
+ `);o.end(`No healthy children available. POST /webpack/reload to recover.
18
20
 
19
21
  Last errors:
20
- ${c}`);return}u.web(e,t,{target:{socketPath:a.socketPath},headers:{"x-proxy-child-id":String(a.id)}},c=>{console.error(`[parent] proxy error to Child#${a.id}:`,c),a.healthy=!1,a.lastError=String(c?.stack||c),t.statusCode=502,t.setHeader("content-type","text/plain; charset=utf-8"),t.end(`Upstream error from Child#${a.id}. Try /webpack/reload.
21
- ${a.lastError}`)})},"requestHandler");let z,ce,we;const ue=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),t=GATEWAY_ROUTES.map(a=>a.host).filter(a=>!!a),d=path.join(process.cwd(),"node_modules",".cache","davnx-webpack");ce=e.getCertificateSync(d,SERVICE_NAME||"lensmcp.dev",t),we=e.caCertPath(),z=require("node:https").createServer({key:ce,cert:ce},ie)}else z=http.createServer(ie);const ve=s((e,t,d)=>{const a=matchGatewayRoute(e);if(a?.prependPrefix&&!e.url?.startsWith(a.prependPrefix)&&(e.url=a.prependPrefix+(e.url??"/")),a?.target){u.ws(e,t,d,{target:a.target,changeOrigin:!0},()=>t.destroy());return}const c=U();if(!c){t.destroy();return}u.ws(e,t,d,{target:{socketPath:c.socketPath}},()=>t.destroy())},"upgradeHandler");z.on("upgrade",ve);const Te=2e3;let Ne=!1;z.on("error",e=>{if(e.code==="EADDRINUSE"){Ne||(Ne=!0,console.warn(`[parent] public port ${m} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${Te/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!T&&!z.listening&&z.listen(m)},Te).unref?.();return}console.error("[parent] public server error:",e)}),await r(_);for(const e of WORKER_NAMES)ye(e);z.listen(m,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${m}`),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(" ")}`),we&&(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 ${we}`)),console.log(`POST ${e}://localhost:${m}/webpack/reload to trigger rolling swap/respawn`)});const he=Number(process.env.INTERNAL_PORT||0);if(he>0){const e=s((d,a)=>{const c=U();if(!c){a.statusCode=503,a.setHeader("content-type","text/plain; charset=utf-8"),a.end("No healthy children available.");return}u.web(d,a,{target:{socketPath:c.socketPath}},n=>{c.healthy=!1,c.lastError=String(n?.stack||n),a.statusCode=502,a.end(`Upstream error from Child#${c.id}.`)})},"internalHandler"),t=http.createServer(e);t.on("upgrade",(d,a,c)=>{const n=U();if(!n){a.destroy();return}u.ws(d,a,c,{target:{socketPath:n.socketPath}},()=>a.destroy())}),t.on("error",d=>{console.warn(`[parent] internal port ${he} unavailable (${d.code}).`)}),t.listen(he,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${he}`)}),ue.push(t)}const Ie=(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of Ie){if(!Number.isInteger(e)||e===Number(m))continue;const t=GATEWAY_HTTPS?require("node:https").createServer({key:ce,cert:ce},ie):http.createServer(ie);t.on("upgrade",ve),t.on("error",d=>{console.warn(`[parent] extra port ${e} unavailable (${d.code}) \u2014 main port still up.`)}),t.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),ue.push(t)}const _e=path.join(SOCK_DIR,"parent.sock");claimSock(_e);const me=http.createServer(ie);me.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),me.listen(_e),ue.push(me);const Ae=s(async e=>{T=!0,console.log(`[${e}] [parent] shutting down\u2026`),cleanupSock(_e);for(const t of ue)try{t.close()}catch{}for(const t of H)try{t.proc.kill("SIGTERM")}catch{}for(const t of[...se])pe(t,"was still draining when its devserver shut down");for(const t of f)try{t.proc.kill("SIGTERM")}catch{}for(const t of w){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,()=>{Ae(e)}));const fe=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(fe)&&fe>0){const e=setInterval(()=>{try{process.kill(fe,0)}catch(t){if(t.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${fe}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),Ae("gateway-gone")}},5e3);e.unref()}})()}
22
+ ${a}`);return}g.web(e,o,{target:{socketPath:d.socketPath},headers:{"x-proxy-child-id":String(d.id)}},a=>{console.error(`[parent] proxy error to Child#${d.id}:`,a),d.healthy=!1,d.lastError=String(a?.stack||a),o.statusCode=502,o.setHeader("content-type","text/plain; charset=utf-8"),o.end(`Upstream error from Child#${d.id}. Try /webpack/reload.
23
+ ${d.lastError}`)})},"requestHandler");let pe,me,Me;const be=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),o=GATEWAY_ROUTES.map(d=>d.host).filter(d=>!!d),c=path.join(process.cwd(),"node_modules",".cache","davnx-webpack");me=e.getCertificateSync(c,SERVICE_NAME||"lensmcp.dev",o),Me=e.caCertPath(),pe=require("node:https").createServer({key:me,cert:me},ye)}else pe=http.createServer(ye);const Ge=n((e,o,c)=>{const d=matchGatewayRoute(e);if(d?.prependPrefix&&!e.url?.startsWith(d.prependPrefix)&&(e.url=d.prependPrefix+(e.url??"/")),d?.target){g.ws(e,o,c,{target:d.target,changeOrigin:!0},()=>o.destroy());return}const a=q();if(!a){o.destroy();return}g.ws(e,o,c,{target:{socketPath:a.socketPath}},()=>o.destroy())},"upgradeHandler");pe.on("upgrade",Ge);const Le=2e3;let We=!1;pe.on("error",e=>{if(e.code==="EADDRINUSE"){We||(We=!0,console.warn(`[parent] public port ${P} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${Le/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!T&&!pe.listening&&pe.listen(P)},Le).unref?.();return}console.error("[parent] public server error:",e)}),await fe(oe);for(const e of WORKER_NAMES)k(e);pe.listen(P,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${P}`),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(o=>`${o.host??"*"}${o.prefix??"/"}\u2192${o.target??"children"}`).join(" ")}`),Me&&(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 ${Me}`)),console.log(`POST ${e}://localhost:${P}/webpack/reload to trigger rolling swap/respawn`)});const Ae=Number(process.env.INTERNAL_PORT||0);if(Ae>0){const e=n((c,d)=>{const a=q();if(!a){d.statusCode=503,d.setHeader("content-type","text/plain; charset=utf-8"),d.end("No healthy children available.");return}g.web(c,d,{target:{socketPath:a.socketPath}},r=>{a.healthy=!1,a.lastError=String(r?.stack||r),d.statusCode=502,d.end(`Upstream error from Child#${a.id}.`)})},"internalHandler"),o=http.createServer(e);o.on("upgrade",(c,d,a)=>{const r=q();if(!r){d.destroy();return}g.ws(c,d,a,{target:{socketPath:r.socketPath}},()=>d.destroy())}),o.on("error",c=>{console.warn(`[parent] internal port ${Ae} unavailable (${c.code}).`)}),o.listen(Ae,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${Ae}`)}),be.push(o)}const He=(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of He){if(!Number.isInteger(e)||e===Number(P))continue;const o=GATEWAY_HTTPS?require("node:https").createServer({key:me,cert:me},ye):http.createServer(ye);o.on("upgrade",Ge),o.on("error",c=>{console.warn(`[parent] extra port ${e} unavailable (${c.code}) \u2014 main port still up.`)}),o.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),be.push(o)}const Ne=path.join(SOCK_DIR,"parent.sock");claimSock(Ne);const Te=http.createServer(ye);Te.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),Te.listen(Ne),be.push(Te);const Ue=n(async e=>{T=!0,console.log(`[${e}] [parent] shutting down\u2026`);const o=sockOwnedByAnother(Ne);cleanupSock(Ne);for(const c of be)if(!(c===Te&&o))try{c.close()}catch{}for(const c of L)try{c.proc.kill("SIGTERM")}catch{}for(const c of[...ge])ve(c,"was still draining when its devserver shut down");for(const c of i)try{c.proc.kill("SIGTERM")}catch{}for(const c of E){try{c.proc.kill("SIGTERM")}catch{}c.socketPath&&cleanupSock(c.socketPath,c.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,()=>{Ue(e)}));const Ie=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(Ie)&&Ie>0){const e=setInterval(()=>{try{process.kill(Ie,0)}catch(o){if(o.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${Ie}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),Ue("gateway-gone")}},5e3);e.unref()}})()}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.23.13",
3
+ "version": "1.24.0",
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.23.13",
61
- "@lensmcp/node-instrumentation": "1.23.13",
62
- "@lensmcp/nx-plugin": "1.23.13",
60
+ "@lensmcp/core": "1.24.0",
61
+ "@lensmcp/node-instrumentation": "1.24.0",
62
+ "@lensmcp/nx-plugin": "1.24.0",
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",
@@ -0,0 +1,135 @@
1
+ import { type WorkspaceLibrary } from './workspace-libraries';
2
+ export declare const HEAVY_BARREL_RULE = "bundle/heavy-barrel-import";
3
+ /** One module of a compilation, as the rule needs it. */
4
+ export interface CompiledModule {
5
+ /** Absolute path of its file (no loaders, no query). */
6
+ resource: string;
7
+ /** Bytes webpack attributes to it — its source after loaders. */
8
+ size: number;
9
+ /** Files it imports (absolute; externals left out). */
10
+ dependencies: string[];
11
+ }
12
+ /** How long the compilation that produced the module set took, for the compile-time estimate. */
13
+ export interface CompileFacts {
14
+ durationMs: number;
15
+ /** Modules it actually built (a cached module costs nothing). */
16
+ builtModules: number;
17
+ }
18
+ /** Everything one project's code compiled to. */
19
+ export interface ProjectCompilation {
20
+ project: string;
21
+ modules: CompiledModule[];
22
+ compile?: CompileFacts;
23
+ }
24
+ /** Source access — injected, so the rule stays a pure function in tests and bounded in the plugin. */
25
+ export interface SourceReader {
26
+ read(file: string): string | undefined;
27
+ exists(file: string): boolean;
28
+ }
29
+ export interface HeavyBarrelThresholds {
30
+ /** The share of the library's files compiled into the project at or above which it may fire. */
31
+ minShare: number;
32
+ /** Modules compiled from the library at or above which it may fire … */
33
+ minModules: number;
34
+ /** … or their bytes. */
35
+ minBytes: number;
36
+ /** It fires only when what the importers need is at most this share of what is compiled ("few symbols"). */
37
+ maxNeededShare: number;
38
+ }
39
+ /**
40
+ * Calibrated on FoodGuard's seven real dev bundles: five services needed 6–39% of the contracts modules they
41
+ * compiled (fires), registry 50.2% — 33 symbols, 445 modules avoidable (fires at 0.6, a miss at 0.5) — and engine 87%
42
+ * with 1,727 symbols, a service that genuinely uses most of the library (quiet).
43
+ */
44
+ export declare const DEFAULT_HEAVY_BARREL_THRESHOLDS: HeavyBarrelThresholds;
45
+ /** One file that imports the root, and what it takes. */
46
+ export interface RootImporter {
47
+ /** Workspace-relative. */
48
+ file: string;
49
+ /** Value symbols it takes from the root (capped). */
50
+ symbols: string[];
51
+ /** Type-only names it imports — erased, no cost. */
52
+ typeOnly: number;
53
+ /** Its use could not be read: it counts as needing the whole library. */
54
+ wholeModule: boolean;
55
+ /** The imports that would replace its root import. */
56
+ rewrite: string[];
57
+ }
58
+ /** An entry that would satisfy some symbols — existing, or one the library should add. */
59
+ export interface SubpathSuggestion {
60
+ specifier: string;
61
+ /** Workspace-relative file the specifier should resolve to. */
62
+ entry: string;
63
+ symbols: string[];
64
+ /** False: the alias does not exist yet — add it (tsconfig `paths` + package `exports`). */
65
+ exists: boolean;
66
+ }
67
+ /** The rule's measurement of one (project, library) pair reached through its root. */
68
+ export interface BarrelMeasurement {
69
+ project: string;
70
+ library: string;
71
+ /** Workspace-relative. */
72
+ libraryRoot: string;
73
+ rootEntry: string;
74
+ libraryFiles: number;
75
+ compiledModules: number;
76
+ compiledBytes: number;
77
+ /** compiled ÷ library files. */
78
+ share: number;
79
+ rootImporterCount: number;
80
+ /** The biggest takers first (capped). */
81
+ rootImporters: RootImporter[];
82
+ /** Files INSIDE the library that import its own root: they keep the barrel compiled after every importer moves. */
83
+ internalRootImporters: string[];
84
+ /** Distinct value symbols the importers take from the root. */
85
+ symbols: number;
86
+ /** Symbols the scan could not follow to a file. */
87
+ unresolvedSymbols: string[];
88
+ /** Symbols the barrel defines itself: importing one runs the whole barrel — they belong in a module of their own. */
89
+ barrelDefines: string[];
90
+ neededModules: number;
91
+ neededBytes: number;
92
+ avoidableModules: number;
93
+ avoidableBytes: number;
94
+ subpaths: SubpathSuggestion[];
95
+ fires: boolean;
96
+ /** Why it does not fire: the first condition it missed. */
97
+ quiet?: string;
98
+ }
99
+ export interface BarrelAnalysisInput {
100
+ workspaceRoot: string;
101
+ libraries: WorkspaceLibrary[];
102
+ projects: ProjectCompilation[];
103
+ thresholds: HeavyBarrelThresholds;
104
+ source: SourceReader;
105
+ /** True once the analysis is out of time: it stops and says it is incomplete. */
106
+ expired?: () => boolean;
107
+ }
108
+ export interface BarrelAnalysis {
109
+ /** False when it ran out of time — nothing may be concluded (least of all a retraction). */
110
+ complete: boolean;
111
+ measurements: BarrelMeasurement[];
112
+ }
113
+ /** Measure every (project, library) pair reached through a root barrel. */
114
+ export declare function analyzeBarrelImports(input: BarrelAnalysisInput): BarrelAnalysis;
115
+ /** Executor memory per compiled module, MB — the fit to FoodGuard's seven watchers (issues/open/pod-memory-05). */
116
+ export declare const EXECUTOR_MB_PER_MODULE = 0.374;
117
+ /** A finding's text, evidence and cost — the payload a finding event carries. */
118
+ export interface HeavyBarrelFindingText {
119
+ subject: string;
120
+ severity: 'warning' | 'error';
121
+ title: string;
122
+ summary: string;
123
+ evidence: Record<string, unknown>;
124
+ cost: {
125
+ modules: number;
126
+ bytes: number;
127
+ compileMs?: number;
128
+ memoryMb: number;
129
+ basis: string;
130
+ };
131
+ fix: string[];
132
+ verify: string;
133
+ }
134
+ /** What an agent reads: the measurement, as a problem with a fix and a way to prove it. */
135
+ export declare function describeHeavyBarrel(m: BarrelMeasurement, compile?: CompileFacts): HeavyBarrelFindingText;
@@ -0,0 +1 @@
1
+ "use strict";var H=Object.defineProperty;var M=(e,o)=>H(e,"name",{value:o,configurable:!0});var C=Object.defineProperty,y=M((e,o)=>C(e,"name",{value:o,configurable:!0}),"y");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EXECUTOR_MB_PER_MODULE=exports.DEFAULT_HEAVY_BARREL_THRESHOLDS=exports.HEAVY_BARREL_RULE=void 0,exports.analyzeBarrelImports=analyzeBarrelImports,exports.describeHeavyBarrel=describeHeavyBarrel;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),workspace_libraries_1=require("./workspace-libraries"),source_scan_1=require("./source-scan");exports.HEAVY_BARREL_RULE="bundle/heavy-barrel-import",exports.DEFAULT_HEAVY_BARREL_THRESHOLDS={minShare:.5,minModules:100,minBytes:1024*1024,maxNeededShare:.6};const MAX_IMPORTERS_LISTED=8,MAX_SYMBOLS_LISTED=12,MAX_SUBPATHS_LISTED=12,MAX_INTERNAL_LISTED=6,MAX_EXPORT_DEPTH=24,MAX_UNRESOLVED_SHARE=.1;class AliasResolver{static{M(this,"AliasResolver")}static{y(this,"AliasResolver")}constructor(o,r){this.exists=r,this.exact=new Map,this.wildcards=[];for(const s of o){this.exact.set(s.name,s.rootEntry);for(const i of s.aliases)this.exact.set(i,s.rootEntry);for(const i of s.subpaths)this.exact.set(i.specifier,i.entry);for(const i of s.wildcards)this.wildcards.push(i)}this.wildcards.sort((s,i)=>i.prefix.length-s.prefix.length)}resolve(o,r){if(r.startsWith("."))return(0,workspace_libraries_1.resolveSourceFile)(path.resolve(path.dirname(o),r),this.exists);const s=this.exact.get(r);if(s)return s;const i=this.wildcards.find(u=>r.startsWith(u.prefix));return i?(0,workspace_libraries_1.resolveSourceFile)(path.join(i.dir,r.slice(i.prefix.length)),this.exists):void 0}}class ExportReader{static{M(this,"ExportReader")}static{y(this,"ExportReader")}constructor(o,r){this.source=o,this.aliases=r,this.cache=new Map,this.resolved=new Map}index(o){let r=this.cache.get(o);if(r===void 0){const s=this.source.read(o);r=s===void 0?null:(0,source_scan_1.scanExports)(s),this.cache.set(o,r)}return r}resolve(o,r){const s=`${o}\0${r}`;let i=this.resolved.get(s);return i===void 0&&(i=this.follow(o,r,0,new Set)??null,this.resolved.set(s,i)),i??void 0}follow(o,r,s,i){if(s>MAX_EXPORT_DEPTH)return;const u=`${o}\0${r}`;if(i.has(u))return;i.add(u);const a=this.index(o);if(!a)return;if(a.values.has(r))return{file:o,typeOnly:!1};if(a.types.has(r))return{file:o,typeOnly:!0};const l=a.namespaces.get(r);if(l!==void 0){const p=this.aliases.resolve(o,l);return p?{file:p,typeOnly:!1}:void 0}const d=a.reexports.get(r);if(d){const p=this.aliases.resolve(o,d.specifier);if(!p)return;const c=this.follow(p,d.name,s+1,i);return c?{...c,typeOnly:c.typeOnly||d.typeOnly}:void 0}if(r!=="default")for(const p of a.stars){const c=this.aliases.resolve(o,p);if(!c)continue;const h=this.follow(c,r,s+1,i);if(h)return h}}}const rel=y((e,o)=>path.relative(e,o)||o,"rel");function readRootUse(e,o,r,s){const i=y(a=>l=>s.resolve(a,l)===e.rootEntry,"toBarrel"),u=[];for(const a of o){const l=/\.(?:[cm]?[jt]sx?)$/.test(a)?r.source.read(a):void 0;if(l===void 0){u.push({file:a,values:[],typeOnly:0,wholeModule:!0});continue}const d=(0,source_scan_1.scanImports)(l,i(a));if(d.length===0){u.push({file:a,values:[],typeOnly:0,wholeModule:!0});continue}const p=new Set;let c=0,h=!1;for(const x of d){h||=x.wholeModule;for(const f of x.names)f.typeOnly?c++:p.add(f.imported)}u.push({file:a,values:[...p].sort(),typeOnly:c,wholeModule:h})}return{importers:u}}M(readRootUse,"readRootUse"),y(readRootUse,"readRootUse");function closureWithin(e,o,r,s,i){const u=new Set,a=[...o].filter(l=>l!==s&&r.has(l)&&i(l)===e);for(;a.length;){const l=a.pop();if(!u.has(l)){u.add(l);for(const d of r.get(l)?.dependencies??[])d!==s&&!u.has(d)&&i(d)===e&&r.has(d)&&a.push(d)}}return u}M(closureWithin,"closureWithin"),y(closureWithin,"closureWithin");function subpathFor(e,o,r,s,i,u,a=0){if(r===e.rootEntry)return;const l=u(r);if(l!==e)return l&&a<2?subpathFor(l,o,r,s,i,u,a+1):void 0;const d=e.subpaths.filter(f=>r.startsWith(path.dirname(f.entry)+path.sep)||r===f.entry).filter(f=>s.resolve(f.entry,o)?.file===r).sort((f,$)=>$.entry.length-f.entry.length);if(d[0])return{specifier:d[0].specifier,entry:d[0].entry,exists:!0};const p=path.dirname(e.rootEntry);for(const f of e.wildcards){if(!r.startsWith(f.dir))continue;const $=r.slice(f.dir.length).split(path.sep).join("/").replace(/\.(?:[cm]?[jt]sx?)$/,""),w=$.split("/")[0],v=(0,workspace_libraries_1.resolveSourceFile)(path.join(f.dir,w),i.exists);return v&&v!==r&&s.resolve(v,o)?.file===r?{specifier:`${f.prefix}${w}`,entry:v,exists:!0}:{specifier:`${f.prefix}${$.replace(/\/index$/,"")}`,entry:r,exists:!0}}const c=path.relative(p,r).split(path.sep),h=c.length>1?c[0]:c[0].replace(/\.(?:[cm]?[jt]sx?)$/,""),x=c.length>1?(0,workspace_libraries_1.resolveSourceFile)(path.join(p,h),i.exists)??path.join(p,h,"index.ts"):r;return{specifier:`${e.name}/${h}`,entry:x,exists:!1}}M(subpathFor,"subpathFor"),y(subpathFor,"subpathFor");function measure(e,o,r,s,i,u,a){if(!r.has(o.rootEntry))return;const l=y(t=>a(t)===o,"inLib"),d=e.modules.filter(t=>l(t.resource)),p=e.modules.filter(t=>!l(t.resource)&&t.dependencies.includes(o.rootEntry)).map(t=>t.resource).sort();if(p.length===0)return;const c=s.workspaceRoot,h=s.thresholds,x=d.filter(t=>t.resource!==o.rootEntry&&t.dependencies.includes(o.rootEntry)).map(t=>t.resource),f=new Set;for(const t of e.modules)if(!l(t.resource))for(const n of t.dependencies)n!==o.rootEntry&&l(n)&&f.add(n);const $=readRootUse(o,p,s,i),w=new Set(f),v=new Set,S=new Map,R=new Set,_=new Set;for(const t of $.importers)for(const n of t.values){if(_.add(n),S.has(n)||v.has(n)||R.has(n))continue;const m=u.resolve(o.rootEntry,n);m?m.file===o.rootEntry?R.add(n):m.typeOnly||(S.set(n,m.file),w.add(m.file)):v.add(n)}if(R.size>0)for(const t of u.index(o.rootEntry)?.valueImports??[]){const n=i.resolve(o.rootEntry,t);n&&w.add(n)}const L=$.importers.some(t=>t.wholeModule),B=L?new Set(d.map(t=>t.resource)):closureWithin(o,w,r,o.rootEntry,a),T=y(t=>{let n=0;for(const m of t)n+=r.get(m)?.size??0;return n},"sizeOf"),U=d.reduce((t,n)=>t+n.size,0),z=T(B),A=d.filter(t=>!B.has(t.resource)),I=o.files>0?Math.min(1,d.length/o.files):1,O=new Map,D=new Map,k=y((t,n)=>{let m=D.get(t);return m===void 0&&(m=subpathFor(o,t,n,u,s.source,a)??null,D.set(t,m)),m??void 0},"choose"),F=$.importers.map(t=>{const n=new Map;for(const m of t.values){const g=S.get(m);if(!g)continue;const E=k(m,g);if(!E)continue;const j=O.get(E.specifier)??{specifier:E.specifier,entry:rel(c,E.entry),symbols:[],exists:E.exists};j.symbols.includes(m)||j.symbols.push(m),O.set(E.specifier,j),n.set(E.specifier,[...n.get(E.specifier)??[],m])}return{file:rel(c,t.file),symbols:t.values.slice(0,MAX_SYMBOLS_LISTED),typeOnly:t.typeOnly,wholeModule:t.wholeModule,rewrite:[...n].sort((m,g)=>m[0].localeCompare(g[0])).map(([m,g])=>`import { ${g.join(", ")} } from '${m}';`)}}).sort((t,n)=>Number(n.wholeModule)-Number(t.wholeModule)||n.symbols.length-t.symbols.length||t.file.localeCompare(n.file)),b={project:e.project,library:o.name,libraryRoot:rel(c,o.root),rootEntry:rel(c,o.rootEntry),libraryFiles:o.files,compiledModules:d.length,compiledBytes:U,share:I,rootImporterCount:p.length,rootImporters:F.slice(0,MAX_IMPORTERS_LISTED),internalRootImporters:x.slice(0,MAX_INTERNAL_LISTED).map(t=>rel(c,t)),symbols:_.size,unresolvedSymbols:[...v].sort().slice(0,MAX_SYMBOLS_LISTED),barrelDefines:[...R].sort().slice(0,MAX_SYMBOLS_LISTED),neededModules:B.size,neededBytes:z,avoidableModules:A.length,avoidableBytes:A.reduce((t,n)=>t+n.size,0),subpaths:[...O.values()].map(t=>({...t,symbols:t.symbols.sort().slice(0,MAX_SYMBOLS_LISTED)})).sort((t,n)=>Number(t.exists)-Number(n.exists)||n.symbols.length-t.symbols.length).slice(0,MAX_SUBPATHS_LISTED),fires:!1};return I<h.minShare?b.quiet=`compiles ${pct(I)} of the library (below ${pct(h.minShare)})`:b.compiledModules<h.minModules&&b.compiledBytes<h.minBytes?b.quiet=`compiles ${b.compiledModules} modules / ${mb(b.compiledBytes)} (below ${h.minModules} modules and ${mb(h.minBytes)})`:L?b.quiet="an importer uses the whole module (a namespace passed on, require(), import(), export *) \u2014 it may need all of it":_.size>0&&v.size/_.size>MAX_UNRESOLVED_SHARE?b.quiet=`could not follow ${v.size} of ${_.size} symbols to their files`:b.neededModules>h.maxNeededShare*b.compiledModules?b.quiet=`its importers need ${b.neededModules} of the ${b.compiledModules} compiled modules (above ${pct(h.maxNeededShare)})`:b.fires=!0,b}M(measure,"measure"),y(measure,"measure");const pct=y(e=>`${Math.round(e*100)}%`,"pct"),mb=y(e=>`${(e/(1024*1024)).toFixed(1)} MB`,"mb");function analyzeBarrelImports(e){const o=y(l=>e.source.exists(l),"exists"),r=new AliasResolver(e.libraries,o),s=new ExportReader(e.source,r),i=new Map,u=y(l=>(i.has(l)||i.set(l,(0,workspace_libraries_1.libraryOf)(e.libraries,l)),i.get(l)),"owner"),a=[];for(const l of e.projects){const d=new Map(l.modules.map(p=>[p.resource,p]));for(const p of e.libraries){if(e.expired?.())return{complete:!1,measurements:a};const c=measure(l,p,d,e,r,s,u);c&&a.push(c)}}return{complete:!e.expired?.(),measurements:a}}M(analyzeBarrelImports,"analyzeBarrelImports"),y(analyzeBarrelImports,"analyzeBarrelImports"),exports.EXECUTOR_MB_PER_MODULE=.374;function describeHeavyBarrel(e,o){const r=o&&o.builtModules>=50?o.durationMs/o.builtModules:void 0,s=r!==void 0?Math.round(r*e.avoidableModules):void 0,i=Math.round(exports.EXECUTOR_MB_PER_MODULE*e.avoidableModules),u=e.rootImporters.slice(0,3),a=e.subpaths.filter(c=>!c.exists),l=u.map(c=>`${c.file}: replace its \`${e.library}\` import with ${c.rewrite.length?c.rewrite.join(" "):"imports from the subpaths that export what it uses"}`),d=[`Import ${e.library} by subpath, never by its root barrel (${e.rootEntry}). ${e.rootImporterCount} file(s) of ${e.project}'s build import the root; ${l.length?`start with \u2014 ${l.join(" | ")}`:"see evidence.rootImporters"}${e.rootImporterCount>u.length?` (+${e.rootImporterCount-u.length} more: evidence.rootImporters)`:""}.`];a.length&&d.push(`Add the missing subpath entr${a.length===1?"y":"ies"} to the library: ${a.slice(0,4).map(c=>`"${c.specifier}" \u2192 ${c.entry}`).join(", ")} \u2014 in tsconfig.base.json \`compilerOptions.paths\` and, if the library has a package.json, its \`exports\` ("./<domain>"). Add exact keys (plus "<domain>/*" for single files), never one library-wide "${e.library}/*": a bundler's resolver can rank it above the exact keys and re-route them at runtime while tsc and jest stay green.`),e.barrelDefines.length&&d.push(`${e.rootEntry} defines ${e.barrelDefines.slice(0,4).join(", ")} itself, so importing ${e.barrelDefines.length===1?"it":"them"} runs the whole barrel: move ${e.barrelDefines.length===1?"it":"them"} into a module of ${e.barrelDefines.length===1?"its":"their"} own and import that.`),e.internalRootImporters.length&&d.push(`${e.internalRootImporters.length} file(s) inside ${e.library} import its own root barrel (${e.internalRootImporters.slice(0,3).join(", ")}): make them import the module they need, or every subpath still pulls the whole library in.`),d.push(`Stop the root import from coming back: in ${e.project}'s ESLint config add \`'no-restricted-imports': ['error', { paths: [{ name: '${e.library}', message: 'Import from a domain subpath (${e.library}/<domain>), not the root barrel.' }] }]\`.`);const p=`Save a file of ${e.project} (or restart it) so its dev build recompiles: it should compile about ${e.neededModules} modules of ${e.library} instead of ${e.compiledModules}, and LensMCP retracts this finding \u2014 \`observe_current_status\` no longer lists bundle/heavy-barrel-import for ${e.project}. Then check its pods' live heap (\`memory.pods\`).`;return{subject:e.library,severity:"warning",title:`${e.project} compiles ${e.compiledModules} of the ${e.libraryFiles} files of ${e.library} through its root barrel; its imports need ${e.neededModules}`,summary:`${e.rootImporterCount} file(s) of ${e.project} import ${e.library} through its root entry and take ${e.symbols} symbol(s); those need ${e.neededModules} of the library's modules, but the barrel makes the dev build compile ${e.compiledModules} (${mb(e.compiledBytes)}) \u2014 ${e.avoidableModules} modules (${mb(e.avoidableBytes)}) nothing uses, compiled in every service that does the same and loaded into every pod.`,evidence:{library:e.library,libraryRoot:e.libraryRoot,rootEntry:e.rootEntry,libraryFiles:e.libraryFiles,compiledModules:e.compiledModules,compiledBytes:e.compiledBytes,share:Math.round(e.share*1e3)/1e3,neededModules:e.neededModules,neededBytes:e.neededBytes,avoidableModules:e.avoidableModules,avoidableBytes:e.avoidableBytes,symbols:e.symbols,unresolvedSymbols:e.unresolvedSymbols,barrelDefines:e.barrelDefines,rootImporterCount:e.rootImporterCount,rootImporters:e.rootImporters,internalRootImporters:e.internalRootImporters,subpaths:e.subpaths},cost:{modules:e.avoidableModules,bytes:e.avoidableBytes,...s!==void 0?{compileMs:s}:{},memoryMb:i,basis:`modules/bytes measured in this compilation; memoryMb = ${exports.EXECUTOR_MB_PER_MODULE} MB of webpack-executor heap per compiled module (fitted on FoodGuard's watchers)${s!==void 0?"; compileMs = the last full build's time per built module \xD7 the avoidable modules":""}. Each pod also loads them, at runtime cost the bundle cannot measure.`},fix:d,verify:p}}M(describeHeavyBarrel,"describeHeavyBarrel"),y(describeHeavyBarrel,"describeHeavyBarrel");
@@ -0,0 +1,29 @@
1
+ import { type HeavyBarrelThresholds } from './heavy-barrel-import';
2
+ /** `LENSMCP_PERF_RULES=0` (or `off`/`false`/`no`) turns every performance rule off. */
3
+ export declare const PERF_RULES_ENV = "LENSMCP_PERF_RULES";
4
+ /** How long one analysis may run before it gives up (and concludes nothing). */
5
+ export declare const PERF_RULES_BUDGET_ENV = "LENSMCP_PERF_RULES_BUDGET_MS";
6
+ export declare const DEFAULT_PERF_RULES_BUDGET_MS = 5000;
7
+ /** The env override of each heavy-barrel threshold. */
8
+ export declare const HEAVY_BARREL_ENV: {
9
+ readonly minShare: "LENSMCP_BARREL_MIN_SHARE";
10
+ readonly minModules: "LENSMCP_BARREL_MIN_MODULES";
11
+ readonly minKb: "LENSMCP_BARREL_MIN_KB";
12
+ readonly maxNeededShare: "LENSMCP_BARREL_MAX_NEEDED_SHARE";
13
+ };
14
+ export interface BundleRuleSettings {
15
+ enabled: boolean;
16
+ thresholds: HeavyBarrelThresholds;
17
+ /** Time budget of one analysis, ms. */
18
+ budgetMs: number;
19
+ }
20
+ interface PerfRulesConfigJson {
21
+ enabled?: unknown;
22
+ budgetMs?: unknown;
23
+ [rule: string]: unknown;
24
+ }
25
+ /** `.lensmcp/config.json` `perfRules`, or nothing. */
26
+ export declare function readPerfRulesConfig(workspaceRoot: string): PerfRulesConfigJson | undefined;
27
+ /** The build-side rules' settings for this workspace. */
28
+ export declare function resolveBundleRuleSettings(workspaceRoot: string, env?: NodeJS.ProcessEnv): BundleRuleSettings;
29
+ export {};
@@ -0,0 +1 @@
1
+ "use strict";var p=Object.defineProperty;var o=(r,e)=>p(r,"name",{value:e,configurable:!0});var u=Object.defineProperty,n=o((r,e)=>u(r,"name",{value:e,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.HEAVY_BARREL_ENV=exports.DEFAULT_PERF_RULES_BUDGET_MS=exports.PERF_RULES_BUDGET_ENV=exports.PERF_RULES_ENV=void 0,exports.readPerfRulesConfig=readPerfRulesConfig,exports.resolveBundleRuleSettings=resolveBundleRuleSettings;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),heavy_barrel_import_1=require("./heavy-barrel-import");exports.PERF_RULES_ENV="LENSMCP_PERF_RULES",exports.PERF_RULES_BUDGET_ENV="LENSMCP_PERF_RULES_BUDGET_MS",exports.DEFAULT_PERF_RULES_BUDGET_MS=5e3,exports.HEAVY_BARREL_ENV={minShare:"LENSMCP_BARREL_MIN_SHARE",minModules:"LENSMCP_BARREL_MIN_MODULES",minKb:"LENSMCP_BARREL_MIN_KB",maxNeededShare:"LENSMCP_BARREL_MAX_NEEDED_SHARE"};const OFF=/^(0|off|false|no)$/i;function bounded(r,e,t){const E=typeof r=="string"&&r.trim()!==""?Number(r):r;return typeof E=="number"&&Number.isFinite(E)&&E>=e&&E<=t?E:void 0}o(bounded,"bounded"),n(bounded,"bounded");function readPerfRulesConfig(r){try{const e=JSON.parse(fs.readFileSync(path.join(r,".lensmcp","config.json"),"utf8"))?.perfRules;return e&&typeof e=="object"&&!Array.isArray(e)?e:void 0}catch{return}}o(readPerfRulesConfig,"readPerfRulesConfig"),n(readPerfRulesConfig,"readPerfRulesConfig");function resolveBundleRuleSettings(r,e=process.env){const t=readPerfRulesConfig(r),E=t?.[heavy_barrel_import_1.HEAVY_BARREL_RULE]&&typeof t[heavy_barrel_import_1.HEAVY_BARREL_RULE]=="object"?t[heavy_barrel_import_1.HEAVY_BARREL_RULE]:{},S=!OFF.test((e[exports.PERF_RULES_ENV]??"").trim())&&t?.enabled!==!1&&E.enabled!==!1,_=n((a,d,s,i,L)=>bounded(e[a],s,i)??bounded(d,s,i)??L,"pick"),R=heavy_barrel_import_1.DEFAULT_HEAVY_BARREL_THRESHOLDS;return{enabled:S,thresholds:{minShare:_(exports.HEAVY_BARREL_ENV.minShare,E.minShare,0,1,R.minShare),minModules:_(exports.HEAVY_BARREL_ENV.minModules,E.minModules,0,1e6,R.minModules),minBytes:Math.round(_(exports.HEAVY_BARREL_ENV.minKb,E.minKb,0,1e7,R.minBytes/1024)*1024),maxNeededShare:_(exports.HEAVY_BARREL_ENV.maxNeededShare,E.maxNeededShare,0,1,R.maxNeededShare)},budgetMs:_(exports.PERF_RULES_BUDGET_ENV,t?.budgetMs,100,12e4,exports.DEFAULT_PERF_RULES_BUDGET_MS)}}o(resolveBundleRuleSettings,"resolveBundleRuleSettings"),n(resolveBundleRuleSettings,"resolveBundleRuleSettings");
@@ -0,0 +1,71 @@
1
+ /** The documentation page of every rule — MIRROR of protocol-types' `PERF_RULES_DOC`. */
2
+ export declare const PERF_RULES_DOC = "https://github.com/kiwiapps-ltd/lensmcp/blob/main/docs/lensmcp/concepts/memory-and-build-cost.mdx";
3
+ /** MIRROR of protocol-types' `PerfRuleCost`. */
4
+ export interface PerfRuleCostWire {
5
+ modules?: number;
6
+ bytes?: number;
7
+ compileMs?: number;
8
+ memoryMb?: number;
9
+ basis?: string;
10
+ }
11
+ /** What a rule found — MIRROR of protocol-types' `PerfRuleFindingRaw`, without the kind and identity. */
12
+ export interface PerfFindingWire {
13
+ rule: string;
14
+ subject: string;
15
+ severity: 'warning' | 'error';
16
+ title: string;
17
+ summary: string;
18
+ evidence: Record<string, unknown>;
19
+ cost: PerfRuleCostWire;
20
+ fix: string[];
21
+ verify: string;
22
+ docs: string;
23
+ }
24
+ /** Loose BaseEvent shape — cluster does not depend on `@lensmcp/protocol-types`. */
25
+ export interface PerfRuleBusEvent {
26
+ id: string;
27
+ sessionId: string;
28
+ timestamp: number;
29
+ source: 'perf';
30
+ category: 'performance';
31
+ severity: 'info' | 'warning' | 'error';
32
+ context: {
33
+ sessionId: string;
34
+ projectName: string;
35
+ };
36
+ fingerprint: string;
37
+ title: string;
38
+ message?: string;
39
+ raw: Record<string, unknown>;
40
+ }
41
+ /** MIRROR of protocol-types' `perfRuleFingerprint`. */
42
+ export declare function perfRuleFingerprint(rule: string, project: string, subject: string): string;
43
+ /** MIRROR of protocol-types' `perfRuleScanFingerprint`. */
44
+ export declare function perfRuleScanFingerprint(rule: string, project: string): string;
45
+ /** A rule fired. */
46
+ export declare function buildPerfFindingEvent(project: string, f: PerfFindingWire, at: number, id?: () => string): PerfRuleBusEvent;
47
+ /** The condition cleared. Same bucket as the finding it retracts. */
48
+ export declare function buildPerfRetractedEvent(rule: string, project: string, subject: string, reason: string, at: number, id?: () => string): PerfRuleBusEvent;
49
+ /** A completed scan: the subjects open now — anything else of this rule and project is gone. `elapsedMs`: what the
50
+ * analysis behind it took (all the projects it covered), so its cost is visible on the bus. */
51
+ export declare function buildPerfScanEvent(rule: string, project: string, open: string[], at: number, id?: () => string, elapsedMs?: number): PerfRuleBusEvent;
52
+ /** The flood guard and rotation every bus writer shares — node-instrumentation's, loaded lazily. */
53
+ export interface PerfBusHygiene {
54
+ maybeRotateEventFile(file: string, addedBytes: number): void;
55
+ createEventFloodGuard(opts?: {
56
+ burst?: number;
57
+ perSec?: number;
58
+ }): {
59
+ admit(fp: string): {
60
+ ok: boolean;
61
+ suppressed: number;
62
+ };
63
+ };
64
+ }
65
+ /** Appends perf-rule events to the bus. */
66
+ export interface PerfRulePublisher {
67
+ /** `false` when the flood guard dropped it, or there is no bus to write to. */
68
+ publish(event: PerfRuleBusEvent): boolean;
69
+ }
70
+ /** A publisher for `eventFile` (`LENSMCP_EVENT_FILE`, else `<workspace>/.lensmcp/events.jsonl`). */
71
+ export declare function createPerfRulePublisher(eventFile: string | undefined, hygiene?: PerfBusHygiene): PerfRulePublisher;
@@ -0,0 +1,2 @@
1
+ "use strict";var d=Object.defineProperty;var o=(r,e)=>d(r,"name",{value:e,configurable:!0});var c=Object.defineProperty,u=o((r,e)=>c(r,"name",{value:e,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.PERF_RULES_DOC=void 0,exports.perfRuleFingerprint=perfRuleFingerprint,exports.perfRuleScanFingerprint=perfRuleScanFingerprint,exports.buildPerfFindingEvent=buildPerfFindingEvent,exports.buildPerfRetractedEvent=buildPerfRetractedEvent,exports.buildPerfScanEvent=buildPerfScanEvent,exports.createPerfRulePublisher=createPerfRulePublisher;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path"));exports.PERF_RULES_DOC="https://github.com/kiwiapps-ltd/lensmcp/blob/main/docs/lensmcp/concepts/memory-and-build-cost.mdx";let idCounter=0;function makeId(){return idCounter=(idCounter+1)%65535,`${Date.now().toString(36)}${idCounter.toString(36)}${Math.random().toString(36).slice(2,8)}`}o(makeId,"makeId"),u(makeId,"makeId");function perfRuleFingerprint(r,e,t){return`perf-rule:${r}:${e}:${t}`}o(perfRuleFingerprint,"perfRuleFingerprint"),u(perfRuleFingerprint,"perfRuleFingerprint");function perfRuleScanFingerprint(r,e){return`perf-rule-scan:${r}:${e}`}o(perfRuleScanFingerprint,"perfRuleScanFingerprint"),u(perfRuleScanFingerprint,"perfRuleScanFingerprint");function envelope(r,e,t,s,n,i,p){const l="pending";return{id:p(),sessionId:l,timestamp:e,source:"perf",category:"performance",severity:t,context:{sessionId:l,projectName:r},fingerprint:s,title:n,raw:i}}o(envelope,"envelope"),u(envelope,"envelope");function buildPerfFindingEvent(r,e,t,s=makeId){const n=envelope(r,t,e.severity,perfRuleFingerprint(e.rule,r,e.subject),`${e.rule}: ${e.title}`,{kind:"perf-rule-finding",rule:e.rule,project:r,subject:e.subject,severity:e.severity,title:e.title,summary:e.summary,evidence:e.evidence,cost:e.cost,fix:e.fix,verify:e.verify,docs:e.docs,detectedAt:t},s);return n.message=e.summary,n}o(buildPerfFindingEvent,"buildPerfFindingEvent"),u(buildPerfFindingEvent,"buildPerfFindingEvent");function buildPerfRetractedEvent(r,e,t,s,n,i=makeId){return envelope(e,n,"info",perfRuleFingerprint(r,e,t),`${r}: cleared for ${e} (${t})`,{kind:"perf-rule-retracted",rule:r,project:e,subject:t,reason:s,at:n},i)}o(buildPerfRetractedEvent,"buildPerfRetractedEvent"),u(buildPerfRetractedEvent,"buildPerfRetractedEvent");function buildPerfScanEvent(r,e,t,s,n=makeId,i){return envelope(e,s,"info",perfRuleScanFingerprint(r,e),`${r}: scanned ${e} \u2014 ${t.length} open`,{kind:"perf-rule-scan",rule:r,project:e,open:t,at:s,...i!==void 0?{elapsedMs:i}:{}},n)}o(buildPerfScanEvent,"buildPerfScanEvent"),u(buildPerfScanEvent,"buildPerfScanEvent");function createPerfRulePublisher(r,e){if(!r)return{publish:u(()=>!1,"publish")};const t=e??(()=>{try{return require("@lensmcp/node-instrumentation")}catch{return}})(),s=t?.createEventFloodGuard({burst:30,perSec:5});let n=!1;return{publish(i){const p=s?.admit(i.fingerprint)??{ok:!0,suppressed:0};if(!p.ok)return!1;const l=JSON.stringify(p.suppressed>0?{...i,raw:{...i.raw,floodSuppressed:p.suppressed}}:i)+`
2
+ `;try{return n||(fs.mkdirSync(path.dirname(r),{recursive:!0}),n=!0),t?.maybeRotateEventFile(r,l.length),fs.appendFileSync(r,l),!0}catch{return n=!1,!1}}}}o(createPerfRulePublisher,"createPerfRulePublisher"),u(createPerfRulePublisher,"createPerfRulePublisher");