@lark-apaas/coding-template-nestjs-react-fullstack 0.1.27-alpha.20260824122815 → 0.1.27

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.
@@ -1,865 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import net from 'node:net';
6
- import http from 'node:http';
7
- import { randomUUID } from 'node:crypto';
8
- import { spawn } from 'node:child_process';
9
- import { fileURLToPath } from 'node:url';
10
- import {
11
- validateCacheGeneration,
12
- validateCacheGenerationReceipt,
13
- writeCacheGenerationValidationReceipt,
14
- } from './cache-generation-preflight.mjs';
15
-
16
- const projectRoot = path.resolve(
17
- process.env.MIAODA_WORKSPACE_ROOT || process.cwd()
18
- );
19
- const defaultCachePointer = path.join(projectRoot, '.miaoda-cache', 'current');
20
- const requestedCacheRoot = path.resolve(
21
- process.env.MIAODA_CACHE_ROOT || defaultCachePointer
22
- );
23
- const cacheRoot = fs.realpathSync(requestedCacheRoot);
24
- if (!process.env.MIAODA_CACHE_ROOT) {
25
- const generationStore = fs.realpathSync(
26
- path.join(projectRoot, '.miaoda-cache', 'generations')
27
- );
28
- const generationRelative = path.relative(generationStore, cacheRoot);
29
- if (!/^[a-f0-9]{64}$/.test(generationRelative)) {
30
- throw new Error('MIAODA_CACHE_GENERATION_POINTER_REJECTED');
31
- }
32
- }
33
- const runtimeManifestFile = path.resolve(
34
- process.env.MIAODA_PLATFORM_RUNTIME_MANIFEST ||
35
- '/opt/miaoda/preview-runtime/runtime-manifest.json'
36
- );
37
- const validateOnly = process.argv.includes('--validate-only');
38
- const artifactScopeOptionIndex = process.argv.indexOf('--artifact-scope');
39
- const validationArtifactScope =
40
- artifactScopeOptionIndex >= 0
41
- ? process.argv[artifactScopeOptionIndex + 1]
42
- : 'all';
43
- const skipWorkspaceInputs = process.argv.includes('--skip-workspace-inputs');
44
- const servingReadyMarker = '/tmp/event/MIAODA_NEST_CACHE_SERVING_READY';
45
- const skipConfigEnvironment = process.argv.includes(
46
- '--skip-config-environment'
47
- );
48
- const runtimeStartedAt = process.hrtime.bigint();
49
- const durationMs = startedAt =>
50
- Number(process.hrtime.bigint() - startedAt) / 1_000_000;
51
- function emitRuntimePhase(phase, startedAt, details = {}) {
52
- if (validateOnly) return;
53
- process.stdout.write(
54
- `${JSON.stringify({
55
- event: 'miaoda_cache_runtime_phase',
56
- runtime: 'nest',
57
- phase,
58
- durationMs: Number(durationMs(startedAt).toFixed(3)),
59
- ...details,
60
- })}\n`
61
- );
62
- }
63
- function publishServingReadyMarker(file) {
64
- fs.mkdirSync(path.dirname(file), { recursive: true });
65
- const temporaryFile = `${file}.${process.pid}.tmp`;
66
- fs.writeFileSync(
67
- temporaryFile,
68
- `${JSON.stringify({ schemaVersion: 1, readyAt: new Date().toISOString() })}\n`,
69
- { mode: 0o600 }
70
- );
71
- fs.renameSync(temporaryFile, file);
72
- }
73
- if (!validateOnly) {
74
- fs.rmSync(servingReadyMarker, { force: true });
75
- process.once('exit', () => {
76
- try {
77
- fs.rmSync(servingReadyMarker, { force: true });
78
- } catch {}
79
- });
80
- }
81
- if (skipConfigEnvironment && !validateOnly) {
82
- process.stderr.write(
83
- 'MIAODA_CACHE_CONFIG_ENVIRONMENT_SKIP_REQUIRES_VALIDATE_ONLY\n'
84
- );
85
- process.exit(2);
86
- }
87
- if (
88
- (!validateOnly && artifactScopeOptionIndex >= 0) ||
89
- (!validateOnly && skipWorkspaceInputs) ||
90
- !['all', 'server', 'vite'].includes(validationArtifactScope) ||
91
- (skipWorkspaceInputs && validationArtifactScope !== 'vite')
92
- ) {
93
- process.stderr.write('MIAODA_CACHE_VALIDATION_OPTIONS_INVALID\n');
94
- process.exit(2);
95
- }
96
-
97
- let validated;
98
- let validatedBy = 'full-sha';
99
- let validationReceiptMiss;
100
- const validationStartedAt = process.hrtime.bigint();
101
- try {
102
- const validationReceiptFile =
103
- process.env.MIAODA_CACHE_VALIDATION_RECEIPT_FILE;
104
- if (validationReceiptFile && !skipWorkspaceInputs) {
105
- try {
106
- validated = validateCacheGenerationReceipt({
107
- projectRoot,
108
- cacheRoot,
109
- runtimeManifestFile,
110
- receiptFile: validationReceiptFile,
111
- validateConfigEnvironment: !skipConfigEnvironment,
112
- artifactScope: validateOnly ? validationArtifactScope : 'server',
113
- });
114
- validatedBy = 'platform-receipt';
115
- } catch (error) {
116
- validationReceiptMiss = String(
117
- error instanceof Error ? error.message : error
118
- ).split(':')[0];
119
- // A missing/stale receipt is only a fast-path miss. Preserve the legacy
120
- // full preflight and dependency-backed fallback semantics below.
121
- }
122
- }
123
- if (!validated) {
124
- validated = validateCacheGeneration({
125
- projectRoot,
126
- cacheRoot,
127
- runtimeManifestFile,
128
- validateConfigEnvironment: !skipConfigEnvironment,
129
- artifactScope: validateOnly ? validationArtifactScope : 'server',
130
- validateWorkspaceInputs: !skipWorkspaceInputs,
131
- });
132
- }
133
- if (
134
- !process.env.MIAODA_CACHE_ROOT &&
135
- path.basename(cacheRoot) !== validated.generation.generationId
136
- ) {
137
- throw new Error('MIAODA_CACHE_GENERATION_POINTER_ID_MISMATCH');
138
- }
139
- if (validateOnly) {
140
- if (
141
- validationReceiptFile &&
142
- validatedBy === 'full-sha' &&
143
- !skipWorkspaceInputs
144
- ) {
145
- writeCacheGenerationValidationReceipt({
146
- validated,
147
- receiptFile: validationReceiptFile,
148
- });
149
- }
150
- process.stdout.write(
151
- `${JSON.stringify({
152
- valid: true,
153
- generationId: validated.generation.generationId,
154
- artifactScope: validationArtifactScope,
155
- workspaceInputsValidated: !skipWorkspaceInputs,
156
- validatedBy,
157
- receiptWritten: Boolean(
158
- validationReceiptFile &&
159
- validatedBy === 'full-sha' &&
160
- !skipWorkspaceInputs
161
- ),
162
- })}\n`
163
- );
164
- process.exit(0);
165
- }
166
- } catch (error) {
167
- process.stderr.write(`${error instanceof Error ? error.message : error}\n`);
168
- process.exit(2);
169
- }
170
- emitRuntimePhase('generation_validation', validationStartedAt, {
171
- artifactScope: 'server',
172
- validatedBy,
173
- ...(validationReceiptMiss ? { validationReceiptMiss } : {}),
174
- });
175
-
176
- const publicPort = Number(process.env.SERVER_PORT || 3000);
177
- const publicHost = process.env.SERVER_HOST || '0.0.0.0';
178
- const dependencyReadyFile = path.resolve(
179
- process.env.MIAODA_DEPENDENCY_READY_FILE ||
180
- path.join(projectRoot, '.miaoda-runtime', 'dependencies-ready.json')
181
- );
182
- const actionPluginReadyFile = path.resolve(
183
- process.env.MIAODA_ACTION_PLUGIN_READY_FILE ||
184
- '/tmp/event/MIAODA_ACTION_PLUGIN_READY.json'
185
- );
186
- const expectedRestoreEpochMs =
187
- process.env.MIAODA_PLATFORM_ENV_NOT_BEFORE_MS || '';
188
- const skipActionPluginInit =
189
- process.env.MIAODA_NEST_SKIP_ACTION_PLUGIN_INIT === 'true';
190
- const sourceHandoffMode =
191
- process.env.MIAODA_NEST_SOURCE_HANDOFF_MODE || 'immediate';
192
- if (!['immediate', 'on-source-change'].includes(sourceHandoffMode)) {
193
- throw new Error(
194
- `MIAODA_NEST_SOURCE_HANDOFF_MODE_INVALID: ${sourceHandoffMode}`
195
- );
196
- }
197
- function positiveDuration(name, fallback) {
198
- const value = Number(process.env[name] || fallback);
199
- return Number.isFinite(value) && value > 0 ? value : fallback;
200
- }
201
- const sourceReadyTimeoutMs = positiveDuration(
202
- 'MIAODA_NEST_SOURCE_READY_TIMEOUT_MS',
203
- 60_000
204
- );
205
- const cacheDrainTimeoutMs = positiveDuration(
206
- 'MIAODA_NEST_CACHE_DRAIN_TIMEOUT_MS',
207
- 5_000
208
- );
209
- const serviceId = randomUUID();
210
- let stopping = false;
211
- let sourceHandoffWatcher;
212
- let sourceHandoffTimer;
213
-
214
- function resolveProbeHost(host) {
215
- const normalized = String(host || '').trim();
216
- const lower = normalized.toLowerCase();
217
- if (!normalized || lower === '0.0.0.0') return '127.0.0.1';
218
- if (lower === '::' || lower === '[::]') return '::1';
219
- if (normalized.startsWith('[') && normalized.endsWith(']')) {
220
- return normalized.slice(1, -1);
221
- }
222
- return normalized;
223
- }
224
-
225
- function waitForPort(host, port, timeoutMs = 60_000) {
226
- const deadline = Date.now() + timeoutMs;
227
- const probeHost = resolveProbeHost(host);
228
- return new Promise((resolve, reject) => {
229
- const attempt = () => {
230
- if (stopping) return reject(new Error('MIAODA_SERVER_RUNTIME_STOPPING'));
231
- const socket = net.createConnection({ host: probeHost, port });
232
- const failed = () => {
233
- socket.destroy();
234
- if (Date.now() >= deadline) {
235
- reject(new Error(`MIAODA_SERVER_UPSTREAM_TIMEOUT: ${port}`));
236
- } else {
237
- setTimeout(attempt, 100);
238
- }
239
- };
240
- socket.once('connect', () => {
241
- socket.destroy();
242
- resolve();
243
- });
244
- socket.once('error', failed);
245
- };
246
- attempt();
247
- });
248
- }
249
-
250
- function reservePort(host) {
251
- return new Promise((resolve, reject) => {
252
- const server = net.createServer();
253
- server.once('error', reject);
254
- server.listen(0, host, () => {
255
- const address = server.address();
256
- if (!address || typeof address === 'string') {
257
- server.close(() =>
258
- reject(new Error('MIAODA_SERVER_UPSTREAM_PORT_RESERVATION_FAILED'))
259
- );
260
- return;
261
- }
262
- server.close(error => (error ? reject(error) : resolve(address.port)));
263
- });
264
- });
265
- }
266
-
267
- function delay(ms) {
268
- return new Promise(resolve => setTimeout(resolve, ms));
269
- }
270
-
271
- function childIsRunning(child) {
272
- return Boolean(
273
- child?.pid && child.exitCode === null && child.signalCode === null
274
- );
275
- }
276
-
277
- function terminate(child) {
278
- if (!child?.pid) return;
279
- try {
280
- process.kill(-child.pid, 'SIGTERM');
281
- } catch {
282
- try {
283
- child.kill('SIGTERM');
284
- } catch {}
285
- }
286
- }
287
-
288
- function waitForChildExit(child, timeoutMs) {
289
- if (!childIsRunning(child)) return Promise.resolve();
290
- return Promise.race([
291
- new Promise(resolve => child.once('exit', resolve)),
292
- delay(timeoutMs),
293
- ]);
294
- }
295
-
296
- function pruneSupersededGenerations() {
297
- if (process.env.MIAODA_CACHE_ROOT) return;
298
- const pruneScript = fileURLToPath(
299
- new URL('./cache-generation-prune.mjs', import.meta.url)
300
- );
301
- if (!fs.existsSync(pruneScript)) {
302
- process.stderr.write(
303
- `${JSON.stringify({ event: 'miaoda_cache_generation_prune_failed', error: 'MIAODA_CACHE_GENERATION_PRUNE_SCRIPT_MISSING' })}\n`
304
- );
305
- return;
306
- }
307
- const pruneChild = spawn(process.execPath, [pruneScript, '--wait-ms=60000'], {
308
- cwd: projectRoot,
309
- stdio: 'inherit',
310
- env: { ...process.env, MIAODA_WORKSPACE_ROOT: projectRoot },
311
- });
312
- pruneChild.once('error', error => {
313
- process.stderr.write(
314
- `${JSON.stringify({ event: 'miaoda_cache_generation_prune_failed', error: error.message })}\n`
315
- );
316
- });
317
- pruneChild.once('exit', (code, signal) => {
318
- if (code === 0) return;
319
- process.stderr.write(
320
- `${JSON.stringify({
321
- event: 'miaoda_cache_generation_prune_failed',
322
- error: signal ? `signal-${signal}` : `exit-${code}`,
323
- })}\n`
324
- );
325
- });
326
- }
327
-
328
- const internalHost = resolveProbeHost(publicHost);
329
- const cacheUpstreamPort = await reservePort(internalHost);
330
- let sourceUpstreamPort = await reservePort(internalHost);
331
- while (sourceUpstreamPort === cacheUpstreamPort) {
332
- sourceUpstreamPort = await reservePort(internalHost);
333
- }
334
- const cacheUpstream = {
335
- name: 'cache',
336
- host: internalHost,
337
- port: cacheUpstreamPort,
338
- inflight: 0,
339
- };
340
- const sourceUpstream = {
341
- name: 'source',
342
- host: internalHost,
343
- port: sourceUpstreamPort,
344
- inflight: 0,
345
- };
346
- let activeUpstream = cacheUpstream;
347
- let cacheRetired = false;
348
- let sourceChild;
349
- let actionPluginInitChild;
350
- let actionPluginsInitialized = false;
351
-
352
- function retain(upstream) {
353
- upstream.inflight += 1;
354
- let released = false;
355
- return () => {
356
- if (released) return;
357
- released = true;
358
- upstream.inflight = Math.max(0, upstream.inflight - 1);
359
- };
360
- }
361
-
362
- const proxy = http.createServer((request, response) => {
363
- const upstream = activeUpstream;
364
- const release = retain(upstream);
365
- const upstreamRequest = http.request(
366
- {
367
- hostname: upstream.host,
368
- port: upstream.port,
369
- method: request.method,
370
- path: request.url,
371
- headers: request.headers,
372
- },
373
- upstreamResponse => {
374
- if (upstreamResponse.statusMessage) {
375
- response.writeHead(
376
- upstreamResponse.statusCode || 502,
377
- upstreamResponse.statusMessage,
378
- upstreamResponse.headers
379
- );
380
- } else {
381
- response.writeHead(
382
- upstreamResponse.statusCode || 502,
383
- upstreamResponse.headers
384
- );
385
- }
386
- upstreamResponse.pipe(response);
387
- }
388
- );
389
- upstreamRequest.once('error', error => {
390
- if (!response.headersSent) {
391
- response.writeHead(502, { 'content-type': 'application/json' });
392
- response.end(
393
- `${JSON.stringify({
394
- error: 'MIAODA_NEST_UPSTREAM_UNAVAILABLE',
395
- upstream: upstream.name,
396
- })}\n`
397
- );
398
- } else {
399
- response.destroy(error);
400
- }
401
- });
402
- request.once('aborted', () => upstreamRequest.destroy());
403
- response.once('finish', release);
404
- response.once('close', release);
405
- request.pipe(upstreamRequest);
406
- });
407
-
408
- proxy.on('upgrade', (request, clientSocket, head) => {
409
- const upstream = activeUpstream;
410
- const release = retain(upstream);
411
- const upstreamSocket = net.createConnection({
412
- host: upstream.host,
413
- port: upstream.port,
414
- });
415
- const close = () => {
416
- release();
417
- clientSocket.destroy();
418
- upstreamSocket.destroy();
419
- };
420
- upstreamSocket.once('connect', () => {
421
- const requestLine = `${request.method} ${request.url} HTTP/${request.httpVersion}\r\n`;
422
- const headers = [];
423
- for (let index = 0; index < request.rawHeaders.length; index += 2) {
424
- headers.push(
425
- `${request.rawHeaders[index]}: ${request.rawHeaders[index + 1]}`
426
- );
427
- }
428
- upstreamSocket.write(`${requestLine}${headers.join('\r\n')}\r\n\r\n`);
429
- if (head.length > 0) upstreamSocket.write(head);
430
- clientSocket.pipe(upstreamSocket).pipe(clientSocket);
431
- });
432
- upstreamSocket.once('error', close);
433
- clientSocket.once('error', close);
434
- upstreamSocket.once('close', release);
435
- clientSocket.once('close', release);
436
- });
437
-
438
- proxy.on('clientError', (_error, socket) => {
439
- if (socket.writable) socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
440
- });
441
-
442
- function listen(server, host, port) {
443
- return new Promise((resolve, reject) => {
444
- server.once('error', reject);
445
- server.listen(port, host, resolve);
446
- });
447
- }
448
-
449
- const bundleSpawnStartedAt = process.hrtime.bigint();
450
- const cacheChild = spawn(process.execPath, [validated.artifacts.serverBundle], {
451
- cwd: projectRoot,
452
- detached: true,
453
- stdio: 'inherit',
454
- env: {
455
- ...process.env,
456
- NODE_PATH: '',
457
- SERVER_HOST: cacheUpstream.host,
458
- SERVER_PORT: String(cacheUpstream.port),
459
- },
460
- });
461
- emitRuntimePhase('bundle_spawn', bundleSpawnStartedAt, {
462
- childPid: cacheChild.pid,
463
- upstreamPort: cacheUpstream.port,
464
- });
465
- cacheChild.once('exit', (code, signal) => {
466
- if (stopping || cacheRetired) return;
467
- process.stderr.write(
468
- `${JSON.stringify({ event: 'miaoda_nest_cache_runtime_exited', code, signal })}\n`
469
- );
470
- if (activeUpstream === cacheUpstream) process.exit(code || 1);
471
- });
472
-
473
- let rejectedDependencyReceiptSignature = '';
474
- function dependenciesAreReady() {
475
- try {
476
- const marker = JSON.parse(fs.readFileSync(dependencyReadyFile, 'utf8'));
477
- if (
478
- marker?.schemaVersion !== 1 ||
479
- marker.generationId !== validated.generation.generationId
480
- ) {
481
- return false;
482
- }
483
- if (
484
- expectedRestoreEpochMs &&
485
- String(marker.restoreEpochMs || '') !== expectedRestoreEpochMs
486
- ) {
487
- const signature = `${marker.generationId}:${marker.restoreEpochMs || ''}`;
488
- if (signature !== rejectedDependencyReceiptSignature) {
489
- rejectedDependencyReceiptSignature = signature;
490
- process.stdout.write(
491
- `${JSON.stringify({
492
- event: 'miaoda_nest_dependency_receipt_rejected',
493
- reason: 'restore-identity-mismatch',
494
- expectedRestoreEpochMs,
495
- })}\n`
496
- );
497
- }
498
- return false;
499
- }
500
- return fs.statSync(path.join(projectRoot, 'node_modules')).isDirectory();
501
- } catch {
502
- return false;
503
- }
504
- }
505
-
506
- function actionPluginPreconditionReady() {
507
- try {
508
- const marker = JSON.parse(fs.readFileSync(actionPluginReadyFile, 'utf8'));
509
- return (
510
- marker?.schemaVersion === 1 &&
511
- (!expectedRestoreEpochMs ||
512
- String(marker.restoreEpochMs || '') === expectedRestoreEpochMs)
513
- );
514
- } catch {
515
- return false;
516
- }
517
- }
518
-
519
- async function waitForActionPluginPrecondition() {
520
- const waitStartedAt = process.hrtime.bigint();
521
- while (!stopping && !actionPluginPreconditionReady()) await delay(100);
522
- if (stopping) throw new Error('MIAODA_SERVER_RUNTIME_STOPPING');
523
- const waitDurationMs = Number(durationMs(waitStartedAt).toFixed(3));
524
- emitRuntimePhase('action_plugin_wait', waitStartedAt, {
525
- actionPluginReadyFile,
526
- ownership: 'shared-parent',
527
- });
528
- return waitDurationMs;
529
- }
530
-
531
- async function waitForDependencies() {
532
- while (!stopping) {
533
- if (dependenciesAreReady()) return;
534
- await delay(100);
535
- }
536
- throw new Error('MIAODA_SERVER_RUNTIME_STOPPING');
537
- }
538
-
539
- async function waitForSourceChange() {
540
- const waitStartedAt = process.hrtime.bigint();
541
- await new Promise((resolve, reject) => {
542
- const watchers = [];
543
- const close = () => {
544
- clearTimeout(sourceHandoffTimer);
545
- for (const watcher of watchers) watcher.close();
546
- sourceHandoffWatcher = undefined;
547
- };
548
- const trigger = () => {
549
- clearTimeout(sourceHandoffTimer);
550
- sourceHandoffTimer = setTimeout(() => {
551
- close();
552
- resolve();
553
- }, 120);
554
- };
555
- try {
556
- for (const relative of ['server', 'shared']) {
557
- const directory = path.join(projectRoot, relative);
558
- if (!fs.existsSync(directory)) continue;
559
- watchers.push(
560
- fs.watch(directory, { recursive: true }, (_event, filename) => {
561
- if (!filename || !/\.(?:[cm]?[jt]sx?|json)$/.test(filename)) {
562
- return;
563
- }
564
- trigger();
565
- })
566
- );
567
- }
568
- if (watchers.length === 0) {
569
- reject(new Error('MIAODA_NEST_SOURCE_WATCH_ROOT_MISSING'));
570
- return;
571
- }
572
- sourceHandoffWatcher = { close };
573
- } catch (error) {
574
- close();
575
- reject(error);
576
- }
577
- });
578
- emitRuntimePhase('source_handoff_wait', waitStartedAt, {
579
- sourceHandoffMode,
580
- reason: 'source-change',
581
- });
582
- }
583
-
584
- function publishActionPluginPrecondition() {
585
- try {
586
- const marker = JSON.parse(fs.readFileSync(actionPluginReadyFile, 'utf8'));
587
- if (
588
- marker?.schemaVersion === 1 &&
589
- (!expectedRestoreEpochMs ||
590
- String(marker.restoreEpochMs || '') === expectedRestoreEpochMs)
591
- ) {
592
- return;
593
- }
594
- } catch {}
595
- fs.mkdirSync(path.dirname(actionPluginReadyFile), { recursive: true });
596
- const temporaryFile = `${actionPluginReadyFile}.${process.pid}.tmp`;
597
- fs.writeFileSync(
598
- temporaryFile,
599
- `${JSON.stringify({
600
- schemaVersion: 1,
601
- restoreEpochMs: expectedRestoreEpochMs,
602
- status: 'completed-by-cache-runtime',
603
- atEpochMs: Date.now(),
604
- })}\n`,
605
- { mode: 0o600 }
606
- );
607
- fs.renameSync(temporaryFile, actionPluginReadyFile);
608
- }
609
-
610
- async function initializeActionPlugins() {
611
- if (actionPluginsInitialized) return;
612
- if (skipActionPluginInit) {
613
- actionPluginsInitialized = true;
614
- process.stdout.write(
615
- `${JSON.stringify({
616
- event: 'miaoda_action_plugin_init_skipped',
617
- reason: 'shared-parent-initialized',
618
- })}\n`
619
- );
620
- return;
621
- }
622
- const cli = [
623
- process.env.MIAODA_FULLSTACK_CLI_BIN,
624
- path.join(
625
- projectRoot,
626
- 'node_modules',
627
- '@lark-apaas',
628
- 'fullstack-cli',
629
- 'bin',
630
- 'cli.js'
631
- ),
632
- '/usr/lib/node_modules/@lark-apaas/fullstack-cli/bin/cli.js',
633
- ].find(candidate => candidate && fs.existsSync(candidate));
634
- if (!cli) {
635
- process.stdout.write(
636
- `${JSON.stringify({ event: 'miaoda_action_plugin_init_skipped', reason: 'project-cli-missing' })}\n`
637
- );
638
- return;
639
- }
640
- const startedAt = process.hrtime.bigint();
641
- actionPluginInitChild = spawn(
642
- process.execPath,
643
- [cli, 'action-plugin', 'init'],
644
- {
645
- cwd: projectRoot,
646
- detached: true,
647
- stdio: 'inherit',
648
- env: { ...process.env },
649
- }
650
- );
651
- const code = await new Promise(resolve => {
652
- actionPluginInitChild.once('exit', exitCode => resolve(exitCode ?? 1));
653
- actionPluginInitChild.once('error', () => resolve(1));
654
- });
655
- actionPluginInitChild = undefined;
656
- process.stdout.write(
657
- `${JSON.stringify({
658
- event: 'miaoda_action_plugin_init_finished',
659
- code,
660
- durationMs: Number(durationMs(startedAt).toFixed(3)),
661
- })}\n`
662
- );
663
- if (code !== 0) {
664
- process.stderr.write(
665
- `${JSON.stringify({
666
- event: 'miaoda_action_plugin_init_failed',
667
- code,
668
- continueStartup: true,
669
- })}\n`
670
- );
671
- return;
672
- }
673
- actionPluginsInitialized = true;
674
- }
675
-
676
- async function drainCacheRuntime() {
677
- const deadline = Date.now() + cacheDrainTimeoutMs;
678
- while (!stopping && cacheUpstream.inflight > 0 && Date.now() < deadline) {
679
- await delay(25);
680
- }
681
- cacheRetired = true;
682
- terminate(cacheChild);
683
- await waitForChildExit(cacheChild, 1_000);
684
- process.stdout.write(
685
- `${JSON.stringify({
686
- event: 'miaoda_nest_cache_runtime_retired',
687
- remainingInflight: cacheUpstream.inflight,
688
- })}\n`
689
- );
690
- pruneSupersededGenerations();
691
- }
692
-
693
- async function promoteSourceRuntime() {
694
- await waitForDependencies();
695
- if (sourceHandoffMode === 'on-source-change') {
696
- await waitForSourceChange();
697
- }
698
- if (stopping) return;
699
- const startedAt = process.hrtime.bigint();
700
- const sourceRuntime = fileURLToPath(
701
- new URL('./server-startup-runtime.mjs', import.meta.url)
702
- );
703
- if (!fs.existsSync(sourceRuntime)) {
704
- throw new Error('MIAODA_NEST_SOURCE_RUNTIME_MISSING');
705
- }
706
- process.stdout.write(
707
- `${JSON.stringify({
708
- event: 'miaoda_nest_source_runtime_starting',
709
- dependencyReadyFile,
710
- sourceMode: 'workspace',
711
- })}\n`
712
- );
713
- await initializeActionPlugins();
714
- publishActionPluginPrecondition();
715
- if (stopping) return;
716
- sourceChild = spawn(process.execPath, [sourceRuntime], {
717
- cwd: projectRoot,
718
- detached: true,
719
- stdio: 'inherit',
720
- env: {
721
- ...process.env,
722
- MIAODA_CACHE_BOOTSTRAP_ENABLED: 'false',
723
- MIAODA_NEST_START_MODE: 'workspace',
724
- MIAODA_NEST_SKIP_ACTION_PLUGIN_INIT: 'true',
725
- MIAODA_WORKSPACE_READY_FILE: dependencyReadyFile,
726
- SERVER_HOST: sourceUpstream.host,
727
- SERVER_PORT: String(sourceUpstream.port),
728
- },
729
- });
730
- let sourceExit;
731
- let sourcePromoted = false;
732
- const sourceExitPromise = new Promise((_, reject) => {
733
- sourceChild.once('exit', (code, signal) => {
734
- sourceExit = { code, signal };
735
- if (stopping) return;
736
- process.stderr.write(
737
- `${JSON.stringify({ event: 'miaoda_nest_source_runtime_exited', code, signal })}\n`
738
- );
739
- if (sourcePromoted) {
740
- // The outer dev supervisor restarts this bridge. It first restores the
741
- // already-sealed cache worker, then promotes the dependency-backed worker.
742
- process.exit(code || 1);
743
- return;
744
- }
745
- reject(
746
- new Error(
747
- `MIAODA_NEST_SOURCE_RUNTIME_EXITED_BEFORE_READY: code=${code} signal=${signal}`
748
- )
749
- );
750
- });
751
- sourceChild.once('error', reject);
752
- });
753
- await Promise.race([
754
- waitForPort(sourceUpstream.host, sourceUpstream.port, sourceReadyTimeoutMs),
755
- sourceExitPromise,
756
- ]);
757
- sourcePromoted = true;
758
- if (sourceExit || !childIsRunning(sourceChild)) {
759
- throw new Error('MIAODA_NEST_SOURCE_RUNTIME_EXITED_DURING_PROMOTION');
760
- }
761
- if (stopping) return;
762
- activeUpstream = sourceUpstream;
763
- process.stdout.write(
764
- `${JSON.stringify({
765
- event: 'miaoda_nest_source_runtime_ready',
766
- pid: sourceChild.pid,
767
- supervisorPid: process.pid,
768
- serviceId,
769
- port: publicPort,
770
- upstreamPort: sourceUpstream.port,
771
- sourceMode: 'workspace',
772
- cutoverDurationMs: Number(durationMs(startedAt).toFixed(3)),
773
- })}\n`
774
- );
775
- void drainCacheRuntime();
776
- }
777
-
778
- async function close(signal) {
779
- if (stopping) return;
780
- stopping = true;
781
- clearTimeout(sourceHandoffTimer);
782
- sourceHandoffWatcher?.close();
783
- process.stdout.write(
784
- `${JSON.stringify({ event: 'miaoda_nest_runtime_stopping', signal })}\n`
785
- );
786
- proxy.close();
787
- terminate(actionPluginInitChild);
788
- terminate(sourceChild);
789
- terminate(cacheChild);
790
- setTimeout(() => process.exit(0), 250).unref();
791
- }
792
-
793
- for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
794
- process.once(signal, () => void close(signal));
795
- }
796
-
797
- const bundleListenWaitStartedAt = process.hrtime.bigint();
798
- await waitForPort(cacheUpstream.host, cacheUpstream.port);
799
- emitRuntimePhase('bundle_listen_wait', bundleListenWaitStartedAt, {
800
- cacheUpstreamPort: cacheUpstream.port,
801
- });
802
- const bundleStartupDurationMs = Number(
803
- durationMs(runtimeStartedAt).toFixed(3)
804
- );
805
- process.stdout.write(
806
- `${JSON.stringify({
807
- event: 'miaoda_nest_bundle_runtime_ready',
808
- pid: cacheChild.pid,
809
- supervisorPid: process.pid,
810
- generationId: validated.generation.generationId,
811
- upstreamHost: cacheUpstream.host,
812
- upstreamPort: cacheUpstream.port,
813
- atEpochMs: Date.now(),
814
- startupDurationMs: bundleStartupDurationMs,
815
- publicReadyBlockedBy: actionPluginPreconditionReady()
816
- ? null
817
- : 'action-plugin',
818
- })}\n`
819
- );
820
- const actionPluginWaitDurationMs = await waitForActionPluginPrecondition();
821
- const listenWaitStartedAt = process.hrtime.bigint();
822
- await listen(proxy, publicHost, publicPort);
823
- emitRuntimePhase('listen_wait', listenWaitStartedAt, {
824
- port: publicPort,
825
- cacheUpstreamPort: cacheUpstream.port,
826
- });
827
- publishServingReadyMarker(servingReadyMarker);
828
- process.stdout.write(
829
- `${JSON.stringify({
830
- event: 'miaoda_nest_cache_runtime_ready',
831
- pid: cacheChild.pid,
832
- supervisorPid: process.pid,
833
- serviceId,
834
- generationId: validated.generation.generationId,
835
- port: publicPort,
836
- bundleStartupDurationMs,
837
- actionPluginWaitDurationMs,
838
- startupDurationMs: Number(durationMs(runtimeStartedAt).toFixed(3)),
839
- })}\n`
840
- );
841
- async function promoteSourceRuntimeWithRetry() {
842
- try {
843
- await promoteSourceRuntime();
844
- } catch (error) {
845
- if (stopping) return;
846
- terminate(sourceChild);
847
- await waitForChildExit(sourceChild, 1_000);
848
- cacheRetired = true;
849
- proxy.close();
850
- terminate(cacheChild);
851
- process.stderr.write(
852
- `${JSON.stringify({
853
- event: 'miaoda_nest_source_runtime_promotion_failed',
854
- cacheServicePreserved: false,
855
- fallback: 'original-preview-retry',
856
- error: error instanceof Error ? error.message : String(error),
857
- })}\n`
858
- );
859
- process.exitCode = 1;
860
- setTimeout(() => process.exit(1), 250).unref();
861
- }
862
- }
863
-
864
- void promoteSourceRuntimeWithRetry();
865
- await new Promise(() => {});