@dimina-kit/devkit 0.1.2-dev.20260703101348 → 0.1.2
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/README.md +16 -0
- package/dist/compile-worker-adopt.test.d.ts +2 -0
- package/dist/compile-worker-adopt.test.d.ts.map +1 -0
- package/dist/compile-worker-adopt.test.js +187 -0
- package/dist/compile-worker-entry-warmpool.test.d.ts +2 -0
- package/dist/compile-worker-entry-warmpool.test.d.ts.map +1 -0
- package/dist/compile-worker-entry-warmpool.test.js +110 -0
- package/dist/compile-worker-entry.d.ts +35 -5
- package/dist/compile-worker-entry.d.ts.map +1 -1
- package/dist/compile-worker-entry.js +45 -1
- package/dist/compile-worker-standby-hardening.test.d.ts +2 -0
- package/dist/compile-worker-standby-hardening.test.d.ts.map +1 -0
- package/dist/compile-worker-standby-hardening.test.js +212 -0
- package/dist/compile-worker-standby-integration.test.d.ts +2 -0
- package/dist/compile-worker-standby-integration.test.d.ts.map +1 -0
- package/dist/compile-worker-standby-integration.test.js +243 -0
- package/dist/compile-worker-standby.d.ts +60 -0
- package/dist/compile-worker-standby.d.ts.map +1 -0
- package/dist/compile-worker-standby.js +283 -0
- package/dist/compile-worker-standby.test.d.ts +2 -0
- package/dist/compile-worker-standby.test.d.ts.map +1 -0
- package/dist/compile-worker-standby.test.js +356 -0
- package/dist/compile-worker.d.ts +53 -0
- package/dist/compile-worker.d.ts.map +1 -1
- package/dist/compile-worker.js +30 -11
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -4
- package/fe/dimina-fe-container/assets/index.js +8 -8
- package/fe/dimina-fe-container/assets/pageFrame.js +3 -3
- package/fe/dimina-fe-container/assets/service.js +2 -2
- package/fe/dimina-fe-container/dimina-version.json +2 -2
- package/package.json +3 -3
package/dist/compile-worker.d.ts
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent-side orchestration of the forked compile worker.
|
|
3
|
+
*
|
|
4
|
+
* One long-lived worker per `openProject` session: the first compile and
|
|
5
|
+
* every watcher rebuild are `{ cmd: 'build' }` IPC messages to the SAME
|
|
6
|
+
* child, so the compiler's module/worker caches stay warm. The parent never
|
|
7
|
+
* calls `process.chdir` — the worker chdirs in its own process (the root
|
|
8
|
+
* motive of this architecture).
|
|
9
|
+
*
|
|
10
|
+
* Crash handling: an unexpected worker death ('exit', 'close' or a lone
|
|
11
|
+
* 'error' event — Node does NOT guarantee an 'exit' after 'error') rejects
|
|
12
|
+
* the in-flight build (so the rebuild scheduler settles instead of hanging)
|
|
13
|
+
* and the NEXT build lazily re-forks a fresh worker. The death handler is
|
|
14
|
+
* idempotent and generation-guarded: a dead child's late 'exit' can never
|
|
15
|
+
* settle a build that belongs to a fresh worker. `close()` kills, rejects
|
|
16
|
+
* the in-flight build itself, and resolves on the child's first death event
|
|
17
|
+
* ('exit'/'close', or a lone 'error') — it never re-forks
|
|
18
|
+
* (refill-on-graceful-close would wedge process teardown).
|
|
19
|
+
*/
|
|
20
|
+
import { type ChildProcess } from 'node:child_process';
|
|
1
21
|
import type { WorkerAppInfo, WorkerBuildOptions } from './compile-worker-entry.js';
|
|
2
22
|
export interface CompileLogEntry {
|
|
3
23
|
stream: 'stdout' | 'stderr';
|
|
@@ -6,6 +26,16 @@ export interface CompileLogEntry {
|
|
|
6
26
|
export interface CompileWorkerOptions {
|
|
7
27
|
/** Filtered dmcc log lines (already through `filterDmccLogLine`). */
|
|
8
28
|
onLog?: (entry: CompileLogEntry) => void;
|
|
29
|
+
/**
|
|
30
|
+
* An already-forked compile-worker-entry process to reuse for the first
|
|
31
|
+
* build (a warm-standby hand-off) instead of paying a fresh fork + compiler
|
|
32
|
+
* import. Must be an IPC fork with piped stdio — the same shape spawnWorker
|
|
33
|
+
* produces. Pure accelerator: an adoptee that is already dead (or dies
|
|
34
|
+
* before the first build) is simply skipped and the next build forks fresh.
|
|
35
|
+
* Once adopted, the process is owned by this worker exactly like a
|
|
36
|
+
* self-forked one — close() kills it, its death rejects in-flight builds.
|
|
37
|
+
*/
|
|
38
|
+
adopt?: ChildProcess;
|
|
9
39
|
}
|
|
10
40
|
export interface BuildRequest {
|
|
11
41
|
projectPath: string;
|
|
@@ -29,5 +59,28 @@ export interface CompileWorker {
|
|
|
29
59
|
*/
|
|
30
60
|
close: () => Promise<void>;
|
|
31
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the fork target. In the published build this is the compiled
|
|
64
|
+
* `dist/compile-worker-entry.js` sibling; under vitest/dev the module runs
|
|
65
|
+
* from `src/`, where only the `.ts` source exists — the entry is a
|
|
66
|
+
* zero-dependency, erasable-syntax-only shell by design, so Node can run it
|
|
67
|
+
* with type stripping (default since 22.18; behind `--experimental-strip-types`
|
|
68
|
+
* on 22.6–22.17, which `workerExecArgv` passes for a `.ts` entry).
|
|
69
|
+
*
|
|
70
|
+
* Exported (with `workerExecArgv`) as the single authority on the fork shape:
|
|
71
|
+
* the warm-standby manager forks the SAME entry with the SAME execArgv, so a
|
|
72
|
+
* spare is indistinguishable from a worker this module forks itself.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveWorkerEntry(): string;
|
|
75
|
+
/**
|
|
76
|
+
* execArgv for the fork. A compiled `.js` entry (the published build, engines
|
|
77
|
+
* `node >=20`) needs nothing. A `.ts` entry (vitest/dev only) needs Node type
|
|
78
|
+
* stripping: `--experimental-strip-types` exists since 22.6 — on older Node
|
|
79
|
+
* the child would die at startup with an opaque ERR_UNKNOWN_FILE_EXTENSION /
|
|
80
|
+
* bad-option crash, so fail HERE with an actionable message instead. The
|
|
81
|
+
* ExperimentalWarning is silenced because the child's stderr is the dmcc log
|
|
82
|
+
* transport. On ≥22.18 (stripping on by default) the flag is still accepted.
|
|
83
|
+
*/
|
|
84
|
+
export declare function workerExecArgv(entry: string): string[];
|
|
32
85
|
export declare function createCompileWorker(opts?: CompileWorkerOptions): CompileWorker;
|
|
33
86
|
//# sourceMappingURL=compile-worker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-worker.d.ts","sourceRoot":"","sources":["../src/compile-worker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compile-worker.d.ts","sourceRoot":"","sources":["../src/compile-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAQ,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAM5D,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAuB,MAAM,2BAA2B,CAAA;AAIvG,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,oBAAoB;IACpC,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,kBAAkB,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC7B;;;;;OAKG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IAC3D;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAWtD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CA8LlF"}
|
package/dist/compile-worker.js
CHANGED
|
@@ -30,8 +30,12 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
30
30
|
* zero-dependency, erasable-syntax-only shell by design, so Node can run it
|
|
31
31
|
* with type stripping (default since 22.18; behind `--experimental-strip-types`
|
|
32
32
|
* on 22.6–22.17, which `workerExecArgv` passes for a `.ts` entry).
|
|
33
|
+
*
|
|
34
|
+
* Exported (with `workerExecArgv`) as the single authority on the fork shape:
|
|
35
|
+
* the warm-standby manager forks the SAME entry with the SAME execArgv, so a
|
|
36
|
+
* spare is indistinguishable from a worker this module forks itself.
|
|
33
37
|
*/
|
|
34
|
-
function resolveWorkerEntry() {
|
|
38
|
+
export function resolveWorkerEntry() {
|
|
35
39
|
const js = path.join(__dirname, 'compile-worker-entry.js');
|
|
36
40
|
if (fs.existsSync(js))
|
|
37
41
|
return js;
|
|
@@ -46,7 +50,7 @@ function resolveWorkerEntry() {
|
|
|
46
50
|
* ExperimentalWarning is silenced because the child's stderr is the dmcc log
|
|
47
51
|
* transport. On ≥22.18 (stripping on by default) the flag is still accepted.
|
|
48
52
|
*/
|
|
49
|
-
function workerExecArgv(entry) {
|
|
53
|
+
export function workerExecArgv(entry) {
|
|
50
54
|
if (!entry.endsWith('.ts'))
|
|
51
55
|
return [];
|
|
52
56
|
const [major = 0, minor = 0] = process.versions.node.split('.').map(Number);
|
|
@@ -112,15 +116,10 @@ export function createCompileWorker(opts = {}) {
|
|
|
112
116
|
stream.on('end', flush);
|
|
113
117
|
stream.on('close', flush);
|
|
114
118
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
silent: true,
|
|
120
|
-
// Explicit execArgv, NOT the parent's (vitest/electron loaders would
|
|
121
|
-
// leak into a plain Node child).
|
|
122
|
-
execArgv: workerExecArgv(entry),
|
|
123
|
-
});
|
|
119
|
+
// Attach the log pipes, result routing and death handling to a worker
|
|
120
|
+
// process — the SAME wiring whether the process was self-forked or adopted
|
|
121
|
+
// from a warm standby (an inline copy for one of the two would drift).
|
|
122
|
+
function wireWorker(worker) {
|
|
124
123
|
attachLineReader(worker.stdout, 'stdout');
|
|
125
124
|
attachLineReader(worker.stderr, 'stderr');
|
|
126
125
|
// Shared, idempotent death handler for 'exit' / 'close' / 'error': the
|
|
@@ -177,6 +176,26 @@ export function createCompileWorker(opts = {}) {
|
|
|
177
176
|
worker.on('error', (err) => settleDeath(`compile worker errored: ${err.message}`));
|
|
178
177
|
return worker;
|
|
179
178
|
}
|
|
179
|
+
function spawnWorker() {
|
|
180
|
+
const entry = resolveWorkerEntry();
|
|
181
|
+
return wireWorker(fork(entry, [], {
|
|
182
|
+
// Pipe stdout/stderr back to the parent — the dmcc log transport.
|
|
183
|
+
silent: true,
|
|
184
|
+
// Explicit execArgv, NOT the parent's (vitest/electron loaders would
|
|
185
|
+
// leak into a plain Node child).
|
|
186
|
+
execArgv: workerExecArgv(entry),
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
// Adopt the warm-standby hand-off, if any, at creation time — wiring
|
|
190
|
+
// immediately (not at the first build) so a spare that dies in the gap is
|
|
191
|
+
// handled by the normal death path (child cleared → next build re-forks)
|
|
192
|
+
// instead of being discovered as a broken pipe mid-build. An adoptee that
|
|
193
|
+
// is ALREADY dead is skipped outright: its death events fired before any
|
|
194
|
+
// listener could attach, so wiring it would leave a permanently wedged
|
|
195
|
+
// child slot.
|
|
196
|
+
if (opts.adopt && opts.adopt.exitCode === null && opts.adopt.signalCode === null && opts.adopt.connected) {
|
|
197
|
+
child = wireWorker(opts.adopt);
|
|
198
|
+
}
|
|
180
199
|
function runBuild(req) {
|
|
181
200
|
if (closed) {
|
|
182
201
|
return Promise.reject(new Error('compile worker is closed'));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import type { CompileLogEntry } from './compile-worker.js';
|
|
2
|
+
import type { CompileWorkerStandby, CompileWorkerStandbyOptions } from './compile-worker-standby.js';
|
|
2
3
|
export { createRebuildScheduler } from './rebuild-scheduler.js';
|
|
3
4
|
export type { RebuildScheduler } from './rebuild-scheduler.js';
|
|
4
5
|
export { filterDmccLogLine } from './compile-log.js';
|
|
5
6
|
export { createCompileWorker } from './compile-worker.js';
|
|
6
7
|
export type { CompileLogEntry, CompileWorker } from './compile-worker.js';
|
|
8
|
+
export { createCompileWorkerStandby } from './compile-worker-standby.js';
|
|
9
|
+
export type { CompileWorkerStandby, CompileWorkerStandbyOptions, StandbyEvent, StandbyState, } from './compile-worker-standby.js';
|
|
10
|
+
/**
|
|
11
|
+
* Turn on the warm-standby accelerator and start warming the first spare
|
|
12
|
+
* immediately. Idempotent while the returned manager is live — repeat calls
|
|
13
|
+
* return the SAME manager (their opts are ignored). After `dispose()` (host
|
|
14
|
+
* shutdown, or a test tearing down) a new call builds a fresh manager. The
|
|
15
|
+
* caller owns disposal; a disposed manager makes every openProject fall back
|
|
16
|
+
* to cold forks and never spawns again.
|
|
17
|
+
*/
|
|
18
|
+
export declare function enableCompileWorkerStandby(opts?: CompileWorkerStandbyOptions): CompileWorkerStandby;
|
|
7
19
|
export interface AppInfo {
|
|
8
20
|
appId: string;
|
|
9
21
|
name: string;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAgB,eAAe,EAAiB,MAAM,qBAAqB,CAAA;AAEvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAgB,eAAe,EAAiB,MAAM,qBAAqB,CAAA;AAEvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AAEpG,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AACxE,YAAY,EACX,oBAAoB,EACpB,2BAA2B,EAC3B,YAAY,EACZ,YAAY,GACZ,MAAM,6BAA6B,CAAA;AAYpC;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACzC,IAAI,CAAC,EAAE,2BAA2B,GAChC,oBAAoB,CAKtB;AAqDD,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAuFD,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoJnF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACpC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GACrC;IAAE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CA2DtD"}
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,46 @@ import { createRequire } from 'node:module';
|
|
|
8
8
|
import chokidar from 'chokidar';
|
|
9
9
|
import { createRebuildScheduler } from './rebuild-scheduler.js';
|
|
10
10
|
import { createCompileWorker } from './compile-worker.js';
|
|
11
|
+
import { createCompileWorkerStandby } from './compile-worker-standby.js';
|
|
11
12
|
export { createRebuildScheduler } from './rebuild-scheduler.js';
|
|
12
13
|
export { filterDmccLogLine } from './compile-log.js';
|
|
13
14
|
export { createCompileWorker } from './compile-worker.js';
|
|
15
|
+
export { createCompileWorkerStandby } from './compile-worker-standby.js';
|
|
16
|
+
// ── warm standby (opt-in) ──────────────────────────────────────────────────
|
|
17
|
+
//
|
|
18
|
+
// One module-level spare compile worker shared by every `openProject` in this
|
|
19
|
+
// process. While no project is open the spare sits forked + prewarmed
|
|
20
|
+
// (project-agnostic — it never chdirs, see compile-worker-entry); openProject
|
|
21
|
+
// adopts it for the first compile and each session close refills it. Purely
|
|
22
|
+
// additive: hosts that never call `enableCompileWorkerStandby` get exactly the
|
|
23
|
+
// cold-fork behavior they had before.
|
|
24
|
+
let standbyManager = null;
|
|
25
|
+
/**
|
|
26
|
+
* Turn on the warm-standby accelerator and start warming the first spare
|
|
27
|
+
* immediately. Idempotent while the returned manager is live — repeat calls
|
|
28
|
+
* return the SAME manager (their opts are ignored). After `dispose()` (host
|
|
29
|
+
* shutdown, or a test tearing down) a new call builds a fresh manager. The
|
|
30
|
+
* caller owns disposal; a disposed manager makes every openProject fall back
|
|
31
|
+
* to cold forks and never spawns again.
|
|
32
|
+
*/
|
|
33
|
+
export function enableCompileWorkerStandby(opts) {
|
|
34
|
+
if (standbyManager && standbyManager.state !== 'disposed')
|
|
35
|
+
return standbyManager;
|
|
36
|
+
standbyManager = createCompileWorkerStandby(opts);
|
|
37
|
+
standbyManager.ensure();
|
|
38
|
+
return standbyManager;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Refill the spare after a compile worker has been consumed and fully closed.
|
|
42
|
+
* Fire-and-forget BY DESIGN: the fork happens asynchronously inside the
|
|
43
|
+
* manager, so a session close never waits on (and can never be wedged by)
|
|
44
|
+
* standby work — the refill-on-graceful-close deadlock class stays
|
|
45
|
+
* structurally impossible. No-ops when the standby is off, already warming,
|
|
46
|
+
* or disposed.
|
|
47
|
+
*/
|
|
48
|
+
function refillStandby() {
|
|
49
|
+
standbyManager?.ensure();
|
|
50
|
+
}
|
|
14
51
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
52
|
const require = createRequire(import.meta.url);
|
|
16
53
|
// esbuild's native binary is shipped inside node_modules, but when packaged
|
|
@@ -125,8 +162,12 @@ export async function openProject(opts) {
|
|
|
125
162
|
// Compilation runs in a long-lived forked worker — the worker chdirs in
|
|
126
163
|
// its OWN process, so this (host) process never mutates its cwd, and dmcc
|
|
127
164
|
// terminal output arrives on the worker's piped streams (delivered to
|
|
128
|
-
// `onLog` line-by-line after `filterDmccLogLine`).
|
|
129
|
-
|
|
165
|
+
// `onLog` line-by-line after `filterDmccLogLine`). When the warm standby is
|
|
166
|
+
// enabled and holds a healthy spare, adopt it: the first compile then runs
|
|
167
|
+
// on an already-forked, toolchain-warm process. adopt() degrades to null on
|
|
168
|
+
// every failure path, which lands in the normal cold-fork behavior.
|
|
169
|
+
const adopted = standbyManager ? await standbyManager.adopt() : null;
|
|
170
|
+
const compileWorker = createCompileWorker({ onLog, ...(adopted ? { adopt: adopted } : {}) });
|
|
130
171
|
const buildRequest = {
|
|
131
172
|
projectPath,
|
|
132
173
|
outputDir: resolvedOutputDir,
|
|
@@ -138,6 +179,9 @@ export async function openProject(opts) {
|
|
|
138
179
|
}
|
|
139
180
|
catch (err) {
|
|
140
181
|
await compileWorker.close();
|
|
182
|
+
// A failed open consumed the spare too — refill (fire-and-forget) so the
|
|
183
|
+
// NEXT open attempt still starts warm.
|
|
184
|
+
refillStandby();
|
|
141
185
|
throw err;
|
|
142
186
|
}
|
|
143
187
|
// When the compiler is racing (e.g. opening a project that was just
|
|
@@ -207,6 +251,7 @@ export async function openProject(opts) {
|
|
|
207
251
|
}
|
|
208
252
|
catch (err) {
|
|
209
253
|
await cleanupFailedOpen(watcher, compileWorker, server);
|
|
254
|
+
refillStandby();
|
|
210
255
|
throw err;
|
|
211
256
|
}
|
|
212
257
|
// `server` is always assigned when the try block completes without throwing
|
|
@@ -220,9 +265,13 @@ export async function openProject(opts) {
|
|
|
220
265
|
close: async () => {
|
|
221
266
|
await watcher?.close();
|
|
222
267
|
// Kill the long-lived compile worker and WAIT for the child to be
|
|
223
|
-
// actually dead —
|
|
224
|
-
// refill here would wedge process teardown).
|
|
268
|
+
// actually dead — THIS worker is never re-forked on a graceful close
|
|
269
|
+
// (an awaited refill here would wedge process teardown).
|
|
225
270
|
await compileWorker.close();
|
|
271
|
+
// The standby refill is different: fire-and-forget into the manager
|
|
272
|
+
// (nothing here waits on it), sequenced after the old worker's death
|
|
273
|
+
// so spare + dying worker never hold memory simultaneously.
|
|
274
|
+
refillStandby();
|
|
226
275
|
liveServer.closeAllConnections?.();
|
|
227
276
|
await new Promise(resolve => liveServer.close(() => resolve()));
|
|
228
277
|
},
|