@lark-apaas/fullstack-cli 1.1.59-alpha.20260719171717 → 1.1.59-alpha.20260720184331

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.59-alpha.20260719171717",
3
+ "version": "1.1.59-alpha.20260720184331",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,9 @@ const readline = require('readline');
8
8
  const {
9
9
  applyPreviewArtifactHostOverrides,
10
10
  buildListeningPortLookupArgs,
11
+ closePreviewPhaseReporterWithRetry,
11
12
  createDeferredTypecheckGate,
13
+ createPreviewPhaseFrameDecoder,
12
14
  createPreviewPhaseReporter,
13
15
  normalizeTcpPort,
14
16
  parseTscWatchSummary,
@@ -199,6 +201,7 @@ function startProcess({
199
201
  args,
200
202
  cleanupPort,
201
203
  phaseDetail = {},
204
+ phaseChannel = false,
202
205
  onReady,
203
206
  onOutputLine,
204
207
  }) {
@@ -236,12 +239,23 @@ function startProcess({
236
239
  }
237
240
 
238
241
  while (!stopping) {
242
+ const childEnv = { ...process.env };
243
+ // The parent is the only outbox writer. Child processes receive a narrow
244
+ // producer channel, never the filesystem destination.
245
+ delete childEnv.MIAODA_PREVIEW_PHASE_OUTBOX_ROOT;
246
+ if (phaseChannel) {
247
+ childEnv.MIAODA_PREVIEW_PHASE_FD = '3';
248
+ } else {
249
+ delete childEnv.MIAODA_PREVIEW_PHASE_FD;
250
+ }
239
251
  const child = spawn(command, args, {
240
252
  detached: true,
241
- stdio: ['ignore', 'pipe', 'pipe'],
253
+ stdio: phaseChannel
254
+ ? ['ignore', 'pipe', 'pipe', 'pipe']
255
+ : ['ignore', 'pipe', 'pipe'],
242
256
  shell: true,
243
257
  cwd: PROJECT_ROOT,
244
- env: { ...process.env },
258
+ env: childEnv,
245
259
  });
246
260
 
247
261
  entry.pid = child.pid;
@@ -307,13 +321,24 @@ function startProcess({
307
321
 
308
322
  // Pipe stdout and stderr through readline for timestamped logging
309
323
  let typecheckSummaryReported = false;
310
- const pipeLines = stream => {
324
+ let phaseReported = false;
325
+ const pipeLines = (stream, allowPhaseFallback = false) => {
311
326
  const rl = readline.createInterface({
312
327
  input: stream,
313
328
  crlfDelay: Infinity,
314
329
  });
315
330
  rl.on('line', line => {
316
331
  if (onOutputLine) onOutputLine(line);
332
+ const legacyPhaseCandidate =
333
+ allowPhaseFallback && line.startsWith('[MiaodaPreviewPhase] ');
334
+ if (legacyPhaseCandidate && !phaseReported) {
335
+ phaseReported = previewPhaseReporter.ingest(line, {
336
+ producer: 'vite_plugin',
337
+ allowed_phases: ['vite_http_listening'],
338
+ attempt,
339
+ allow_legacy_stdout: true,
340
+ });
341
+ }
317
342
  if (name === 'server-typecheck' && !typecheckSummaryReported) {
318
343
  const summary = parseTscWatchSummary(line);
319
344
  if (summary) {
@@ -334,11 +359,38 @@ function startProcess({
334
359
  try {
335
360
  fs.writeSync(logFd, msg);
336
361
  } catch {}
337
- writeOutput(msg);
362
+ // Phase fallbacks are normalized by the parent reporter before they
363
+ // enter the shared log. Keep the raw child claim quarantined in the
364
+ // component log so collectors never observe two representations.
365
+ if (!legacyPhaseCandidate) writeOutput(msg);
338
366
  });
339
367
  };
340
- if (child.stdout) pipeLines(child.stdout);
368
+ if (child.stdout) pipeLines(child.stdout, phaseChannel);
341
369
  if (child.stderr) pipeLines(child.stderr);
370
+ if (phaseChannel && child.stdio[3]) {
371
+ let oversizedPhaseFrameReported = false;
372
+ const phaseDecoder = createPreviewPhaseFrameDecoder({
373
+ max_bytes: 16 * 1024,
374
+ on_oversized_frame: () => {
375
+ if (oversizedPhaseFrameReported) return;
376
+ oversizedPhaseFrameReported = true;
377
+ logEvent(
378
+ 'WARN',
379
+ name,
380
+ 'Dropped an oversized preview phase producer frame'
381
+ );
382
+ },
383
+ on_frame: line => {
384
+ if (phaseReported) return;
385
+ phaseReported = previewPhaseReporter.ingest(line, {
386
+ producer: 'vite_plugin',
387
+ allowed_phases: ['vite_http_listening'],
388
+ attempt,
389
+ });
390
+ },
391
+ });
392
+ child.stdio[3].on('data', chunk => phaseDecoder.push(chunk));
393
+ }
342
394
 
343
395
  // Wait for the direct child to exit.
344
396
  // NOTE: must use 'exit', not 'close'. With shell:true, grandchild processes
@@ -468,6 +520,16 @@ async function cleanup() {
468
520
 
469
521
  logEvent('INFO', 'main', 'All processes stopped');
470
522
 
523
+ const previewPhaseSealed =
524
+ await closePreviewPhaseReporterWithRetry(previewPhaseReporter);
525
+ if (!previewPhaseSealed) {
526
+ logEvent(
527
+ 'ERROR',
528
+ 'main',
529
+ 'Preview phase outbox did not seal after bounded shutdown retries'
530
+ );
531
+ }
532
+
471
533
  try {
472
534
  fs.closeSync(devStdLogFd);
473
535
  } catch {}
@@ -475,7 +537,7 @@ async function cleanup() {
475
537
  fs.closeSync(devLogFd);
476
538
  } catch {}
477
539
 
478
- process.exit(0);
540
+ process.exit(previewPhaseSealed ? 0 : 1);
479
541
  }
480
542
 
481
543
  process.on('SIGTERM', cleanup);
@@ -644,6 +706,7 @@ async function main() {
644
706
  command: 'npm',
645
707
  args: ['run', 'dev:client'],
646
708
  cleanupPort: CLIENT_DEV_PORT,
709
+ phaseChannel: true,
647
710
  });
648
711
 
649
712
  writeOutput(`📋 Dev processes running. Press Ctrl+C to stop.\n`);
@@ -657,7 +720,14 @@ async function main() {
657
720
  }
658
721
  }
659
722
 
660
- main().catch(err => {
723
+ main().catch(async err => {
661
724
  console.error('Fatal error:', err);
725
+ const previewPhaseSealed =
726
+ await closePreviewPhaseReporterWithRetry(previewPhaseReporter);
727
+ if (!previewPhaseSealed) {
728
+ console.error(
729
+ 'Preview phase outbox did not seal after bounded fatal-exit retries'
730
+ );
731
+ }
662
732
  process.exit(1);
663
733
  });