@lark-apaas/fullstack-cli 1.1.62-alpha.20260824160408 → 1.1.62-alpha.20260824233147

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.62-alpha.20260824160408",
3
+ "version": "1.1.62-alpha.20260824233147",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import fs from 'node:fs';
4
- import http from 'node:http';
5
4
  import net from 'node:net';
6
5
  import path from 'node:path';
7
6
  import { spawn } from 'node:child_process';
@@ -17,7 +16,6 @@ const clientHost = process.env.CLIENT_DEV_HOST || '0.0.0.0';
17
16
  const clientPort = Number(process.env.CLIENT_DEV_PORT || 8080);
18
17
  const serverHost = process.env.SERVER_HOST || '0.0.0.0';
19
18
  const serverPort = Number(process.env.SERVER_PORT || 3000);
20
- const internalViteHost = process.env.MIAODA_VITE_INTERNAL_HOST || '127.0.0.1';
21
19
  const vite8RuntimeEnabled = process.env.MIAODA_VITE8_RUNTIME_ENABLED === 'true';
22
20
  const devBundleEnabled = process.env.MIAODA_VITE_DEV_BUNDLE_ENABLED === 'true';
23
21
  if (devBundleEnabled && !vite8RuntimeEnabled) {
@@ -61,7 +59,8 @@ const vite8Bin = path.resolve(
61
59
  process.env.MIAODA_VITE8_BIN ||
62
60
  '/opt/miaoda/preview-runtime/node_modules/vite/bin/vite.js'
63
61
  );
64
- // pair_ready 的就绪口径由 MIAODA_PAIR_READY_GATE 控制:
62
+ // pair_ready 仅作为就绪观测事件,不再阻断 Vite 公共端口。
63
+ // 就绪口径由 MIAODA_PAIR_READY_GATE 控制:
65
64
  // 'true'(默认)= 等 Vite + Nest 双就绪('vite-and-nest');
66
65
  // 'false' = 只等 Vite 就绪('vite'),对齐 legacy 预览链路的放行语义。
67
66
  // 关闭 gate 时 Nest 就绪仍被观测并照常发 nest_listen_ready 事件,
@@ -74,8 +73,6 @@ const runtimeStartedAtEpochMs = Date.now();
74
73
  const startedAt = process.hrtime.bigint();
75
74
  let stopping = false;
76
75
  let child;
77
- let proxyServer;
78
- let pairReady = false;
79
76
 
80
77
  function durationMs(since = startedAt) {
81
78
  return Number(process.hrtime.bigint() - since) / 1_000_000;
@@ -202,26 +199,6 @@ function probeHost(host) {
202
199
  return host.replace(/^\[|\]$/g, '');
203
200
  }
204
201
 
205
- async function reservePort() {
206
- const configured = Number(process.env.MIAODA_VITE_INTERNAL_PORT || 0);
207
- if (
208
- Number.isSafeInteger(configured) &&
209
- configured > 0 &&
210
- configured <= 65_535
211
- ) {
212
- return configured;
213
- }
214
- return await new Promise((resolve, reject) => {
215
- const server = net.createServer();
216
- server.once('error', reject);
217
- server.listen(0, internalViteHost, () => {
218
- const address = server.address();
219
- const port = address && typeof address !== 'string' ? address.port : 0;
220
- server.close(error => (error ? reject(error) : resolve(port)));
221
- });
222
- });
223
- }
224
-
225
202
  async function waitForPort(host, port, phase) {
226
203
  const listenStartedAt = process.hrtime.bigint();
227
204
  const deadline = Date.now() + 60_000;
@@ -250,93 +227,6 @@ async function waitForPort(host, port, phase) {
250
227
  throw new Error(`MIAODA_WORKSPACE_SERVICE_READY_TIMEOUT: ${phase}`);
251
228
  }
252
229
 
253
- function rejectBeforePair(response) {
254
- response.writeHead(503, {
255
- 'content-type': 'text/plain; charset=utf-8',
256
- 'cache-control': 'no-store',
257
- 'retry-after': '1',
258
- connection: 'close',
259
- });
260
- response.end('preview services are starting');
261
- }
262
-
263
- function proxyHttp(request, response, internalVitePort) {
264
- if (!pairReady) {
265
- rejectBeforePair(response);
266
- return;
267
- }
268
- const upstream = http.request(
269
- {
270
- host: internalViteHost,
271
- port: internalVitePort,
272
- method: request.method,
273
- path: request.url,
274
- headers: request.headers,
275
- },
276
- upstreamResponse => {
277
- response.writeHead(
278
- upstreamResponse.statusCode || 502,
279
- upstreamResponse.statusMessage,
280
- upstreamResponse.headers
281
- );
282
- upstreamResponse.pipe(response);
283
- }
284
- );
285
- upstream.once('error', error => {
286
- if (!response.headersSent) {
287
- response.writeHead(502, {
288
- 'content-type': 'text/plain; charset=utf-8',
289
- 'cache-control': 'no-store',
290
- });
291
- }
292
- response.end(`vite upstream unavailable: ${error.code || 'unknown'}`);
293
- });
294
- request.pipe(upstream);
295
- }
296
-
297
- function proxyUpgrade(request, socket, head, internalVitePort) {
298
- if (!pairReady) {
299
- socket.destroy();
300
- return;
301
- }
302
- const upstream = net.createConnection(
303
- { host: internalViteHost, port: internalVitePort },
304
- () => {
305
- const lines = [
306
- `${request.method} ${request.url} HTTP/${request.httpVersion}`,
307
- ];
308
- for (let index = 0; index < request.rawHeaders.length; index += 2) {
309
- lines.push(
310
- `${request.rawHeaders[index]}: ${request.rawHeaders[index + 1]}`
311
- );
312
- }
313
- upstream.write(`${lines.join('\r\n')}\r\n\r\n`);
314
- if (head.length > 0) upstream.write(head);
315
- socket.pipe(upstream).pipe(socket);
316
- }
317
- );
318
- upstream.once('error', () => socket.destroy());
319
- socket.once('error', () => upstream.destroy());
320
- }
321
-
322
- async function listenPublicProxy(internalVitePort) {
323
- proxyServer = http.createServer((request, response) =>
324
- proxyHttp(request, response, internalVitePort)
325
- );
326
- proxyServer.on('upgrade', (request, socket, head) =>
327
- proxyUpgrade(request, socket, head, internalVitePort)
328
- );
329
- await new Promise((resolve, reject) => {
330
- proxyServer.once('error', reject);
331
- proxyServer.listen(clientPort, clientHost, resolve);
332
- });
333
- emit('public_gate_listen', startedAt, {
334
- host: clientHost,
335
- port: clientPort,
336
- readinessScope,
337
- });
338
- }
339
-
340
230
  function terminate(signal = 'SIGTERM') {
341
231
  if (!child?.pid) return;
342
232
  try {
@@ -351,8 +241,6 @@ function terminate(signal = 'SIGTERM') {
351
241
  async function close(signal) {
352
242
  if (stopping) return;
353
243
  stopping = true;
354
- pairReady = false;
355
- proxyServer?.close();
356
244
  terminate(signal);
357
245
  setTimeout(() => terminate('SIGKILL'), 1_000).unref();
358
246
  }
@@ -377,8 +265,6 @@ if (
377
265
  }
378
266
 
379
267
  await waitForDependencies();
380
- const internalVitePort = await reservePort();
381
- await listenPublicProxy(internalVitePort);
382
268
  if (nestStartMode === 'bundle-handoff') {
383
269
  await waitForPort(serverHost, serverPort, 'nest_precedes_vite');
384
270
  }
@@ -404,9 +290,9 @@ const viteCliArgs = [
404
290
  '--config',
405
291
  viteRuntimeConfig,
406
292
  '--host',
407
- internalViteHost,
293
+ clientHost,
408
294
  '--port',
409
- String(internalVitePort),
295
+ String(clientPort),
410
296
  '--strictPort',
411
297
  ];
412
298
  if (devBundleEnabled) {
@@ -438,8 +324,6 @@ child = spawn(viteCommand, viteArgs, {
438
324
  MIAODA_VITE_DEV_BUNDLE_ENABLED: String(devBundleEnabled),
439
325
  MIAODA_VITE_DEPS_CACHE_ENABLED: viteDepsCacheEnabled,
440
326
  MIAODA_VITE_EPHEMERAL_CACHE_DIR: viteEphemeralCacheDir,
441
- MIAODA_VITE_INTERNAL_HOST: internalViteHost,
442
- MIAODA_VITE_INTERNAL_PORT: String(internalVitePort),
443
327
  },
444
328
  });
445
329
  emit('process_spawn', spawnStartedAt, {
@@ -454,59 +338,80 @@ emit('process_spawn', spawnStartedAt, {
454
338
  viteRuntimeConfig,
455
339
  viteEphemeralCacheDir:
456
340
  viteDepsCacheEnabled === 'false' ? viteEphemeralCacheDir : undefined,
457
- internalViteHost,
458
- internalVitePort,
341
+ host: clientHost,
342
+ port: clientPort,
343
+ portMode: 'direct',
459
344
  });
460
345
 
461
346
  const exitPromise = new Promise((resolve, reject) => {
462
347
  child.once('error', reject);
463
348
  child.once('exit', (code, signal) => resolve({ code, signal }));
464
349
  });
465
- const viteListenWait = waitForPort(
466
- internalViteHost,
467
- internalVitePort,
468
- 'vite_listen_ready'
469
- );
350
+ const viteListenWait = waitForPort(clientHost, clientPort, 'vite_listen_ready');
470
351
  const viteRuntimeReadiness = [viteListenWait];
471
352
  if (devBundleEnabled) {
472
353
  viteRuntimeReadiness.push(waitForBundledDevDocument());
473
354
  } else {
474
355
  viteRuntimeReadiness.push(waitForWarmupReady());
475
356
  }
476
- let readinessWait = Promise.all(viteRuntimeReadiness);
477
- if (pairReadyGate) {
478
- readinessWait = Promise.all([
479
- ...viteRuntimeReadiness,
480
- waitForPort(serverHost, serverPort, 'nest_listen_ready'),
481
- ]);
482
- } else {
483
- // Gate 关闭时 pair_ready 只跟随 Vite;Nest 就绪在后台继续观测,
484
- // nest_listen_ready 事件照常发出,失败不阻断预览放行。
485
- waitForPort(serverHost, serverPort, 'nest_listen_ready').catch(() => {});
486
- }
357
+ const nestReadiness = waitForPort(serverHost, serverPort, 'nest_listen_ready')
358
+ .then(() => ({ ready: true }))
359
+ .catch(error => ({ ready: false, error }));
487
360
  await Promise.race([
488
- readinessWait,
361
+ Promise.all(viteRuntimeReadiness),
489
362
  exitPromise.then(exit => {
490
363
  throw new Error(
491
364
  `MIAODA_WORKSPACE_VITE_EXITED_BEFORE_READY: code=${exit.code} signal=${exit.signal}`
492
365
  );
493
366
  }),
494
367
  ]);
495
- pairReady = true;
496
- emit('pair_ready', startedAt, {
497
- host: clientHost,
498
- port: clientPort,
499
- internalViteHost,
500
- internalVitePort,
501
- serverHost,
502
- serverPort,
503
- readinessScope,
504
- });
505
368
  emit('startup', startedAt, {
506
369
  pid: child.pid,
507
370
  port: clientPort,
508
- readinessScope,
371
+ readinessScope: 'vite',
372
+ pairReadinessScope: readinessScope,
373
+ portMode: 'direct',
509
374
  });
375
+ const emitPairReady = () =>
376
+ emit('pair_ready', startedAt, {
377
+ host: clientHost,
378
+ port: clientPort,
379
+ portMode: 'direct',
380
+ serverHost,
381
+ serverPort,
382
+ readinessScope,
383
+ });
384
+ if (pairReadyGate) {
385
+ void nestReadiness.then(result => {
386
+ if (result.ready) {
387
+ emitPairReady();
388
+ } else if (!stopping) {
389
+ emit('pair_ready_failed', startedAt, {
390
+ serverHost,
391
+ serverPort,
392
+ readinessScope,
393
+ reason:
394
+ result.error instanceof Error
395
+ ? result.error.message
396
+ : String(result.error),
397
+ });
398
+ }
399
+ });
400
+ } else {
401
+ emitPairReady();
402
+ void nestReadiness.then(result => {
403
+ if (!result.ready && !stopping) {
404
+ emit('nest_readiness_observation_failed', startedAt, {
405
+ serverHost,
406
+ serverPort,
407
+ reason:
408
+ result.error instanceof Error
409
+ ? result.error.message
410
+ : String(result.error),
411
+ });
412
+ }
413
+ });
414
+ }
510
415
 
511
416
  const exit = await exitPromise;
512
417
  const wasStopping = stopping;