@lensmcp/cluster 1.23.11 → 1.23.12
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/package.json +4 -4
- package/tsgo-check-plugin.d.ts +47 -2
- package/tsgo-check-plugin.js +5 -5
- package/typecheck-queue.d.ts +36 -0
- package/typecheck-queue.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/cluster",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.12",
|
|
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.
|
|
61
|
-
"@lensmcp/node-instrumentation": "1.23.
|
|
62
|
-
"@lensmcp/nx-plugin": "1.23.
|
|
60
|
+
"@lensmcp/core": "1.23.12",
|
|
61
|
+
"@lensmcp/node-instrumentation": "1.23.12",
|
|
62
|
+
"@lensmcp/nx-plugin": "1.23.12",
|
|
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",
|
package/tsgo-check-plugin.d.ts
CHANGED
|
@@ -5,7 +5,20 @@
|
|
|
5
5
|
* 2-worker pair (~80MB+) per service, ~1GB across a 13-service cluster, alive even when nobody
|
|
6
6
|
* saves. tsgo (@typescript/native-preview, the native TS compiler) full-checks one of these
|
|
7
7
|
* services cold in ~1-3s, so instead of holding a program we re-check COLD after each successful
|
|
8
|
-
* rebuild (debounced
|
|
8
|
+
* rebuild (debounced): idle cost is exactly zero.
|
|
9
|
+
*
|
|
10
|
+
* ONE AT A TIME (issues/foodguard/34). "Zero resident" is not "small": a cold check of a real service
|
|
11
|
+
* peaks at 1.4-3.4GB. Every service's plugin used to start its own the moment its build finished, so
|
|
12
|
+
* one edit to a shared file started one per service at the same instant; and a newer build was meant
|
|
13
|
+
* to REPLACE the running check with SIGTERM, which this checker ignores — it runs to the end and
|
|
14
|
+
* exits 0 — so each further build ADDED a process. Fifteen at once on a 32GB machine turned a 2s
|
|
15
|
+
* check into two minutes, then fourteen. So now:
|
|
16
|
+
* - a check waits its turn in a MACHINE-WIDE line (`typecheck-queue.ts`), one slot by default;
|
|
17
|
+
* - a service has at most one check running and one asked for: a build that lands while a check
|
|
18
|
+
* runs is remembered, and one more check follows — a running check is never killed to be replaced;
|
|
19
|
+
* - stopping one for real is SIGKILL, and that happens only on shutdown or on a timeout, which is
|
|
20
|
+
* reported as the checker failing, never as a verdict;
|
|
21
|
+
* - one checker is held to a few cores (`GOMAXPROCS`).
|
|
9
22
|
*
|
|
10
23
|
* The output CONTRACT is the point: `--pretty false` prints classic `file.ts(1,2): error TS1234:`
|
|
11
24
|
* lines to the executor's console, which the gateway sniffs (`error TS\d+`) into the lens
|
|
@@ -35,6 +48,7 @@
|
|
|
35
48
|
* reports itself onto the lens bus instead of only warning into a log nobody reads.
|
|
36
49
|
*/
|
|
37
50
|
import { spawn } from 'node:child_process';
|
|
51
|
+
import { type TypecheckQueue } from './typecheck-queue';
|
|
38
52
|
export interface TsgoCheckPluginOptions {
|
|
39
53
|
appName: string;
|
|
40
54
|
/** Absolute path to the app tsconfig the check covers. */
|
|
@@ -53,7 +67,15 @@ export interface TsgoCheckPluginOptions {
|
|
|
53
67
|
eventFile?: string;
|
|
54
68
|
/** Set false to skip publishing to the lens bus (console output is unchanged). */
|
|
55
69
|
publishEvents?: boolean;
|
|
70
|
+
/** The line a check waits in before it starts. Defaults to the machine-wide one. Injectable for tests. */
|
|
71
|
+
queue?: TypecheckQueue;
|
|
72
|
+
/** A check still running after this long is killed and reported as a tool failure. Default 120s. */
|
|
73
|
+
timeoutMs?: number;
|
|
74
|
+
/** Cores one checker may use (`GOMAXPROCS`). Default {@link checkerMaxProcs}. */
|
|
75
|
+
maxProcs?: number;
|
|
56
76
|
}
|
|
77
|
+
/** `LENSMCP_TYPECHECK_GOMAXPROCS`, else {@link DEFAULT_MAX_PROCS}. An explicit `GOMAXPROCS` in the env wins over both. */
|
|
78
|
+
export declare function checkerMaxProcs(env?: NodeJS.ProcessEnv): number;
|
|
57
79
|
/** The npm package that ships the tsgo compiler — a real `dependency` of `@lensmcp/cluster`. */
|
|
58
80
|
export declare const TSGO_PACKAGE = "@typescript/native-preview";
|
|
59
81
|
/** Seams for {@link resolveTsgoBin}, so every branch of the precedence is testable without an install. */
|
|
@@ -133,19 +155,42 @@ export declare function writeShimTsconfig(appName: string, tsConfig: string, wor
|
|
|
133
155
|
export declare class TsgoCheckPlugin {
|
|
134
156
|
private readonly opts;
|
|
135
157
|
private readonly spawnImpl;
|
|
158
|
+
private readonly queue;
|
|
136
159
|
private timer;
|
|
160
|
+
/** idle → waiting (in line) → running → idle. */
|
|
161
|
+
private phase;
|
|
162
|
+
/** A build finished while a check was running: one more check follows it. */
|
|
163
|
+
private dirty;
|
|
164
|
+
private ticket;
|
|
137
165
|
private inflight;
|
|
166
|
+
private askedAt;
|
|
138
167
|
private shimPath;
|
|
139
168
|
private disabled;
|
|
169
|
+
private disposed;
|
|
140
170
|
constructor(opts: TsgoCheckPluginOptions);
|
|
141
171
|
apply(compiler: any): void;
|
|
142
|
-
/** Debounced: rapid successive rebuilds collapse into one
|
|
172
|
+
/** Debounced: rapid successive rebuilds collapse into one request for a check. */
|
|
143
173
|
schedule(): void;
|
|
174
|
+
/**
|
|
175
|
+
* A build asks for a check. At most one runs and one is asked for:
|
|
176
|
+
* - already in line → nothing to add, the check reads the files when its turn comes;
|
|
177
|
+
* - running → remembered, and ONE more check follows this one. The running check is left alone:
|
|
178
|
+
* it cannot be stopped politely, and alone it is done in seconds.
|
|
179
|
+
*/
|
|
180
|
+
private request;
|
|
181
|
+
/** Asked through a method on purpose: `run()` may have moved the phase inside the call above. */
|
|
182
|
+
private isIdle;
|
|
144
183
|
/**
|
|
145
184
|
* Publish one completed check onto the lens bus so `typecheck://*` reflects
|
|
146
185
|
* it. Strictly passive + fully swallowed: the console output above is the
|
|
147
186
|
* contract webpack/the gateway rely on, this is pure observation on top.
|
|
148
187
|
*/
|
|
149
188
|
private publish;
|
|
189
|
+
/** This service's turn in the line. */
|
|
150
190
|
private run;
|
|
191
|
+
/** The check is over: give the slot back, and check once more if a build landed meanwhile. */
|
|
192
|
+
private finish;
|
|
193
|
+
private leave;
|
|
194
|
+
/** The dev server is going away: leave the line, and stop the checker for real (it ignores SIGTERM). */
|
|
195
|
+
dispose(): void;
|
|
151
196
|
}
|
package/tsgo-check-plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`&&(
|
|
3
|
-
`)?
|
|
4
|
-
`);const
|
|
5
|
-
`)[0]?.slice(0,300)??""}
|
|
1
|
+
"use strict";var y=Object.defineProperty;var h=(e,t)=>y(e,"name",{value:t,configurable:!0});var S=Object.defineProperty,a=h((e,t)=>S(e,"name",{value:t,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.TsgoCheckPlugin=exports.TSGO_MISSING_CAUSE=exports.TSGO_PACKAGE=void 0,exports.checkerMaxProcs=checkerMaxProcs,exports.resolveTsgoPackageDir=resolveTsgoPackageDir,exports.resolveTsgoBin=resolveTsgoBin,exports.reportTsgoUnavailable=reportTsgoUnavailable,exports.effectivePaths=effectivePaths,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}h(checkerMaxProcs,"checkerMaxProcs"),a(checkerMaxProcs,"checkerMaxProcs");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()}))}h(stopWithTheProcess,"stopWithTheProcess"),a(stopWithTheProcess,"stopWithTheProcess"),exports.TSGO_PACKAGE="@typescript/native-preview";function shimNames(e){return e==="win32"?["tsgo.cmd","tsgo.exe"]:["tsgo"]}h(shimNames,"shimNames"),a(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}}h(isExecutableFile,"isExecutableFile"),a(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 r=path.dirname(e);if(r===e)break;e=r}}catch{}return null}h(resolveTsgoPackageDir,"resolveTsgoPackageDir"),a(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}}h(packageDeclaredBin,"packageDeclaredBin"),a(packageDeclaredBin,"packageDeclaredBin");function ownBinCandidates(e,t){const o=path.resolve(e,"..","..",".bin"),r=shimNames(t).map(i=>path.join(o,i)),s=t==="win32"?null:packageDeclaredBin(e);return s?[s,...r]:r}h(ownBinCandidates,"ownBinCandidates"),a(ownBinCandidates,"ownBinCandidates");function resolveTsgoBin(e,t,o={}){const r=o.platform??process.platform,s=o.isExecutable??(n=>isExecutableFile(n,r));if(t)return s(t)?t:null;for(const n of shimNames(r)){const l=path.join(e,"node_modules",".bin",n);if(s(l))return l}const i=(o.ownPackageDir??resolveTsgoPackageDir)();return i?ownBinCandidates(i,r).find(s)??null:null}h(resolveTsgoBin,"resolveTsgoBin"),a(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{}}h(reportTsgoUnavailable,"reportTsgoUnavailable"),a(reportTsgoUnavailable,"reportTsgoUnavailable");function parseJsonc(e){let t="",o=!1,r=!1,s=!1;for(let i=0;i<e.length;i++){const n=e[i],l=e[i+1];if(r){n===`
|
|
2
|
+
`&&(r=!1,t+=n);continue}if(s){n==="*"&&l==="/"&&(s=!1,i++);continue}if(o){t+=n,n==="\\"?(t+=l??"",i++):n==='"'&&(o=!1);continue}if(n==='"'){o=!0,t+=n;continue}if(n==="/"&&l==="/"){r=!0,i++;continue}if(n==="/"&&l==="*"){s=!0,i++;continue}t+=n}try{return JSON.parse(t.replace(/,(\s*[}\]])/g,"$1"))}catch{return null}}h(parseJsonc,"parseJsonc"),a(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 r of[o,`${o}.json`,path.join(o,"tsconfig.json")])try{if(fs.statSync(r).isFile())return r}catch{}return null}h(resolveExtends,"resolveExtends"),a(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 r=parseJsonc(o);if(!r)return null;const s=path.dirname(e),i=r.compilerOptions??{},n=Array.isArray(r.extends)?r.extends.filter(p=>typeof p=="string"):typeof r.extends=="string"?[r.extends]:[],l=a(()=>{for(const p of[...n].reverse()){const d=resolveExtends(p,s);if(!d)continue;const f=effectivePaths(d,t);if(f)return f}return null},"inherited"),u=i.paths,c=typeof i.baseUrl=="string"?path.resolve(s,i.baseUrl):void 0;if(!u){const p=l();return p?{paths:p.paths,anchorDir:c??p.anchorDir}:null}return{paths:u,anchorDir:c??l()?.anchorDir??s}}h(effectivePaths,"effectivePaths"),a(effectivePaths,"effectivePaths");function writeShimTsconfig(e,t,o){const r=path.join(o,"tmp","lensmcp-tsgo");fs.mkdirSync(r,{recursive:!0});const s=path.join(r,`${e}.tsconfig.json`),i=effectivePaths(t),n=i?Object.fromEntries(Object.entries(i.paths).map(([l,u])=>[l,(Array.isArray(u)?u:[]).map(c=>path.isAbsolute(c)?c:path.resolve(i.anchorDir,c))])):void 0;return fs.writeFileSync(s,JSON.stringify({extends:t,compilerOptions:{module:"preserve",moduleResolution:"bundler",isolatedModules:!1,baseUrl:null,...n&&Object.keys(n).length?{paths:n}:{}}},null,2)),s}h(writeShimTsconfig,"writeShimTsconfig"),a(writeShimTsconfig,"writeShimTsconfig");class TsgoCheckPlugin{static{h(this,"TsgoCheckPlugin")}static{a(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)()}apply(t){t.hooks.done.tap("TsgoCheckPlugin",o=>{o.hasErrors()||this.schedule()}),t.hooks.shutdown?.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());this.isIdle()?t.release():this.ticket=t}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&&(this.shimPath=writeShimTsconfig(this.opts.appName,this.opts.tsConfig,this.opts.workspaceRoot))}catch(c){return console.warn(`[tsgo] ${this.opts.appName}: could not prepare the check \u2014 ${c.message}`),this.publish({output:"",durationMs:0,toolError:`could not prepare the check: ${c.message}`}),this.leave()}if(!t){this.disabled=!0;const c=`${exports.TSGO_MISSING_CAUSE}. Type checking is disabled for the rest of this session`;return console.warn(`[tsgo] ${this.opts.appName}: ${c}.`),this.publish({output:"",durationMs:0,toolError:c}),this.leave()}this.phase="running",this.dirty=!1;const o=Date.now(),r=o-this.askedAt,s=this.spawnImpl(t,["--noEmit","--pretty","false","-p",this.shimPath],{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="",n=!1;const l=this.opts.timeoutMs??DEFAULT_TIMEOUT_MS,u=setTimeout(()=>{n=!0,s.kill("SIGKILL")},l);u.unref?.(),s.stdout?.on("data",c=>{i+=c.toString()}),s.stderr?.on("data",c=>{i+=c.toString()}),s.on("error",()=>{clearTimeout(u),this.inflight===s&&(console.warn(`[tsgo] ${this.opts.appName}: failed to spawn ${t}`),this.publish({output:"",durationMs:Date.now()-o,toolError:`failed to spawn ${t}`}),this.finish(s))}),s.on("close",(c,p)=>{if(clearTimeout(u),this.inflight!==s)return;const d=Date.now()-o,f=(d/1e3).toFixed(1),m=r>=1e3?`${f}s, waited ${(r/1e3).toFixed(1)}s`:`${f}s`;if(this.disposed)return this.finish(s);if(n){const k=`the check was killed after ${Math.round(l/1e3)}s without finishing`;return console.warn(`[tsgo] ${this.opts.appName}: ${k} \u2014 no verdict.`),this.publish({output:"",durationMs:d,toolError:k}),this.finish(s)}if(p)return this.finish(s);if(c===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})`),this.publish({output:i,durationMs:d,...g===0?{toolError:`tsgo exited ${c} 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?.kill("SIGKILL"),this.ticket?.release(),this.ticket=null)}}exports.TsgoCheckPlugin=TsgoCheckPlugin;function firstLine(e){return e.trim().split(`
|
|
5
|
+
`)[0]?.slice(0,300)??""}h(firstLine,"firstLine"),a(firstLine,"firstLine");
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface QueueTicket {
|
|
2
|
+
/** Give the slot back, or leave the line. Idempotent. */
|
|
3
|
+
release(): void;
|
|
4
|
+
}
|
|
5
|
+
/** Join the line; `onTurn` is called once, when this caller may run. */
|
|
6
|
+
export type TypecheckQueue = (label: string, onTurn: () => void) => QueueTicket;
|
|
7
|
+
export interface MachineQueueOptions {
|
|
8
|
+
dir?: string;
|
|
9
|
+
slots?: number;
|
|
10
|
+
/** How often a waiter looks again. */
|
|
11
|
+
pollMs?: number;
|
|
12
|
+
/**
|
|
13
|
+
* How long a new ticket waits before its first look. A ticket created in the same clock tick as
|
|
14
|
+
* ours may not be listed yet; after this pause it is, so two newcomers can never both see
|
|
15
|
+
* themselves first.
|
|
16
|
+
*/
|
|
17
|
+
settleMs?: number;
|
|
18
|
+
/** A ticket older than this is treated as abandoned whatever its pid says (a reused pid looks alive). */
|
|
19
|
+
maxAgeMs?: number;
|
|
20
|
+
pid?: number;
|
|
21
|
+
isAlive?: (pid: number) => boolean;
|
|
22
|
+
now?: () => number;
|
|
23
|
+
}
|
|
24
|
+
/** `~/.lensmcp/typecheck-queue` — machine-wide on purpose; `LENSMCP_HOME` moves it (tests, sandboxes). */
|
|
25
|
+
export declare function typecheckQueueDir(env?: NodeJS.ProcessEnv): string;
|
|
26
|
+
/** `LENSMCP_TYPECHECK_CONCURRENCY`, default ONE. Anything unreadable is one, never unlimited. */
|
|
27
|
+
export declare function typecheckSlots(env?: NodeJS.ProcessEnv): number;
|
|
28
|
+
/**
|
|
29
|
+
* The line, oldest first. A ticket whose owner is dead — or that has sat past `maxAgeMs` — is
|
|
30
|
+
* deleted on the way, so a crashed service never holds a slot.
|
|
31
|
+
*/
|
|
32
|
+
export declare function liveTickets(dir: string, options?: Pick<MachineQueueOptions, 'isAlive' | 'now' | 'maxAgeMs'>): string[];
|
|
33
|
+
/** The real queue. Every option has a production default; they are parameters for the spec. */
|
|
34
|
+
export declare function machineTypecheckQueue(options?: MachineQueueOptions): TypecheckQueue;
|
|
35
|
+
/** No queue: run at once. For specs of the plugin's own logic, and for nothing else. */
|
|
36
|
+
export declare const immediateQueue: TypecheckQueue;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var T=Object.defineProperty;var c=(e,t)=>T(e,"name",{value:t,configurable:!0});var E=Object.defineProperty,r=c((e,t)=>E(e,"name",{value:t,configurable:!0}),"r");Object.defineProperty(exports,"__esModule",{value:!0}),exports.immediateQueue=void 0,exports.typecheckQueueDir=typecheckQueueDir,exports.typecheckSlots=typecheckSlots,exports.liveTickets=liveTickets,exports.machineTypecheckQueue=machineTypecheckQueue;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path")),TICKET_SUFFIX=".ticket",DEFAULT_POLL_MS=250,DEFAULT_SETTLE_MS=120,DEFAULT_MAX_AGE_MS=15*6e4,MAX_SLOTS=8;function typecheckQueueDir(e=process.env){return path.join(e.LENSMCP_HOME||os.homedir(),".lensmcp","typecheck-queue")}c(typecheckQueueDir,"typecheckQueueDir"),r(typecheckQueueDir,"typecheckQueueDir");function typecheckSlots(e=process.env){const t=Number(e.LENSMCP_TYPECHECK_CONCURRENCY);return Number.isInteger(t)&&t>=1?Math.min(t,MAX_SLOTS):1}c(typecheckSlots,"typecheckSlots"),r(typecheckSlots,"typecheckSlots");function pidAlive(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}c(pidAlive,"pidAlive"),r(pidAlive,"pidAlive");const ticketPid=r(e=>{const t=Number(e.split("-")[0]);return Number.isInteger(t)&&t>0?t:void 0},"ticketPid");function liveTickets(e,t={}){const h=t.isAlive??pidAlive,S=(t.now??Date.now)(),m=t.maxAgeMs??DEFAULT_MAX_AGE_MS;let s;try{s=fs.readdirSync(e).filter(i=>i.endsWith(TICKET_SUFFIX))}catch{return[]}const u=[];for(const i of s){const n=path.join(e,i);let o;try{o=fs.statSync(n,{bigint:!0})}catch{continue}const a=ticketPid(i);if(a===void 0||!h(a)||S-Number(o.mtimeMs)>m){try{fs.unlinkSync(n)}catch{}continue}u.push({name:i,at:o.mtimeNs})}return u.sort((i,n)=>i.at<n.at?-1:i.at>n.at?1:i.name<n.name?-1:i.name>n.name?1:0),u.map(i=>i.name)}c(liveTickets,"liveTickets"),r(liveTickets,"liveTickets");let ticketSeq=0;function machineTypecheckQueue(e={}){const t=e.dir??typecheckQueueDir(),h=Math.max(1,e.slots??typecheckSlots()),S=e.pollMs??DEFAULT_POLL_MS,m=e.settleMs??DEFAULT_SETTLE_MS,s=e.pid??process.pid,u=r(()=>liveTickets(t,e),"scan");return(i,n)=>{const o=`${s}-${(ticketSeq++).toString(36)}-${Math.random().toString(36).slice(2,8)}${TICKET_SUFFIX}`,a=path.join(t,o);let l=!1,y=!1,p;const f=r(()=>{l||y||(y=!0,n())},"turn"),_=r(()=>{try{return fs.mkdirSync(t,{recursive:!0}),fs.writeFileSync(a,JSON.stringify({pid:s,label:i,at:(e.now??Date.now)()})),!0}catch{return!1}},"write"),k=r(d=>{p=setTimeout(M,d),p.unref?.()},"later");function M(){if(l||y)return;const d=u().indexOf(o);if(d===-1)return _()?k(m):f();if(d<h)return f();k(S)}return c(M,"k"),r(M,"look"),_()?(k(m),{release(){if(!l){l=!0,p&&clearTimeout(p);try{fs.unlinkSync(a)}catch{}}}}):(f(),{release(){l=!0}})}}c(machineTypecheckQueue,"machineTypecheckQueue"),r(machineTypecheckQueue,"machineTypecheckQueue");const immediateQueue=r((e,t)=>(t(),{release(){}}),"immediateQueue");exports.immediateQueue=immediateQueue;
|