@lensmcp/cluster 1.25.38 → 1.25.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config-files.d.ts +20 -0
- package/config-files.js +1 -1
- package/devserver-notifier.d.ts +1 -1
- package/executors/gateway/prod-runtime/handler.js +1 -1
- package/executors/gateway/prod-runtime/observability.d.ts +4 -2
- package/executors/gateway/prod-runtime/observability.js +1 -1
- package/executors/gateway/runtime/awake.d.ts +42 -0
- package/executors/gateway/runtime/awake.js +1 -0
- package/executors/gateway/runtime/control.d.ts +11 -1
- package/executors/gateway/runtime/control.js +2 -2
- package/executors/gateway/runtime/discovery.js +1 -1
- package/executors/gateway/runtime/instance.d.ts +19 -3
- package/executors/gateway/runtime/instance.js +3 -2
- package/executors/gateway/runtime/lifecycle.d.ts +4 -1
- package/executors/gateway/runtime/lifecycle.js +3 -3
- package/executors/gateway/runtime/observability.d.ts +8 -3
- package/executors/gateway/runtime/observability.js +3 -3
- package/executors/gateway/runtime/proxy.js +1 -1
- package/executors/gateway/runtime/server.js +2 -2
- package/executors/gateway/runtime/shared-build.d.ts +1 -1
- package/executors/gateway/runtime/types.d.ts +46 -2
- package/executors/gateway/runtime/warm-start.d.ts +18 -0
- package/executors/gateway/runtime/warm-start.js +1 -1
- package/executors/serve/serve.impl.d.ts +1 -1
- package/executors/serve/serve.impl.js +1 -1
- package/package.json +4 -4
- package/perf/heavy-barrel-import.d.ts +1 -1
- package/shared-build/attach.d.ts +9 -0
- package/shared-build/attach.js +3 -3
- package/shared-build/protocol.d.ts +1 -1
- package/tsgo-check-plugin.d.ts +15 -0
- package/tsgo-check-plugin.js +4 -4
|
@@ -270,6 +270,17 @@ export interface LensFrontendSupervisor {
|
|
|
270
270
|
/** Ensure a replacement is running and resolve when its route is ready. */
|
|
271
271
|
recover(frontend: LensFrontend, reason: string): Promise<boolean>;
|
|
272
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* What a `POST /awake` did with one service (awake.ts): it was `up` already; it is `queued` behind the machine's one
|
|
275
|
+
* start at a time, or `starting`; it `started`; the memory floor `deferred` it; it `failed` to come up; or it was
|
|
276
|
+
* `skipped` (stopped by hand and not named, or a stop/start/restart of it under way). `reason` says why for the last three.
|
|
277
|
+
*/
|
|
278
|
+
export interface ServiceWake {
|
|
279
|
+
/** When it was asked for (ms). */
|
|
280
|
+
at: number;
|
|
281
|
+
outcome: 'up' | 'queued' | 'starting' | 'started' | 'deferred' | 'failed' | 'skipped';
|
|
282
|
+
reason?: string;
|
|
283
|
+
}
|
|
273
284
|
/**
|
|
274
285
|
* A restart someone asked for — an agent's `service_restart`, `lensmcp restart <service>` — of ONE service of one
|
|
275
286
|
* workspace or worktree. A fresh tree re-reads what the service reads when it starts: its config files, its env, its
|
|
@@ -284,6 +295,9 @@ export interface ServiceRestart {
|
|
|
284
295
|
startedAt: number;
|
|
285
296
|
/** The pod generation it replaces (none when it was down). */
|
|
286
297
|
fromGeneration?: number;
|
|
298
|
+
/** A worktree instance's service only: the variables its new tree is started with, read again from the instance's
|
|
299
|
+
* files when the restart was asked ({@link ServiceRestartEnv}). Never a value. */
|
|
300
|
+
env?: ServiceRestartEnv;
|
|
287
301
|
/** Set once it settled: a pod of `generation` answers (`ok`), or why not. */
|
|
288
302
|
done?: {
|
|
289
303
|
ok: boolean;
|
|
@@ -292,6 +306,19 @@ export interface ServiceRestart {
|
|
|
292
306
|
error?: string;
|
|
293
307
|
};
|
|
294
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* What a restart of a worktree instance's service applied (issues/closed/foodguard-127). The gateway holds the instance's
|
|
311
|
+
* variables — its hosts, `instance.resources.env`, `instance.env` — and gives them to every tree it starts; a restart
|
|
312
|
+
* reads the files again, so an edit to them takes. These say which variables, never what they are.
|
|
313
|
+
*/
|
|
314
|
+
export interface ServiceRestartEnv {
|
|
315
|
+
/** instance.ts `instanceEnvFingerprint` of the variables the new tree is started with. */
|
|
316
|
+
fingerprint: string;
|
|
317
|
+
/** The same over what the service had before. Equal to `fingerprint` when nothing changed. */
|
|
318
|
+
fromFingerprint: string;
|
|
319
|
+
/** The names of the variables added, removed or changed since then — sorted. */
|
|
320
|
+
changed: string[];
|
|
321
|
+
}
|
|
295
322
|
export interface ServiceCtl {
|
|
296
323
|
project: string;
|
|
297
324
|
decl: ClusterProjectDecl;
|
|
@@ -306,8 +333,12 @@ export interface ServiceCtl {
|
|
|
306
333
|
* `sockNs`): passed as `LENSMCP_SOCK_NS`, and the pool is `$TMPDIR/<sockNs>/<svc>-devserver`. */
|
|
307
334
|
sockNs?: string;
|
|
308
335
|
/** A worktree instance's variables (instance.ts `InstanceRouting.env`): its hosts and `instance.env`, injected
|
|
309
|
-
* into this service's tree under the gateway's own variables.
|
|
336
|
+
* into this service's tree under the gateway's own variables. Read at registration; a restart of this service
|
|
337
|
+
* (lifecycle.ts `restartService`) reads the files again and replaces this one's — no other service's. */
|
|
310
338
|
instanceEnv?: Readonly<Record<string, string>>;
|
|
339
|
+
/** The built-ins those variables were layered over at discovery (instance.ts `instanceBuiltinEnv`: the instance's
|
|
340
|
+
* name, apexes and hosts) — what a restart reads the files over again. Set exactly when `instanceEnv` is. */
|
|
341
|
+
instanceBuiltins?: Readonly<Record<string, string>>;
|
|
311
342
|
/** Set while this service waits for a cold-start slot ({@link MAX_COLD_STARTS}): later requests wait on it
|
|
312
343
|
* rather than queueing a second start. */
|
|
313
344
|
coldWait?: Promise<void>;
|
|
@@ -339,6 +370,19 @@ export interface ServiceCtl {
|
|
|
339
370
|
rollFailure?: string;
|
|
340
371
|
/** The latest restart someone ASKED for (control.ts `POST /restart`, lifecycle.ts `restartService`) — settled or not. */
|
|
341
372
|
restart?: ServiceRestart;
|
|
373
|
+
/**
|
|
374
|
+
* Kept awake until then (ms): the idle sweep stops it no sooner, and its idle window counts from then rather than from
|
|
375
|
+
* a request long ago (control.ts `POST /awake`, awake.ts). A queue worker's work arrives with no request, and stops
|
|
376
|
+
* with its service (issues/closed/foodguard-123).
|
|
377
|
+
*/
|
|
378
|
+
awakeUntil?: number;
|
|
379
|
+
/** When the idle sweep last stopped it, and why — what a preflight says of a required service it finds down. */
|
|
380
|
+
lastIdleStop?: {
|
|
381
|
+
at: number;
|
|
382
|
+
why: string;
|
|
383
|
+
};
|
|
384
|
+
/** The latest `POST /awake` of it, and how its start went ({@link ServiceWake}). */
|
|
385
|
+
wake?: ServiceWake;
|
|
342
386
|
/** The pod GENERATION the current `proc` was spawned as (`LENSMCP_POD_GENERATION`, echoed in `/__lensmcp/alive`). */
|
|
343
387
|
generation?: number;
|
|
344
388
|
/** How the current `proc` was started: the gateway's serve runner, or `nx run` (serve-launch.ts). An `nx run`
|
|
@@ -855,7 +899,7 @@ export interface GatewayRuntime {
|
|
|
855
899
|
* each current. Absent when the gateway was started without a graph — every service then starts through `nx run`. */
|
|
856
900
|
readonly graphStores?: ProjectGraphStores;
|
|
857
901
|
/** The gateway's `sharedCompiler` option — one webpack compilation for this workspace's services
|
|
858
|
-
* (shared-build.ts, issues/
|
|
902
|
+
* (shared-build.ts, issues/closed/pod-memory-05). `LENSMCP_SHARED_COMPILER` wins over it. */
|
|
859
903
|
readonly sharedCompiler?: boolean;
|
|
860
904
|
/** Managed lens frontend handoff state, installed by the child supervisor. */
|
|
861
905
|
lensSupervisor?: LensFrontendSupervisor;
|
|
@@ -12,6 +12,16 @@ export declare function warmMinFreeBytes(env?: NodeJS.ProcessEnv): number;
|
|
|
12
12
|
* `undefined` — never idle-stopped — for the daemon's own and other registered workspaces' services, as before.
|
|
13
13
|
*/
|
|
14
14
|
export declare function instanceIdleSec(svc: Pick<ServiceCtl, 'decl'>, isInstance: boolean, env?: NodeJS.ProcessEnv): number | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The idle window the sweep gives `svc` (lifecycle.ts `sweepOnce`) — {@link instanceIdleSec}, an instance being a
|
|
17
|
+
* registered worktree's service, never the daemon's own. One rule for the sweep and for what `/status` says of it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function serviceIdleSec(rt: {
|
|
20
|
+
readonly root?: string;
|
|
21
|
+
readonly registry?: {
|
|
22
|
+
get(wsKey: string): WorkspaceFragment | undefined;
|
|
23
|
+
};
|
|
24
|
+
}, svc: Pick<ServiceCtl, 'decl' | 'root' | 'wsKey'>, env?: NodeJS.ProcessEnv): number | undefined;
|
|
15
25
|
export interface MemoryProbe {
|
|
16
26
|
platform?: NodeJS.Platform;
|
|
17
27
|
/** `sysctl -n <name>`. */
|
|
@@ -39,6 +49,8 @@ export interface WarmOutcome {
|
|
|
39
49
|
outcome: 'up' | 'failed' | 'deferred' | 'already';
|
|
40
50
|
reason?: string;
|
|
41
51
|
}
|
|
52
|
+
/** Why a start waits, when the memory the system can still give out is under the floor; nothing when it is not (or unread). */
|
|
53
|
+
export declare function belowFloor(free: number | undefined, minFreeBytes: number): string | undefined;
|
|
42
54
|
/** Start `services`' always-warm ones, one at a time, while the floor holds. Never throws. */
|
|
43
55
|
export declare function warmStart(services: readonly ServiceCtl[], deps: WarmStartDeps): Promise<WarmOutcome[]>;
|
|
44
56
|
export interface WorktreeWarmerDeps {
|
|
@@ -61,4 +73,10 @@ export interface WorktreeWarmerDeps {
|
|
|
61
73
|
export declare function createWorktreeWarmer(deps: WorktreeWarmerDeps): {
|
|
62
74
|
onRegister(fragment: WorkspaceFragment): Promise<WarmOutcome[]> | undefined;
|
|
63
75
|
onUnregister(wsKey: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Run `job` in the machine's one turn — after every warm start before it, and before any after it. A worktree's
|
|
78
|
+
* services woken for a milestone (control.ts `POST /awake`, awake.ts) start here too, so a wake and a registration
|
|
79
|
+
* never start two compilers at once (issues/closed/foodguard-123).
|
|
80
|
+
*/
|
|
81
|
+
take<T>(job: () => Promise<T>): Promise<T>;
|
|
64
82
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var m=Object.defineProperty;var u=(e,r)=>m(e,"name",{value:r,configurable:!0});var f=Object.defineProperty,a=u((e,r)=>f(e,"name",{value:r,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DEFAULT_WORKTREE_IDLE_SEC=exports.WORKTREE_IDLE_ENV=exports.DEFAULT_WARM_MIN_FREE_MB=exports.WARM_MIN_FREE_ENV=void 0,exports.warmMinFreeBytes=warmMinFreeBytes,exports.instanceIdleSec=instanceIdleSec,exports.serviceIdleSec=serviceIdleSec,exports.availableMemory=availableMemory,exports.belowFloor=belowFloor,exports.warmStart=warmStart,exports.createWorktreeWarmer=createWorktreeWarmer;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path"));exports.WARM_MIN_FREE_ENV="LENSMCP_WARM_MIN_FREE_MB",exports.DEFAULT_WARM_MIN_FREE_MB=4096,exports.WORKTREE_IDLE_ENV="LENSMCP_WORKTREE_IDLE_SEC",exports.DEFAULT_WORKTREE_IDLE_SEC=1800;function warmMinFreeBytes(e=process.env){const r=Number(e[exports.WARM_MIN_FREE_ENV]);return(Number.isFinite(r)&&r>=0?r:exports.DEFAULT_WARM_MIN_FREE_MB)*1024*1024}u(warmMinFreeBytes,"warmMinFreeBytes"),a(warmMinFreeBytes,"warmMinFreeBytes");function instanceIdleSec(e,r,n=process.env){if(e.decl.idleKillSec)return e.decl.idleKillSec;if(!r)return;const o=Number(n[exports.WORKTREE_IDLE_ENV]?.trim()||NaN);return!Number.isFinite(o)||o<0?exports.DEFAULT_WORKTREE_IDLE_SEC:o>0?o:void 0}u(instanceIdleSec,"instanceIdleSec"),a(instanceIdleSec,"instanceIdleSec");function serviceIdleSec(e,r,n=process.env){const o=e.root!==void 0&&r.root!==void 0&&path.resolve(r.root)===path.resolve(e.root);return instanceIdleSec(r,!o&&!!e.registry?.get(r.wsKey)?.instance,n)}u(serviceIdleSec,"serviceIdleSec"),a(serviceIdleSec,"serviceIdleSec");function availableMemory(e={}){const r=e.platform??process.platform;try{if(r==="darwin"){const n=e.sysctl??(t=>(0,node_child_process_1.execFileSync)("sysctl",["-n",t],{encoding:"utf8",timeout:2e3})),o=Number(n("kern.memorystatus_level").trim());return!Number.isFinite(o)||o<0||o>100?void 0:Math.round(o/100*(e.totalmem??os.totalmem)())}if(r==="linux"){const n=(e.readFile??(t=>fs.readFileSync(t,"utf8")))("/proc/meminfo"),o=/^MemAvailable:\s+(\d+)\s+kB/m.exec(n)?.[1];return o===void 0?void 0:Number(o)*1024}}catch{}}u(availableMemory,"availableMemory"),a(availableMemory,"availableMemory");const gb=a(e=>`${(e/1024**3).toFixed(1)} GB`,"gb");function belowFloor(e,r){return e!==void 0&&e<r?`${gb(e)} of memory free, under the ${gb(r)} a warm start keeps (${exports.WARM_MIN_FREE_ENV})`:void 0}u(belowFloor,"belowFloor"),a(belowFloor,"belowFloor");async function warmStart(e,r){const n=[],o=e.filter(t=>t.decl.eager&&!t.frozen);for(let t=0;t<o.length;t++){const c=o[t];if(!r.current(c))break;if(c.proc||c.state!=="down"){n.push({project:c.project,outcome:"already"});continue}const d=r.available(),s=belowFloor(d,r.minFreeBytes);if(s){const p=o.slice(t).filter(i=>!i.proc&&i.state==="down");r.log(`[gateway] warm start stopped for ${c.wsKey}: ${s} \u2014 ${p.map(i=>i.project).join(", ")} start on their first request.`);for(const i of p)r.emit(i,"info",`warm start deferred: ${i.project} \u2014 ${s}`,{kind:"service-warm-deferred",project:i.project,reason:s,availableBytes:d,minFreeBytes:r.minFreeBytes}),n.push({project:i.project,outcome:"deferred",reason:s});return n}const l=await r.ensureUp(c).catch(()=>!1);l&&(c.lastUsed=Date.now()),n.push({project:c.project,outcome:l?"up":"failed"})}return n}u(warmStart,"warmStart"),a(warmStart,"warmStart");function createWorktreeWarmer(e){const r=new Map,n=e.log??(t=>console.log(t));let o=(e.ready??Promise.resolve()).catch(()=>{});return{onRegister(t){if(!t.instance||t.instance.frozen)return;const c=Symbol(t.wsKey);r.set(t.wsKey,c);const d=o.then(()=>new Promise(s=>setTimeout(s,e.delayMs??1e3).unref?.())).then(()=>warmStart(t.services,{ensureUp:e.ensureUp,available:e.available??(()=>availableMemory()),minFreeBytes:warmMinFreeBytes(e.env),current:a(s=>!e.rt.stopped()&&r.get(s.wsKey)===c&&!!e.rt.registry.get(s.wsKey)?.services.includes(s),"current"),emit:a((s,l,p,i)=>e.obs.emit(l,p,`cluster-service:${s.project}`,{wsKey:s.wsKey,...i},void 0,path.join(s.root,".lensmcp","events.jsonl")),"emit"),log:n})).then(s=>{const l=a(p=>s.filter(i=>i.outcome===p).map(i=>i.project),"by");return s.length&&n(`[gateway] warm start of ${t.wsKey}: up ${l("up").join(", ")||"\u2014"}${l("failed").length?`; failed ${l("failed").join(", ")}`:""}${l("deferred").length?`; on first request ${l("deferred").join(", ")}`:""}`),s});return o=d.catch(()=>{}),d},onUnregister(t){r.delete(t)},take(t){const c=o.then(t);return o=c.catch(()=>{}),c}}}u(createWorktreeWarmer,"createWorktreeWarmer"),a(createWorktreeWarmer,"createWorktreeWarmer");
|
|
@@ -19,7 +19,7 @@ interface ServeResult {
|
|
|
19
19
|
*
|
|
20
20
|
* Owns the full devserver lifecycle:
|
|
21
21
|
* 1. Starts webpack in watch mode — or, under a gateway running the shared compiler, joins the workspace's ONE
|
|
22
|
-
* compilation (shared-build/attach.ts, issues/
|
|
22
|
+
* compilation (shared-build/attach.ts, issues/closed/pod-memory-05) and is told about each build of its bundle
|
|
23
23
|
* 2. On first successful build, starts main.devserver.js with proper env vars — in this process under the gateway's
|
|
24
24
|
* serve runner (devserver-host.ts), forked otherwise
|
|
25
25
|
* 3. On subsequent builds, POSTs /webpack/reload to devserver — attached: only when ITS bundle changed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var me=Object.defineProperty;var i=(e,o)=>me(e,"name",{value:o,configurable:!0});var pe=Object.defineProperty,s=i((e,o)=>pe(e,"name",{value:o,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),node_child_process_1=require("node:child_process"),yaml=tslib_1.__importStar(require("js-yaml")),child_process_env_1=require("../../child-process-env"),dev_devtool_1=require("../../dev-devtool"),devserver_host_1=require("../../devserver-host"),devserver_notifier_1=require("../../devserver-notifier"),process_name_1=require("../../process-name"),worker_supervisor_1=require("../../worker-supervisor"),attach_1=require("../../shared-build/attach"),stop_on_signal_1=require("./stop-on-signal"),compile_retry_1=require("../../compile-retry"),config_files_1=require("../../config-files"),signature_1=require("../../shared-build/signature");function definedEnv(e){const o={};for(const[$,n]of Object.entries(e))typeof n=="string"&&(o[$]=n);return o}i(definedEnv,"definedEnv"),s(definedEnv,"definedEnv");const frozen=s(()=>process.env.LENSMCP_FREEZE==="1","frozen");async function*serveExecutor(e,o){process.env.NODE_ENV="development";const $=o.projectsConfigurations.projects[o.projectName],n=o.root,N=path.join(n,$.root),K=e.outputPath?path.join(n,e.outputPath):path.join(n,"dist",$.root),Z=e.configEnv||"development",D=path.join(n,"config",`config.${Z}.yaml`),T=3050;let d=T,a=o.projectName,j={};fs.existsSync(D)?(j=yaml.load(fs.readFileSync(D,"utf8")),d=Number(j.port)||d,a=j.serviceName||a):console.warn(`[serve] ${o.projectName}: no config file at ${D} \u2014 public devserver port defaults to ${T}. If another service also lacks its config, both bind ${T} \u2192 EADDRINUSE. Add config/config.${Z}.yaml with a distinct \`port:\` (matching this project's cluster.port).`),e.serviceName&&(a=e.serviceName);const x=e.servePrefix??"",y=e.debugPort??(Number(process.env.LENSMCP_DEBUG_PORT)||void 0),ne=/^(1|true|yes|on)$/i.test((process.env.LENSMCP_DEBUG_FIXED_PORTS??"").trim()),m=e.logFile?path.resolve(n,e.logFile):process.env.LENSMCP_LOG_FILE||void 0;if(m){fs.mkdirSync(path.dirname(m),{recursive:!0}),fs.writeFileSync(m,"",{mode:384});try{fs.chmodSync(m,384)}catch{}}let A,I;e.gateway?.middleware&&(A=path.resolve(N,e.gateway.middleware),I=JSON.stringify(j));const ie=e.entryFile||"./src/deployments/service/main.ts",ce=e.tsConfigFile||"./tsconfig.app.json",ae=path.resolve(N,ie),Q=path.resolve(N,ce),le=(e.workers||[]).map(r=>({entryName:r.name,entryPath:path.resolve(N,r.entryPath)})),E={appName:o.projectName,appRoot:N,outputDir:K,main:ae,tsConfig:Q,assets:e.assets||[],additionalEntryPoints:le,port:d,serviceName:a,memoryLimit:e.memoryLimit||8192,buildLibsFromSource:e.buildLibsFromSource!==!1,workspaceRoot:n,orgScopes:e.orgScopes||[],bundlePackages:e.bundlePackages||[],nodeExternalsConfig:e.nodeExternalsConfig,webpackConfigPath:e.webpackConfigPath,httpsReload:e.gateway?.https===!0,...e.devtool!==void 0&&{devtool:e.devtool}};let l=null,q=!1,F=!1,M=!1,p=!1;const ee=path.join(__dirname,"../../main.devserver.js"),oe=path.join(__dirname,"../../main.devserver.ts"),U=!fs.existsSync(ee)&&fs.existsSync(oe),C=U?oe:ee,ue=path.join(K,"main.js");function B(){l&&!l.killed&&(l.kill("SIGTERM"),l=null)}i(B,"K"),s(B,"cleanup");function O(){p=!0,B()}i(O,"U"),s(O,"stop");const de=(0,stop_on_signal_1.stopOnSignal)({stop:O,close:s(()=>b(),"close")});process.on("exit",O);function S(){if(p||q)return;if(!fs.existsSync(C)){console.error(`[serve] devserver script not found at ${C}`);return}const r=(0,child_process_env_1.childProcessEnv)({...process.env,PORT:String(d),SERVICE_NAME:a,SERVE_PREFIX:x,LENSMCP_PROJECT:process.env.LENSMCP_PROJECT||o.projectName||a,LENSMCP_EVENT_FILE:process.env.LENSMCP_EVENT_FILE||path.join(n,".lensmcp","events.jsonl"),CHILD_COUNT:String(e.childCount||process.env.CHILD_COUNT||1),BUNDLE_PATH:ue,...A&&{GATEWAY_MIDDLEWARE:A},...I&&{GATEWAY_CONFIG:I},...e.gateway?.routes?.length&&{GATEWAY_ROUTES:JSON.stringify(e.gateway.routes)},...e.gateway?.https&&{GATEWAY_HTTPS:"1"},...e.gateway?.extraPorts?.length&&{GATEWAY_EXTRA_PORTS:JSON.stringify(e.gateway.extraPorts)},...e.internalPort&&{INTERNAL_PORT:String(e.internalPort)},...y&&{LENSMCP_DEBUG_PORT:String(y)},...m&&{LENSMCP_LOG_FILE:m},...e.workers?.length&&{WORKERS:(0,worker_supervisor_1.serializeWorkerSpecs)(e.workers)},[dev_devtool_1.DEV_SOURCE_MAPS_ENV]:M?"1":"0"});if((0,devserver_host_1.devserverInProcessOffered)()&&(!U||require.extensions[".ts"])){q=!0;for(const[u,f]of Object.entries(r))f!==void 0&&(process.env[u]=f);(0,devserver_host_1.markDevserverInProcess)(),require(C),G("in this process");return}const v=["--enable-source-maps"];U&&v.unshift("--require","@swc-node/register");const c=(0,node_child_process_1.fork)(C,{cwd:n,env:r,stdio:["inherit","inherit","inherit","ipc"],execArgv:v,execPath:(0,process_name_1.namedExecPath)((0,process_name_1.processName)(process.env.LENSMCP_WS_KEY||path.basename(n),a,"dev"))});l=c,c.on("exit",(u,f)=>{l===c&&(l=null),!(f||u===0||p)&&(console.error(`[serve] devserver exited unexpectedly (code=${u}, signal=${f}) \u2014 respawning in 1.5s`),setTimeout(()=>{!p&&!l&&F&&S()},1500).unref?.())}),G()}i(S,"C"),s(S,"startDevServer");function G(r){console.log(`[serve] Devserver started on port ${d} (service: ${a}${x?`, prefix: /${x}`:""}${r?`, ${r}`:""})`),y?(console.warn(`[serve] DEBUGGER ENABLED for ${a}. Node's inspector has NO authentication \u2014 the ws:// UUID is the whole credential, and any account on this host that obtains it runs code as you. Do not enable this on a shared machine.`),ne?console.warn(`[serve] LENSMCP_DEBUG_FIXED_PORTS=1 \u2014 pod inspectors bind PREDICTABLE ports 127.0.0.1:${y}+slot (workers ${y}+20+i), reachable by any account on this host. Attach WebStorm (Attach to Node.js/Chrome) to ${y}. Unset it to bind ephemeral ports instead.`):console.log(`[serve] Debug: pod inspectors bind an EPHEMERAL 127.0.0.1 port. Get the ws:// URLs with \`lensmcp debug ${a}\` (the owner-only control socket); a devserver run in your own terminal also prints them there. Set LENSMCP_DEBUG_FIXED_PORTS=1 if your IDE needs a fixed attach port.`)):console.log(`[serve] Debug on demand: \`lensmcp debug ${a}\` opens pod inspectors without a restart`),m&&console.log(`[serve] Logs teed to ${m} \u2014 tail it in WebStorm via Run/Debug configuration \u2192 Logs tab`)}i(G,"Z"),s(G,"announceDevServer");function ve(){if(!l||l.killed){console.log("[serve] Devserver not running, restarting..."),S();return}fetch(`http://localhost:${d}/webpack/reload`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"}).then(()=>console.log("[serve] Notified devserver to reload")).catch(r=>{console.warn("[serve] Failed to notify devserver, restarting...",r.message),B(),S()})}i(ve,"he"),s(ve,"_notifyDevServerReload");let k=null;const W=[];function _(r){if(k){const v=k;k=null,v(r)}else W.push(r)}i(_,"w"),s(_,"pushResult");function re(){return W.length>0?Promise.resolve(W.shift()):new Promise(r=>{k=r})}i(re,"le"),s(re,"waitForNext");let b=s(()=>{},"closeBuild");function V(){return!F||!l&&!q?(F=!0,S(),!0):!1}i(V,"Q"),s(V,"builtOk");function H(){try{return JSON.stringify((0,signature_1.transpileCompilerOptions)(E.tsConfig,n))}catch{return}}i(H,"ee"),s(H,"compileShape");function P(){if(p)return;const{createDevWebpackConfig:r}=require("../../create-webpack-dev"),v=require("webpack"),c=E.webpackConfigPath?path.resolve(E.appRoot,E.webpackConfigPath):void 0;if(c)try{delete require.cache[require.resolve(c)]}catch{}const u=r(E),{watch:f,...t}=u,J=(0,config_files_1.configFilesOf)(E),Y=H();let X;t.plugins=[...t.plugins??[],new config_files_1.ConfigFilesPlugin({files:s(()=>J,"files"),changed:s(w=>{if(!c||!w.includes(c)){const h=H();if(h===void 0||h===Y)return}X=w.map(h=>path.relative(n,h)).sort().join(", ")},"changed")})];const R=v(t);M=(0,dev_devtool_1.bundleCarriesSourceMaps)(R.options.devtool);let g;const L=(0,compile_retry_1.createFailureRetry)(()=>{p||!g||(console.log(`[serve] ${o.projectName}: compiling again after the failure`),g.invalidate())}),se=s((w,h)=>{if(X!==void 0&&g&&!p){console.log(`[serve] ${o.projectName}: ${X} changed what it compiles with \u2014 compiling again with a new compiler`),L.dispose();const z=g;g=void 0,setImmediate(()=>z.close(()=>P()));return}if(w){if(console.error("[serve] Webpack error:",w),g&&!p){const{delayMs:z,retry:he}=L.failed();console.log(`[serve] ${o.projectName}: compiling again in ${Math.round(z/1e3)} s (retry ${he}) \u2014 webpack does not after a failure like this`)}_({success:!1});return}L.succeeded();const fe=h.hasErrors();if(console.info(h.toString({colors:!0,chunks:!1,assets:!0})),fe){_({success:!1});return}V(),_({success:!0,baseUrl:`http://localhost:${d}`})},"onBuild");if(frozen()){console.log(`[serve] ${o.projectName}: frozen \u2014 compiling once, no file watcher`),R.run((w,h)=>{se(w,h),R.close(()=>{})});return}g=R.watch(u.watchOptions||{},se),b=s(()=>{L.dispose(),g?.close(()=>{})},"closeBuild")}i(P,"$"),s(P,"compileOnItsOwn");function te(r){const v=r.accepted.projects.filter(t=>t!==o.projectName);console.log(`[serve] ${o.projectName}: compiled by the workspace's shared compilation ${r.accepted.group.slice(0,8)}${v.length?` with ${v.join(", ")}`:""} \u2014 no webpack in this process`);const c=new devserver_notifier_1.DevserverNotifier(d,e.gateway?.https===!0,(0,devserver_notifier_1.devserverControlSock)(a)),u=(0,attach_1.createAttachedTypecheck)({appName:o.projectName,tsConfig:Q,workspaceRoot:n,memoryLimit:e.memoryLimit||8192});let f;b=s(()=>{r.close(),u.dispose()},"closeBuild"),r.listen(t=>{if(!p)switch(t.type){case"watch-run":c.watchRun(t.paths);return;case"watch-closed":c.degrade("watch closed");return;case"failed":console.error(`[serve] the shared compiler failed: ${t.message}`),c.failed(`the shared compiler failed: ${t.message}`),_({success:!1});return;case"refused":console.log(`[serve] ${o.projectName}: compiling on its own from now on \u2014 ${t.reason}`),b(),P();return;case"built":{if(console.info(t.text),M=t.sourceMaps,u.built(t),!t.ok){c.failed(),_({success:!1});return}const J=t.hash!==f;f=t.hash;const Y=V();c.built(J&&!Y),_({success:!0,baseUrl:`http://localhost:${d}`});return}default:return}})}if(i(te,"ue"),s(te,"attachToSharedBuild"),(0,attach_1.sharedBuildOffered)()&&!frozen()){const r=await(0,attach_1.joinSharedBuild)({project:o.projectName,target:o.targetName??"serve-hmr",...o.configurationName?{configuration:o.configurationName}:{},options:E,targetOptions:{...e},env:definedEnv(process.env)});r.kind==="attached"?te(r.session):(console.log(`[serve] ${o.projectName}: compiling on its own \u2014 ${r.reason}`),P())}else P();try{for(;;)yield await re()}finally{O(),b(),de()}}i(serveExecutor,"serveExecutor"),s(serveExecutor,"serveExecutor"),exports.default=serveExecutor,module.exports=serveExecutor,module.exports.default=serveExecutor;
|
|
1
|
+
"use strict";var _e=Object.defineProperty;var i=(e,o)=>_e(e,"name",{value:o,configurable:!0});var ve=Object.defineProperty,s=i((e,o)=>ve(e,"name",{value:o,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),node_child_process_1=require("node:child_process"),yaml=tslib_1.__importStar(require("js-yaml")),child_process_env_1=require("../../child-process-env"),dev_devtool_1=require("../../dev-devtool"),devserver_host_1=require("../../devserver-host"),devserver_notifier_1=require("../../devserver-notifier"),process_name_1=require("../../process-name"),worker_supervisor_1=require("../../worker-supervisor"),attach_1=require("../../shared-build/attach"),stop_on_signal_1=require("./stop-on-signal"),compile_retry_1=require("../../compile-retry"),config_files_1=require("../../config-files"),signature_1=require("../../shared-build/signature");function definedEnv(e){const o={};for(const[j,n]of Object.entries(e))typeof n=="string"&&(o[j]=n);return o}i(definedEnv,"definedEnv"),s(definedEnv,"definedEnv");const frozen=s(()=>process.env.LENSMCP_FREEZE==="1","frozen");async function*serveExecutor(e,o){process.env.NODE_ENV="development";const j=o.projectsConfigurations.projects[o.projectName],n=o.root,S=path.join(n,j.root),Q=e.outputPath?path.join(n,e.outputPath):path.join(n,"dist",j.root),ee=e.configEnv||"development",A=path.join(n,"config",`config.${ee}.yaml`),q=3050;let d=q,a=o.projectName,O={};fs.existsSync(A)?(O=yaml.load(fs.readFileSync(A,"utf8")),d=Number(O.port)||d,a=O.serviceName||a):console.warn(`[serve] ${o.projectName}: no config file at ${A} \u2014 public devserver port defaults to ${q}. If another service also lacks its config, both bind ${q} \u2192 EADDRINUSE. Add config/config.${ee}.yaml with a distinct \`port:\` (matching this project's cluster.port).`),e.serviceName&&(a=e.serviceName);const I=e.servePrefix??"",y=e.debugPort??(Number(process.env.LENSMCP_DEBUG_PORT)||void 0),ce=/^(1|true|yes|on)$/i.test((process.env.LENSMCP_DEBUG_FIXED_PORTS??"").trim()),g=e.logFile?path.resolve(n,e.logFile):process.env.LENSMCP_LOG_FILE||void 0;if(g){fs.mkdirSync(path.dirname(g),{recursive:!0}),fs.writeFileSync(g,"",{mode:384});try{fs.chmodSync(g,384)}catch{}}let M,F;e.gateway?.middleware&&(M=path.resolve(S,e.gateway.middleware),F=JSON.stringify(O));const ae=e.entryFile||"./src/deployments/service/main.ts",le=e.tsConfigFile||"./tsconfig.app.json",ue=path.resolve(S,ae),oe=path.resolve(S,le),pe=(e.workers||[]).map(r=>({entryName:r.name,entryPath:path.resolve(S,r.entryPath)})),m={appName:o.projectName,appRoot:S,outputDir:Q,main:ue,tsConfig:oe,assets:e.assets||[],additionalEntryPoints:pe,port:d,serviceName:a,memoryLimit:e.memoryLimit||8192,buildLibsFromSource:e.buildLibsFromSource!==!1,workspaceRoot:n,orgScopes:e.orgScopes||[],bundlePackages:e.bundlePackages||[],nodeExternalsConfig:e.nodeExternalsConfig,webpackConfigPath:e.webpackConfigPath,httpsReload:e.gateway?.https===!0,...e.devtool!==void 0&&{devtool:e.devtool}};let l=null,U=!1,G=!1,B=!1,h=!1;const re=path.join(__dirname,"../../main.devserver.js"),te=path.join(__dirname,"../../main.devserver.ts"),W=!fs.existsSync(re)&&fs.existsSync(te),L=W?te:re,de=path.join(Q,"main.js");function H(){l&&!l.killed&&(l.kill("SIGTERM"),l=null)}i(H,"Q"),s(H,"cleanup");function R(){h=!0,H()}i(R,"W"),s(R,"stop");const he=(0,stop_on_signal_1.stopOnSignal)({stop:R,close:s(()=>b(),"close")});process.on("exit",R);function P(){if(h||U)return;if(!fs.existsSync(L)){console.error(`[serve] devserver script not found at ${L}`);return}const r=(0,child_process_env_1.childProcessEnv)({...process.env,PORT:String(d),SERVICE_NAME:a,SERVE_PREFIX:I,LENSMCP_PROJECT:process.env.LENSMCP_PROJECT||o.projectName||a,LENSMCP_EVENT_FILE:process.env.LENSMCP_EVENT_FILE||path.join(n,".lensmcp","events.jsonl"),CHILD_COUNT:String(e.childCount||process.env.CHILD_COUNT||1),BUNDLE_PATH:de,...M&&{GATEWAY_MIDDLEWARE:M},...F&&{GATEWAY_CONFIG:F},...e.gateway?.routes?.length&&{GATEWAY_ROUTES:JSON.stringify(e.gateway.routes)},...e.gateway?.https&&{GATEWAY_HTTPS:"1"},...e.gateway?.extraPorts?.length&&{GATEWAY_EXTRA_PORTS:JSON.stringify(e.gateway.extraPorts)},...e.internalPort&&{INTERNAL_PORT:String(e.internalPort)},...y&&{LENSMCP_DEBUG_PORT:String(y)},...g&&{LENSMCP_LOG_FILE:g},...e.workers?.length&&{WORKERS:(0,worker_supervisor_1.serializeWorkerSpecs)(e.workers)},[dev_devtool_1.DEV_SOURCE_MAPS_ENV]:B?"1":"0"});if((0,devserver_host_1.devserverInProcessOffered)()&&(!W||require.extensions[".ts"])){U=!0;for(const[u,f]of Object.entries(r))f!==void 0&&(process.env[u]=f);(0,devserver_host_1.markDevserverInProcess)(),require(L),J("in this process");return}const v=["--enable-source-maps"];W&&v.unshift("--require","@swc-node/register");const c=(0,node_child_process_1.fork)(L,{cwd:n,env:r,stdio:["inherit","inherit","inherit","ipc"],execArgv:v,execPath:(0,process_name_1.namedExecPath)((0,process_name_1.processName)(process.env.LENSMCP_WS_KEY||path.basename(n),a,"dev"))});l=c,c.on("exit",(u,f)=>{l===c&&(l=null),!(f||u===0||h)&&(console.error(`[serve] devserver exited unexpectedly (code=${u}, signal=${f}) \u2014 respawning in 1.5s`),setTimeout(()=>{!h&&!l&&G&&P()},1500).unref?.())}),J()}i(P,"j"),s(P,"startDevServer");function J(r){console.log(`[serve] Devserver started on port ${d} (service: ${a}${I?`, prefix: /${I}`:""}${r?`, ${r}`:""})`),y?(console.warn(`[serve] DEBUGGER ENABLED for ${a}. Node's inspector has NO authentication \u2014 the ws:// UUID is the whole credential, and any account on this host that obtains it runs code as you. Do not enable this on a shared machine.`),ce?console.warn(`[serve] LENSMCP_DEBUG_FIXED_PORTS=1 \u2014 pod inspectors bind PREDICTABLE ports 127.0.0.1:${y}+slot (workers ${y}+20+i), reachable by any account on this host. Attach WebStorm (Attach to Node.js/Chrome) to ${y}. Unset it to bind ephemeral ports instead.`):console.log(`[serve] Debug: pod inspectors bind an EPHEMERAL 127.0.0.1 port. Get the ws:// URLs with \`lensmcp debug ${a}\` (the owner-only control socket); a devserver run in your own terminal also prints them there. Set LENSMCP_DEBUG_FIXED_PORTS=1 if your IDE needs a fixed attach port.`)):console.log(`[serve] Debug on demand: \`lensmcp debug ${a}\` opens pod inspectors without a restart`),g&&console.log(`[serve] Logs teed to ${g} \u2014 tail it in WebStorm via Run/Debug configuration \u2192 Logs tab`)}i(J,"ee"),s(J,"announceDevServer");function fe(){if(!l||l.killed){console.log("[serve] Devserver not running, restarting..."),P();return}fetch(`http://localhost:${d}/webpack/reload`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"}).then(()=>console.log("[serve] Notified devserver to reload")).catch(r=>{console.warn("[serve] Failed to notify devserver, restarting...",r.message),H(),P()})}i(fe,"me"),s(fe,"_notifyDevServerReload");let D=null;const V=[];function _(r){if(D){const v=D;D=null,v(r)}else V.push(r)}i(_,"w"),s(_,"pushResult");function se(){return V.length>0?Promise.resolve(V.shift()):new Promise(r=>{D=r})}i(se,"de"),s(se,"waitForNext");let b=s(()=>{},"closeBuild");function Y(){return!G||!l&&!U?(G=!0,P(),!0):!1}i(Y,"re"),s(Y,"builtOk");function X(){try{return JSON.stringify((0,signature_1.transpileCompilerOptions)(m.tsConfig,n))}catch{return}}i(X,"te"),s(X,"compileShape");function C(){if(h)return;const{createDevWebpackConfig:r}=require("../../create-webpack-dev"),v=require("webpack"),c=m.webpackConfigPath?path.resolve(m.appRoot,m.webpackConfigPath):void 0;if(c)try{delete require.cache[require.resolve(c)]}catch{}const u=r(m),{watch:f,...t}=u,z=(0,config_files_1.configFilesOf)(m),K=X(),{TsgoCheckPlugin:ge}=require("../../tsgo-check-plugin"),me=process.env.LENSMCP_TYPECHECK!=="off"&&!(t.plugins??[]).some(p=>p instanceof ge);let $,Z=!1;t.plugins=[...t.plugins??[],new config_files_1.ConfigFilesPlugin({files:s(()=>z,"files"),changed:s(p=>{let w=!1;if(!c||!p.includes(c)){const N=X();if(N===void 0)return;if(N===K){if(!me||p.every(k=>k===m.tsConfig))return;w=!0}}Z=($===void 0||Z)&&w,$=p.map(N=>path.relative(n,N)).sort().join(", ")},"changed")})];const T=v(t);B=(0,dev_devtool_1.bundleCarriesSourceMaps)(T.options.devtool);let E;const x=(0,compile_retry_1.createFailureRetry)(()=>{h||!E||(console.log(`[serve] ${o.projectName}: compiling again after the failure`),E.invalidate())}),ie=s((p,w)=>{if($!==void 0&&E&&!h){console.log(Z?`[serve] ${o.projectName}: ${$} changed what it type-checks with \u2014 fork-ts-checker reads that only when it starts, so a new compiler checks it`:`[serve] ${o.projectName}: ${$} changed what it compiles with \u2014 compiling again with a new compiler`),x.dispose();const k=E;E=void 0,setImmediate(()=>k.close(()=>C()));return}if(p){if(console.error("[serve] Webpack error:",p),E&&!h){const{delayMs:k,retry:Ee}=x.failed();console.log(`[serve] ${o.projectName}: compiling again in ${Math.round(k/1e3)} s (retry ${Ee}) \u2014 webpack does not after a failure like this`)}_({success:!1});return}x.succeeded();const N=w.hasErrors();if(console.info(w.toString({colors:!0,chunks:!1,assets:!0})),N){_({success:!1});return}Y(),_({success:!0,baseUrl:`http://localhost:${d}`})},"onBuild");if(frozen()){console.log(`[serve] ${o.projectName}: frozen \u2014 compiling once, no file watcher`),T.run((p,w)=>{ie(p,w),T.close(()=>{})});return}E=T.watch(u.watchOptions||{},ie),b=s(()=>{x.dispose(),E?.close(()=>{})},"closeBuild")}i(C,"L"),s(C,"compileOnItsOwn");function ne(r){const v=r.accepted.projects.filter(t=>t!==o.projectName);console.log(`[serve] ${o.projectName}: compiled by the workspace's shared compilation ${r.accepted.group.slice(0,8)}${v.length?` with ${v.join(", ")}`:""} \u2014 no webpack in this process`);const c=new devserver_notifier_1.DevserverNotifier(d,e.gateway?.https===!0,(0,devserver_notifier_1.devserverControlSock)(a)),u=(0,attach_1.createAttachedTypecheck)({appName:o.projectName,tsConfig:oe,workspaceRoot:n,memoryLimit:e.memoryLimit||8192});let f;b=s(()=>{r.close(),u.dispose()},"closeBuild"),r.listen(t=>{if(!h)switch(t.type){case"watch-run":c.watchRun(t.paths);return;case"watch-closed":c.degrade("watch closed");return;case"failed":console.error(`[serve] the shared compiler failed: ${t.message}`),c.failed(`the shared compiler failed: ${t.message}`),_({success:!1});return;case"refused":console.log(`[serve] ${o.projectName}: compiling on its own from now on \u2014 ${t.reason}`),b(),C();return;case"built":{if(console.info(t.text),B=t.sourceMaps,u.built(t),!t.ok){c.failed(),_({success:!1});return}const z=t.hash!==f;f=t.hash;const K=Y();c.built(z&&!K),_({success:!0,baseUrl:`http://localhost:${d}`});return}default:return}})}if(i(ne,"fe"),s(ne,"attachToSharedBuild"),(0,attach_1.sharedBuildOffered)()&&!frozen()){const r=await(0,attach_1.joinSharedBuild)({project:o.projectName,target:o.targetName??"serve-hmr",...o.configurationName?{configuration:o.configurationName}:{},options:m,targetOptions:{...e},env:definedEnv(process.env)});r.kind==="attached"?ne(r.session):(console.log(`[serve] ${o.projectName}: compiling on its own \u2014 ${r.reason}`),C())}else C();try{for(;;)yield await se()}finally{R(),b(),he()}}i(serveExecutor,"serveExecutor"),s(serveExecutor,"serveExecutor"),exports.default=serveExecutor,module.exports=serveExecutor,module.exports.default=serveExecutor;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/cluster",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.40",
|
|
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",
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@lensmcp/core": "1.25.
|
|
62
|
-
"@lensmcp/node-instrumentation": "1.25.
|
|
63
|
-
"@lensmcp/nx-plugin": "1.25.
|
|
61
|
+
"@lensmcp/core": "1.25.40",
|
|
62
|
+
"@lensmcp/node-instrumentation": "1.25.40",
|
|
63
|
+
"@lensmcp/nx-plugin": "1.25.40",
|
|
64
64
|
"fastify": "^5.8.5",
|
|
65
65
|
"@fastify/reply-from": "^12.6.2",
|
|
66
66
|
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
|
|
@@ -112,7 +112,7 @@ export interface BarrelAnalysis {
|
|
|
112
112
|
}
|
|
113
113
|
/** Measure every (project, library) pair reached through a root barrel. */
|
|
114
114
|
export declare function analyzeBarrelImports(input: BarrelAnalysisInput): BarrelAnalysis;
|
|
115
|
-
/** Executor memory per compiled module, MB — the fit to FoodGuard's seven watchers (issues/
|
|
115
|
+
/** Executor memory per compiled module, MB — the fit to FoodGuard's seven watchers (issues/closed/pod-memory-05). */
|
|
116
116
|
export declare const EXECUTOR_MB_PER_MODULE = 0.374;
|
|
117
117
|
/** A finding's text, evidence and cost — the payload a finding event carries. */
|
|
118
118
|
export interface HeavyBarrelFindingText {
|
package/shared-build/attach.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface AttachedTypecheckOptions {
|
|
|
40
40
|
workspaceRoot: string;
|
|
41
41
|
memoryLimit: number;
|
|
42
42
|
env?: NodeJS.ProcessEnv;
|
|
43
|
+
/** How often the tsconfig chain is read for a change. Default `CONFIG_POLL_MS` (config-files.ts). */
|
|
44
|
+
configPollMs?: number;
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
45
47
|
* The checker `LENSMCP_TYPECHECK` asks for, run in THIS process for THIS service — what its own compiler used to
|
|
@@ -52,5 +54,12 @@ export interface AttachedTypecheckOptions {
|
|
|
52
54
|
* the watcher's changed files and adds its program's files to what it watches. Its program lists only the
|
|
53
55
|
* tsconfig's own files, so the build host's file list for this bundle (`files`) is watched too — a library
|
|
54
56
|
* save re-checks every service that compiles it, as before.
|
|
57
|
+
*
|
|
58
|
+
* Either one also follows the tsconfig and every file it extends (issues/closed/foodguard-115). The checker read them
|
|
59
|
+
* when it started, and the build host follows them only for what it compiles: a `paths` change moves the service to
|
|
60
|
+
* a new compiler, and a type-check-only edit compiles nothing again. On a change, tsgo checks again (its shim is
|
|
61
|
+
* derived from the chain for every check) and fork-ts-checker is made again: it reads its configuration again only
|
|
62
|
+
* when its own tsconfig changes, never a file that one extends. The webpack override is not watched: no checker
|
|
63
|
+
* reads it.
|
|
55
64
|
*/
|
|
56
65
|
export declare function createAttachedTypecheck(opts: AttachedTypecheckOptions): AttachedTypecheck;
|
package/shared-build/attach.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`)!==
|
|
3
|
-
`);s.files&&(
|
|
1
|
+
"use strict";var y=Object.defineProperty;var k=(e,t)=>y(e,"name",{value:t,configurable:!0});var _=Object.defineProperty,o=k((e,t)=>_(e,"name",{value:t,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.JOIN_TIMEOUT_MS=void 0,exports.sharedBuildOffered=sharedBuildOffered,exports.joinSharedBuild=joinSharedBuild,exports.createAttachedTypecheck=createAttachedTypecheck;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),config_files_1=require("../config-files"),fork_ts_check_reporter_1=require("../fork-ts-check-reporter"),tsgo_check_plugin_1=require("../tsgo-check-plugin"),protocol_1=require("./protocol");exports.JOIN_TIMEOUT_MS=12e4;function sharedBuildOffered(e=process.env,t=process){return e[protocol_1.SHARED_BUILD_ENV]==="1"&&typeof t.send=="function"}k(sharedBuildOffered,"sharedBuildOffered"),o(sharedBuildOffered,"sharedBuildOffered");function joinSharedBuild(e,t={}){const c=t.proc??process,r=t.timeoutMs??exports.JOIN_TIMEOUT_MS;return new Promise(n=>{const p=[];let l,f=!1;const a=o(i=>{if(!(0,protocol_1.isRunnerInbound)(i))return;const d=i.event;if(!f)return d.type==="refused"?h({kind:"refused",reason:d.reason}):d.type!=="accepted"?void 0:h({kind:"attached",session:u(d)});l?l(d):p.push(d)},"onMessage"),s=o(()=>{c.removeListener("message",a)},"close"),u=o(i=>({accepted:i,listen(d){l=d;for(const w of p.splice(0))d(w)},close:s}),"makeSession"),h=o(i=>{f||(f=!0,clearTimeout(g),i.kind==="refused"&&s(),n(i))},"finish"),g=setTimeout(()=>h({kind:"refused",reason:`the build host did not answer within ${Math.round(r/1e3)} s`}),r);g.unref?.(),c.on("message",a);const m={channel:protocol_1.SHARED_BUILD_CHANNEL,type:"join",join:e};try{c.send?.(m,void 0,void 0,i=>{i&&h({kind:"refused",reason:`could not reach the gateway (${i.message})`})})}catch(i){h({kind:"refused",reason:`could not reach the gateway (${i.message})`})}})}k(joinSharedBuild,"joinSharedBuild"),o(joinSharedBuild,"joinSharedBuild");function createAttachedTypecheck(e){const t=e.env??process.env,c=t.LENSMCP_TYPECHECK;if(c==="off")return{built:o(()=>{},"built"),dispose:o(()=>{},"dispose")};if(c==="tsgo"){if((0,tsgo_check_plugin_1.resolveTsgoBin)(e.workspaceRoot,t.LENSMCP_TSGO_BIN)){const n=new tsgo_check_plugin_1.TsgoCheckPlugin({appName:e.appName,tsConfig:e.tsConfig,workspaceRoot:e.workspaceRoot,...t.LENSMCP_TSGO_BIN?{binPath:t.LENSMCP_TSGO_BIN}:{}}),p=[];n.apply({hooks:{done:{tap:o((a,s)=>p.push(s),"tap")}}});let l;const f=watchTypecheckConfig(e,a=>{l&&(console.log(`[serve] ${e.appName}: ${a} changed \u2014 type-checking again`),n.schedule())});return{built:o(a=>{l=a.ok;for(const s of p)s({hasErrors:o(()=>!a.ok,"hasErrors")})},"built"),dispose:o(()=>{f.close(),n.dispose()},"dispose")}}const r=`${tsgo_check_plugin_1.TSGO_MISSING_CAUSE}. Fell back to fork-ts-checker, which reports to the console only \u2014 so typecheck:// has no producer for this project until it is fixed`;console.warn(`[serve] ${e.appName}: LENSMCP_TYPECHECK=tsgo but ${r}.`),(0,tsgo_check_plugin_1.reportTsgoUnavailable)({appName:e.appName,tsConfig:e.tsConfig,workspaceRoot:e.workspaceRoot,detail:r})}return forkTsCheckerOnItsOwn(e)}k(createAttachedTypecheck,"createAttachedTypecheck"),o(createAttachedTypecheck,"createAttachedTypecheck");function forkTsCheckerOnItsOwn(e){const t=(0,fork_ts_check_reporter_1.loadForkTsChecker)(e.workspaceRoot),c=t?.default??t;if(typeof c!="function")return console.warn(`[serve] ${e.appName}: fork-ts-checker-webpack-plugin is not installed \u2014 this service is not type-checked`),{built:o(()=>{},"built"),dispose:o(()=>{},"dispose")};let r=[],n,p=!1;const l=o(()=>{try{n?.close(()=>{})}catch{}n=void 0},"close"),f=watchTypecheckConfig(e,s=>{p||!n||(console.log(`[serve] ${e.appName}: ${s} changed \u2014 type-checking again with a new fork-ts-checker`),l(),a())}),a=o(()=>{n=require("webpack")({mode:"development",context:e.workspaceRoot,entry:{},infrastructureLogging:{level:"info"},plugins:[{apply(s){s.hooks.afterCompile.tapPromise("LensmcpAttachedTypecheck",()=>new Promise(u=>setTimeout(u,0))),s.hooks.afterCompile.tap("LensmcpAttachedTypecheck",u=>{for(const h of r)u.fileDependencies.add(h)})}},new c({async:!0,typescript:{configFile:e.tsConfig,memoryLimit:e.memoryLimit}}),new fork_ts_check_reporter_1.ForkTsCheckReporterPlugin({appName:e.appName,tsConfig:e.tsConfig,workspaceRoot:e.workspaceRoot})]}).watch({ignored:["**/node_modules/**",`${path.join(e.workspaceRoot,"dist")}/**`]},()=>{})},"start");return{built(s){if(p)return;const u=s.files&&s.files.join(`
|
|
2
|
+
`)!==r.join(`
|
|
3
|
+
`);s.files&&(r=[...s.files]),n?u&&n.invalidate():a()},dispose(){p=!0,f.close(),l()}}}k(forkTsCheckerOnItsOwn,"forkTsCheckerOnItsOwn"),o(forkTsCheckerOnItsOwn,"forkTsCheckerOnItsOwn");function watchTypecheckConfig(e,t){return(0,config_files_1.watchConfigFiles)({files:o(()=>(0,tsgo_check_plugin_1.tsconfigChain)(e.tsConfig),"files"),changed:o(c=>t(c.map(r=>path.relative(e.workspaceRoot,r)||r).join(", ")),"changed"),...e.configPollMs!==void 0?{intervalMs:e.configPollMs}:{}})}k(watchTypecheckConfig,"watchTypecheckConfig"),o(watchTypecheckConfig,"watchTypecheckConfig");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The shared compiler (issues/
|
|
2
|
+
* The shared compiler (issues/closed/pod-memory-05): ONE webpack compilation for every service of a workspace
|
|
3
3
|
* instead of one watcher per service. Three processes talk:
|
|
4
4
|
*
|
|
5
5
|
* runner (the serve executor, ATTACHED) ⇄ gateway (relay) ⇄ build host (one per workspace)
|
package/tsgo-check-plugin.d.ts
CHANGED
|
@@ -168,6 +168,21 @@ export declare function effectivePaths(tsConfig: string, seen?: Set<string>): {
|
|
|
168
168
|
paths: Record<string, string[]>;
|
|
169
169
|
anchorDir: string;
|
|
170
170
|
} | null;
|
|
171
|
+
/**
|
|
172
|
+
* The tsconfig and every file it extends, as {@link effectivePaths} walks them — what a check's configuration is read
|
|
173
|
+
* from (issues/closed/foodguard-115). The tsconfig itself is always listed, even when it cannot be read: a half-saved
|
|
174
|
+
* file is watched until the next save. A file extended from node_modules changes only with an install, and is left
|
|
175
|
+
* out, as `configFilesOf` leaves it out. Read with this file's own parser, so a service that type-checks with tsgo
|
|
176
|
+
* never loads TypeScript into its process just to list its config files.
|
|
177
|
+
*/
|
|
178
|
+
export declare function tsconfigChain(tsConfig: string): string[];
|
|
179
|
+
/**
|
|
180
|
+
* Write the shim for one check, and return its path. Called before EVERY check (issues/closed/foodguard-115): the
|
|
181
|
+
* shim's `paths` REPLACE the ones the tsconfig chain declares, so they are the one part of the configuration tsgo
|
|
182
|
+
* does not read for itself. Written once, a shim kept the aliases the chain had when the service started: FoodGuard
|
|
183
|
+
* added `@foodguard-shared/execution-graph` to `tsconfig.base.json`, the bundle built with it, and every check for
|
|
184
|
+
* the next 80 minutes still reported «TS2307: Cannot find module». The file is left alone when it would not change.
|
|
185
|
+
*/
|
|
171
186
|
export declare function writeShimTsconfig(appName: string, tsConfig: string, workspaceRoot: string): string;
|
|
172
187
|
export declare class TsgoCheckPlugin {
|
|
173
188
|
private readonly opts;
|
package/tsgo-check-plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`&&(n=!1,t+=r);continue}if(s){r==="*"&&c==="/"&&(s=!1,
|
|
3
|
-
`)?
|
|
4
|
-
`);const g=(
|
|
1
|
+
"use strict";var k=Object.defineProperty;var a=(e,t)=>k(e,"name",{value:t,configurable:!0});var b=Object.defineProperty,l=a((e,t)=>b(e,"name",{value:t,configurable:!0}),"l");Object.defineProperty(exports,"__esModule",{value:!0}),exports.TsgoCheckPlugin=exports.TSGO_MISSING_CAUSE=exports.TSGO_PACKAGE=void 0,exports.checkerMaxProcs=checkerMaxProcs,exports.tsgoIncremental=tsgoIncremental,exports.tsgoBuildInfoPath=tsgoBuildInfoPath,exports.tsgoCheckArgs=tsgoCheckArgs,exports.resolveTsgoPackageDir=resolveTsgoPackageDir,exports.resolveTsgoBin=resolveTsgoBin,exports.reportTsgoUnavailable=reportTsgoUnavailable,exports.effectivePaths=effectivePaths,exports.tsconfigChain=tsconfigChain,exports.writeShimTsconfig=writeShimTsconfig;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),typecheck_bus_1=require("./typecheck-bus"),typecheck_queue_1=require("./typecheck-queue"),DEFAULT_TIMEOUT_MS=12e4,DEFAULT_MAX_PROCS=4;function checkerMaxProcs(e=process.env){const t=Number(e.LENSMCP_TYPECHECK_GOMAXPROCS);return Number.isInteger(t)&&t>=1?t:DEFAULT_MAX_PROCS}a(checkerMaxProcs,"checkerMaxProcs"),l(checkerMaxProcs,"checkerMaxProcs");function tsgoIncremental(e=process.env){const t=(e.LENSMCP_TSGO_INCREMENTAL??"").trim().toLowerCase();return!(t==="0"||t==="false"||t==="off"||t==="no")}a(tsgoIncremental,"tsgoIncremental"),l(tsgoIncremental,"tsgoIncremental");function tsgoBuildInfoPath(e,t){return path.join(t,"tmp","lensmcp-tsgo",`${e}.tsbuildinfo`)}a(tsgoBuildInfoPath,"tsgoBuildInfoPath"),l(tsgoBuildInfoPath,"tsgoBuildInfoPath");function tsgoCheckArgs(e,t){return["--noEmit","--pretty","false",...t?["--incremental","--tsBuildInfoFile",t]:[],"-p",e]}a(tsgoCheckArgs,"tsgoCheckArgs"),l(tsgoCheckArgs,"tsgoCheckArgs");const livePlugins=new Set;let exitHooked=!1;function stopWithTheProcess(e){livePlugins.add(e),!exitHooked&&(exitHooked=!0,process.once("exit",()=>{for(const t of livePlugins)t.dispose()}))}a(stopWithTheProcess,"stopWithTheProcess"),l(stopWithTheProcess,"stopWithTheProcess"),exports.TSGO_PACKAGE="@typescript/native-preview";function shimNames(e){return e==="win32"?["tsgo.cmd","tsgo.exe"]:["tsgo"]}a(shimNames,"shimNames"),l(shimNames,"shimNames");function isExecutableFile(e,t){try{if(!fs.statSync(e).isFile())return!1}catch{return!1}if(t==="win32")return!0;try{return fs.accessSync(e,fs.constants.X_OK),!0}catch{return!1}}a(isExecutableFile,"isExecutableFile"),l(isExecutableFile,"isExecutableFile");function resolveTsgoPackageDir(){try{return path.dirname(require.resolve(`${exports.TSGO_PACKAGE}/package.json`))}catch{}try{let e=path.dirname(require.resolve(exports.TSGO_PACKAGE));for(let t=0;t<6;t++){const o=path.join(e,"package.json");if(fs.existsSync(o))try{const{name:s}=JSON.parse(fs.readFileSync(o,"utf8"));if(s===exports.TSGO_PACKAGE)return e}catch{}const n=path.dirname(e);if(n===e)break;e=n}}catch{}return null}a(resolveTsgoPackageDir,"resolveTsgoPackageDir"),l(resolveTsgoPackageDir,"resolveTsgoPackageDir");function packageDeclaredBin(e){try{const{bin:t}=JSON.parse(fs.readFileSync(path.join(e,"package.json"),"utf8")),o=typeof t=="string"?t:t?.tsgo;return o?path.join(e,o):null}catch{return null}}a(packageDeclaredBin,"packageDeclaredBin"),l(packageDeclaredBin,"packageDeclaredBin");function ownBinCandidates(e,t){const o=path.resolve(e,"..","..",".bin"),n=shimNames(t).map(i=>path.join(o,i)),s=t==="win32"?null:packageDeclaredBin(e);return s?[s,...n]:n}a(ownBinCandidates,"ownBinCandidates"),l(ownBinCandidates,"ownBinCandidates");function resolveTsgoBin(e,t,o={}){const n=o.platform??process.platform,s=o.isExecutable??(r=>isExecutableFile(r,n));if(t)return s(t)?t:null;for(const r of shimNames(n)){const c=path.join(e,"node_modules",".bin",r);if(s(c))return c}const i=(o.ownPackageDir??resolveTsgoPackageDir)();return i?ownBinCandidates(i,n).find(s)??null:null}a(resolveTsgoBin,"resolveTsgoBin"),l(resolveTsgoBin,"resolveTsgoBin"),exports.TSGO_MISSING_CAUSE=`tsgo binary not found \u2014 @lensmcp/cluster depends on ${exports.TSGO_PACKAGE}, so an absent binary means an incomplete install (reinstall node_modules), a platform with no ${exports.TSGO_PACKAGE} build, or a LENSMCP_TSGO_BIN override pointing at a missing or non-executable file`;function reportTsgoUnavailable(e){try{(0,typecheck_bus_1.publishTypecheckRun)({workspaceRoot:e.workspaceRoot,project:e.appName,tool:"tsgo",tsConfig:e.tsConfig,output:"",durationMs:0,toolError:e.detail,...e.eventFile?{eventFile:e.eventFile}:{}})}catch{}}a(reportTsgoUnavailable,"reportTsgoUnavailable"),l(reportTsgoUnavailable,"reportTsgoUnavailable");function parseJsonc(e){let t="",o=!1,n=!1,s=!1;for(let i=0;i<e.length;i++){const r=e[i],c=e[i+1];if(n){r===`
|
|
2
|
+
`&&(n=!1,t+=r);continue}if(s){r==="*"&&c==="/"&&(s=!1,i++);continue}if(o){t+=r,r==="\\"?(t+=c??"",i++):r==='"'&&(o=!1);continue}if(r==='"'){o=!0,t+=r;continue}if(r==="/"&&c==="/"){n=!0,i++;continue}if(r==="/"&&c==="*"){s=!0,i++;continue}t+=r}try{return JSON.parse(t.replace(/,(\s*[}\]])/g,"$1"))}catch{return null}}a(parseJsonc,"parseJsonc"),l(parseJsonc,"parseJsonc");function resolveExtends(e,t){if(!e.startsWith(".")&&!path.isAbsolute(e))return null;const o=path.isAbsolute(e)?e:path.resolve(t,e);for(const n of[o,`${o}.json`,path.join(o,"tsconfig.json")])try{if(fs.statSync(n).isFile())return n}catch{}return null}a(resolveExtends,"resolveExtends"),l(resolveExtends,"resolveExtends");function effectivePaths(e,t=new Set){if(t.has(e))return null;t.add(e);let o;try{o=fs.readFileSync(e,"utf8")}catch{return null}const n=parseJsonc(o);if(!n)return null;const s=path.dirname(e),i=n.compilerOptions??{},r=Array.isArray(n.extends)?n.extends.filter(p=>typeof p=="string"):typeof n.extends=="string"?[n.extends]:[],c=l(()=>{for(const p of[...r].reverse()){const d=resolveExtends(p,s);if(!d)continue;const f=effectivePaths(d,t);if(f)return f}return null},"inherited"),u=i.paths,h=typeof i.baseUrl=="string"?path.resolve(s,i.baseUrl):void 0;if(!u){const p=c();return p?{paths:p.paths,anchorDir:h??p.anchorDir}:null}return{paths:u,anchorDir:h??c()?.anchorDir??s}}a(effectivePaths,"effectivePaths"),l(effectivePaths,"effectivePaths");function tsconfigChain(e){const t=[],o=l(n=>{if(t.includes(n))return;t.push(n);let s;try{s=parseJsonc(fs.readFileSync(n,"utf8"))}catch{return}const i=s?.extends,r=Array.isArray(i)?i.filter(c=>typeof c=="string"):typeof i=="string"?[i]:[];for(const c of r){const u=resolveExtends(c,path.dirname(n));u&&!u.split(path.sep).includes("node_modules")&&o(u)}},"visit");return o(path.resolve(e)),t}a(tsconfigChain,"tsconfigChain"),l(tsconfigChain,"tsconfigChain");function writeShimTsconfig(e,t,o){const n=path.join(o,"tmp","lensmcp-tsgo");fs.mkdirSync(n,{recursive:!0});const s=path.join(n,`${e}.tsconfig.json`),i=effectivePaths(t),r=i?Object.fromEntries(Object.entries(i.paths).map(([h,p])=>[h,(Array.isArray(p)?p:[]).map(d=>path.isAbsolute(d)?d:path.resolve(i.anchorDir,d))])):void 0,c=JSON.stringify({extends:t,compilerOptions:{module:"preserve",moduleResolution:"bundler",isolatedModules:!1,baseUrl:null,...r&&Object.keys(r).length?{paths:r}:{}}},null,2);let u;try{u=fs.readFileSync(s,"utf8")}catch{}return u!==c&&fs.writeFileSync(s,c),s}a(writeShimTsconfig,"writeShimTsconfig"),l(writeShimTsconfig,"writeShimTsconfig");class TsgoCheckPlugin{static{a(this,"TsgoCheckPlugin")}static{l(this,"TsgoCheckPlugin")}constructor(t){this.timer=null,this.phase="idle",this.dirty=!1,this.ticket=null,this.inflight=null,this.askedAt=0,this.shimPath=null,this.disabled=!1,this.disposed=!1,this.opts=t,this.spawnImpl=t.spawnImpl??node_child_process_1.spawn,this.queue=t.queue??(0,typecheck_queue_1.machineTypecheckQueue)(),this.buildInfo=t.incremental??tsgoIncremental()?tsgoBuildInfoPath(t.appName,t.workspaceRoot):void 0}apply(t){t.hooks.done.tap("TsgoCheckPlugin",o=>{o.hasErrors()||this.schedule()}),t.hooks.shutdown?.tap?.("TsgoCheckPlugin",()=>this.dispose()),t.hooks.watchClose?.tap?.("TsgoCheckPlugin",()=>this.dispose()),stopWithTheProcess(this)}schedule(){this.disabled||this.disposed||(this.timer&&clearTimeout(this.timer),this.timer=setTimeout(()=>this.request(),this.opts.debounceMs??500),this.timer.unref?.())}request(){if(this.disabled||this.disposed)return;if(this.phase==="running"){this.dirty=!0;return}if(this.phase==="waiting")return;this.phase="waiting",this.askedAt=Date.now();const t=this.queue(this.opts.appName,()=>this.run(),{onHold:l(o=>this.held(o),"onHold")});this.isIdle()?t.release():this.ticket=t}held(t){if(!this.disposed&&(console.log(`[tsgo] ${this.opts.appName}: check held \u2014 ${t.availableMb} MB free, it waits for ${t.minFreeMb} MB (LENSMCP_TYPECHECK_MIN_FREE_MB)`),this.opts.publishEvents!==!1))try{(0,typecheck_bus_1.publishTypecheckHeld)({workspaceRoot:this.opts.workspaceRoot,project:this.opts.appName,tool:"tsgo",tsConfig:this.opts.tsConfig,availableMb:t.availableMb,minFreeMb:t.minFreeMb,...this.opts.eventFile?{eventFile:this.opts.eventFile}:{}})}catch{}}forgetBuildInfo(){if(this.buildInfo)try{fs.rmSync(this.buildInfo,{force:!0})}catch{}}isIdle(){return this.phase==="idle"}publish(t){if(this.opts.publishEvents!==!1)try{(0,typecheck_bus_1.publishTypecheckRun)({workspaceRoot:this.opts.workspaceRoot,project:this.opts.appName,tool:"tsgo",tsConfig:this.opts.tsConfig,output:t.output,durationMs:t.durationMs,...t.toolError?{toolError:t.toolError}:{},...this.opts.eventFile?{eventFile:this.opts.eventFile}:{}})}catch{}}run(){if(this.disposed)return this.leave();let t;try{t=resolveTsgoBin(this.opts.workspaceRoot,this.opts.binPath),t&&(this.shimPath=writeShimTsconfig(this.opts.appName,this.opts.tsConfig,this.opts.workspaceRoot))}catch(h){return console.warn(`[tsgo] ${this.opts.appName}: could not prepare the check \u2014 ${h.message}`),this.publish({output:"",durationMs:0,toolError:`could not prepare the check: ${h.message}`}),this.leave()}if(!t){this.disabled=!0;const h=`${exports.TSGO_MISSING_CAUSE}. Type checking is disabled for the rest of this session`;return console.warn(`[tsgo] ${this.opts.appName}: ${h}.`),this.publish({output:"",durationMs:0,toolError:h}),this.leave()}this.phase="running",this.dirty=!1;const o=Date.now(),n=o-this.askedAt,s=this.spawnImpl(t,tsgoCheckArgs(this.shimPath,this.buildInfo),{cwd:this.opts.workspaceRoot,stdio:["ignore","pipe","pipe"],env:{...process.env,GOMAXPROCS:process.env.GOMAXPROCS??String(this.opts.maxProcs??checkerMaxProcs())}});this.inflight=s;let i="",r=!1;const c=this.opts.timeoutMs??DEFAULT_TIMEOUT_MS,u=setTimeout(()=>{r=!0,s.kill("SIGKILL")},c);u.unref?.(),s.stdout?.on("data",h=>{i+=h.toString()}),s.stderr?.on("data",h=>{i+=h.toString()}),s.on("error",()=>{clearTimeout(u),this.inflight===s&&(console.warn(`[tsgo] ${this.opts.appName}: failed to spawn ${t}`),this.forgetBuildInfo(),this.publish({output:"",durationMs:Date.now()-o,toolError:`failed to spawn ${t}`}),this.finish(s))}),s.on("close",(h,p)=>{if(clearTimeout(u),this.inflight!==s)return;const d=Date.now()-o,f=(d/1e3).toFixed(1),m=n>=1e3?`${f}s, waited ${(n/1e3).toFixed(1)}s`:`${f}s`;if(this.disposed)return this.finish(s);if(r){const y=`the check was killed after ${Math.round(c/1e3)}s without finishing`;return console.warn(`[tsgo] ${this.opts.appName}: ${y} \u2014 no verdict.`),this.forgetBuildInfo(),this.publish({output:"",durationMs:d,toolError:y}),this.finish(s)}if(p)return this.forgetBuildInfo(),this.finish(s);if(h===0)return console.log(`[tsgo] ${this.opts.appName}: types clean (${m})`),this.publish({output:"",durationMs:d}),this.finish(s);i.trim()&&process.stdout.write(i.endsWith(`
|
|
3
|
+
`)?i:`${i}
|
|
4
|
+
`);const g=(i.match(/error TS\d+/g)??[]).length;console.log(`[tsgo] ${this.opts.appName}: ${g} type error${g===1?"":"s"} (${m})`),g===0&&this.forgetBuildInfo(),this.publish({output:i,durationMs:d,...g===0?{toolError:`tsgo exited ${h} with no diagnostics: ${firstLine(i)}`}:{}}),this.finish(s)})}finish(t){this.inflight===t&&(this.inflight=null),this.leave(),this.dirty&&!this.disposed&&(this.dirty=!1,this.request())}leave(){this.phase="idle",this.ticket?.release(),this.ticket=null}dispose(){this.disposed||(this.disposed=!0,livePlugins.delete(this),this.timer&&clearTimeout(this.timer),this.inflight&&(this.inflight.kill("SIGKILL"),this.forgetBuildInfo()),this.ticket?.release(),this.ticket=null)}}exports.TsgoCheckPlugin=TsgoCheckPlugin;function firstLine(e){return e.trim().split(`
|
|
5
5
|
`)[0]?.slice(0,300)??""}a(firstLine,"firstLine"),l(firstLine,"firstLine");
|