@lark-apaas/fullstack-cli 1.1.66-alpha.20260903064712 → 1.1.66-alpha.20260903130240

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.66-alpha.20260903064712",
3
+ "version": "1.1.66-alpha.20260903130240",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -931,7 +931,11 @@ async function main() {
931
931
  initializeActionPlugins();
932
932
  }
933
933
 
934
- // Start server and client
934
+ // Client startup never waits for the source Nest process. With a verified
935
+ // handoff owner, the image runtime already serves port 3000 and owns the
936
+ // delayed source lifecycle; dev.js starts only Vite and watches that owner.
937
+ // Without that receipt this block keeps the legacy ownership model: start
938
+ // source Nest and Vite together.
935
939
  const serverPromise = handoffOwnsBackend
936
940
  ? Promise.resolve()
937
941
  : startProcess({
@@ -8,6 +8,15 @@ import { spawn } from 'node:child_process';
8
8
  import { createHash, randomUUID } from 'node:crypto';
9
9
  import { fileURLToPath } from 'node:url';
10
10
 
11
+ // Restore-time state machine:
12
+ // validate immutable generation -> start cached Nest -> bind public proxy
13
+ // -> publish owner -> serve the first HTML response
14
+ // -> wait a fixed 15s -> start source Nest in the background
15
+ // -> switch only after source readiness, then drain/stop the cache.
16
+ // Source startup is therefore outside the first-screen critical path. The
17
+ // browser's MiaodaPreviewRendered event is observability only and is not a
18
+ // control-plane input. A failed source start terminates the handoff runtime so
19
+ // the backend immediately returns to the legacy startup-failure lifecycle.
11
20
  const SCHEMA_VERSION = 7;
12
21
  const BUNDLE_ENTRY = 'payload/server.bundle.cjs';
13
22
  const MAX_GENERATION_FILES = 50_000;
@@ -16,8 +25,6 @@ const MAX_GENERATION_FILE_BYTES = 64 * 1024 * 1024;
16
25
  const MAX_FILE_INDEX_BYTES = 16 * 1024 * 1024;
17
26
  const MAX_MANIFEST_BYTES = 1024 * 1024;
18
27
  const MAX_READINESS_RESPONSE_BYTES = 64 * 1024;
19
- const SOURCE_RETRY_INITIAL_DELAY_MS = 250;
20
- const SOURCE_RETRY_MAX_DELAY_MS = 5_000;
21
28
  const DEFAULT_SOURCE_START_DELAY_MS = 15_000;
22
29
  const TEST_SOURCE_START_DELAY_ARG = '--miaoda-test-source-start-delay-ms=';
23
30
  const RUNTIME_CONTROL_ENV_KEYS = new Set([
@@ -236,6 +243,9 @@ function positiveDuration(name, fallback) {
236
243
  }
237
244
 
238
245
  function sourceStartDelayForRuntime() {
246
+ // Production starts this file without extra argv, so the delay is fixed.
247
+ // The explicit argument exists only to keep integration tests fast and
248
+ // deterministic; inherited workspace environment cannot shorten the delay.
239
249
  const testArgument = process.argv
240
250
  .slice(2)
241
251
  .find(argument => argument.startsWith(TEST_SOURCE_START_DELAY_ARG));
@@ -918,7 +928,11 @@ function waitForHttp(
918
928
  if (response.statusCode !== 200) {
919
929
  attemptSettled = true;
920
930
  response.resume();
921
- retry();
931
+ finish(
932
+ new Error(
933
+ `${record.name} HTTP readiness rejected with status ${response.statusCode}`
934
+ )
935
+ );
922
936
  return;
923
937
  }
924
938
  const chunks = [];
@@ -930,7 +944,9 @@ function waitForHttp(
930
944
  if (responseBytes > MAX_READINESS_RESPONSE_BYTES) {
931
945
  attemptSettled = true;
932
946
  response.destroy();
933
- retry();
947
+ finish(
948
+ new Error(`${record.name} HTTP readiness response too large`)
949
+ );
934
950
  return;
935
951
  }
936
952
  chunks.push(buffer);
@@ -945,7 +961,10 @@ function waitForHttp(
945
961
  )
946
962
  )
947
963
  finish();
948
- else retry();
964
+ else
965
+ finish(
966
+ new Error(`${record.name} HTTP readiness payload rejected`)
967
+ );
949
968
  });
950
969
  response.once('error', () => {
951
970
  if (attemptSettled || settled) return;
@@ -1642,6 +1661,9 @@ async function waitForReceipt(expectedGenerationId) {
1642
1661
  }
1643
1662
 
1644
1663
  async function waitForSourceStartWindow() {
1664
+ // `finish` on the public proxy response proves the complete cached HTML body
1665
+ // has been handed to Node's HTTP layer. It is intentionally independent of
1666
+ // frontend callbacks and of source readiness.
1645
1667
  if (sourceStartDelayMs === 0) return;
1646
1668
  const htmlTransferredAtMs = await firstHtmlTransferred;
1647
1669
  const deadline = htmlTransferredAtMs + sourceStartDelayMs;
@@ -1682,27 +1704,6 @@ async function waitForDrain(upstream) {
1682
1704
  }
1683
1705
  }
1684
1706
 
1685
- function reportSourcePromotionFailure(stage, error, retryDelayMs) {
1686
- process.stderr.write(
1687
- `${JSON.stringify({
1688
- event: 'miaoda_nest_source_promotion_retry',
1689
- stage,
1690
- error: error instanceof Error ? error.message : String(error),
1691
- retryDelayMs,
1692
- cacheContinuesServing: true,
1693
- })}\n`
1694
- );
1695
- }
1696
-
1697
- async function waitForSourceRetry(delayMs) {
1698
- const deadline = Date.now() + delayMs;
1699
- while (!shuttingDown && Date.now() < deadline) {
1700
- await new Promise(resolve =>
1701
- setTimeout(resolve, Math.min(50, deadline - Date.now()))
1702
- );
1703
- }
1704
- }
1705
-
1706
1707
  async function startAndPromoteSource(baseEnv, expectedCapabilityIds) {
1707
1708
  const sourcePort = await reservePort(probeHost(publicHost));
1708
1709
  const candidate = {
@@ -1786,30 +1787,9 @@ async function promoteSource(expectedGenerationId, expectedCapabilityIds) {
1786
1787
  : 0,
1787
1788
  })}\n`
1788
1789
  );
1789
- let retryDelayMs = SOURCE_RETRY_INITIAL_DELAY_MS;
1790
- let baseEnv;
1791
- while (!shuttingDown && !baseEnv) {
1792
- try {
1793
- baseEnv = { ...process.env, ...readRuntimeEnvironment() };
1794
- await runActionPluginInit(baseEnv);
1795
- } catch (error) {
1796
- baseEnv = undefined;
1797
- reportSourcePromotionFailure('environment-and-init', error, retryDelayMs);
1798
- await waitForSourceRetry(retryDelayMs);
1799
- retryDelayMs = Math.min(retryDelayMs * 2, SOURCE_RETRY_MAX_DELAY_MS);
1800
- }
1801
- }
1802
- retryDelayMs = SOURCE_RETRY_INITIAL_DELAY_MS;
1803
- while (!shuttingDown && !promoted) {
1804
- try {
1805
- await startAndPromoteSource(baseEnv, expectedCapabilityIds);
1806
- } catch (error) {
1807
- if (shuttingDown) break;
1808
- reportSourcePromotionFailure('source-readiness', error, retryDelayMs);
1809
- await waitForSourceRetry(retryDelayMs);
1810
- retryDelayMs = Math.min(retryDelayMs * 2, SOURCE_RETRY_MAX_DELAY_MS);
1811
- }
1812
- }
1790
+ const baseEnv = { ...process.env, ...readRuntimeEnvironment() };
1791
+ await runActionPluginInit(baseEnv);
1792
+ await startAndPromoteSource(baseEnv, expectedCapabilityIds);
1813
1793
  }
1814
1794
 
1815
1795
  function closeProxyBounded() {
@@ -1910,6 +1890,10 @@ async function main() {
1910
1890
  [generation.entryFile],
1911
1891
  {
1912
1892
  env: {
1893
+ // The first-screen bundle is self-contained. NODE_PATH deliberately
1894
+ // points at the restored workspace only for latent external package
1895
+ // loads; the Builder probe starts from an isolated copy and rejects
1896
+ // workspace module resolution on the supported probe surface.
1913
1897
  NODE_PATH: generation.workspaceNodeModulesPath,
1914
1898
  NODE_OPTIONS: '',
1915
1899
  NODE_ENV: 'development',
@@ -1943,13 +1927,15 @@ async function main() {
1943
1927
  generation.expectedCapabilityIds
1944
1928
  ).catch(error => {
1945
1929
  if (!shuttingDown) {
1930
+ const detail = error instanceof Error ? error.message : String(error);
1946
1931
  process.stderr.write(
1947
1932
  `${JSON.stringify({
1948
- event: 'miaoda_nest_source_promotion_stopped',
1949
- error: error instanceof Error ? error.message : String(error),
1950
- cacheContinuesServing: true,
1933
+ event: 'miaoda_nest_source_promotion_failed',
1934
+ error: detail,
1935
+ cacheContinuesServing: false,
1951
1936
  })}\n`
1952
1937
  );
1938
+ void shutdown(`source-promotion-failed:${detail}`, 1);
1953
1939
  }
1954
1940
  });
1955
1941
  await lifetime;