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

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.20260902183957",
3
+ "version": "1.1.66-alpha.20260903064712",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,6 +18,8 @@ const MAX_MANIFEST_BYTES = 1024 * 1024;
18
18
  const MAX_READINESS_RESPONSE_BYTES = 64 * 1024;
19
19
  const SOURCE_RETRY_INITIAL_DELAY_MS = 250;
20
20
  const SOURCE_RETRY_MAX_DELAY_MS = 5_000;
21
+ const DEFAULT_SOURCE_START_DELAY_MS = 15_000;
22
+ const TEST_SOURCE_START_DELAY_ARG = '--miaoda-test-source-start-delay-ms=';
21
23
  const RUNTIME_CONTROL_ENV_KEYS = new Set([
22
24
  'MIAODA_COLD_START_DECISION_FILE',
23
25
  'MIAODA_DEPENDENCY_READY_FILE',
@@ -72,14 +74,16 @@ const publicPort = Number(process.env.SERVER_PORT || 3000);
72
74
  const logDir = path.resolve(projectRoot, process.env.LOG_DIR || 'logs');
73
75
  const readinessPath =
74
76
  process.env.MIAODA_NEST_READINESS_PATH || '/__innerapi__/capability/list';
75
- const clientBasePath =
76
- process.env.CLIENT_BASE_PATH?.startsWith('/') &&
77
- !process.env.CLIENT_BASE_PATH.startsWith('//')
78
- ? process.env.CLIENT_BASE_PATH.replace(/\/+$/, '')
79
- : '';
80
- const sourceReadinessPath = clientBasePath
81
- ? `${clientBasePath}/__innerapi__/capability/list`
82
- : readinessPath;
77
+ function resolveSourceReadinessPath(baseEnv) {
78
+ const clientBasePath =
79
+ baseEnv.CLIENT_BASE_PATH?.startsWith('/') &&
80
+ !baseEnv.CLIENT_BASE_PATH.startsWith('//')
81
+ ? baseEnv.CLIENT_BASE_PATH.replace(/\/+$/, '')
82
+ : '';
83
+ return clientBasePath
84
+ ? `${clientBasePath}/__innerapi__/capability/list`
85
+ : readinessPath;
86
+ }
83
87
  const sourceReadyTimeoutMs = positiveDuration(
84
88
  'MIAODA_NEST_SOURCE_READY_TIMEOUT_MS',
85
89
  60_000
@@ -89,6 +93,7 @@ const shutdownTimeoutMs = positiveDuration(
89
93
  5_000
90
94
  );
91
95
  const drainTimeoutMs = positiveDuration('MIAODA_NEST_DRAIN_TIMEOUT_MS', 5_000);
96
+ const sourceStartDelayMs = sourceStartDelayForRuntime();
92
97
  const processStartTicks =
93
98
  readLinuxProcessStartTicks(process.pid) ||
94
99
  String(
@@ -219,12 +224,29 @@ let resolveLifetime;
219
224
  const lifetime = new Promise(resolve => {
220
225
  resolveLifetime = resolve;
221
226
  });
227
+ let firstHtmlTransferredAtMs = 0;
228
+ let resolveFirstHtmlTransferred;
229
+ const firstHtmlTransferred = new Promise(resolve => {
230
+ resolveFirstHtmlTransferred = resolve;
231
+ });
222
232
 
223
233
  function positiveDuration(name, fallback) {
224
234
  const value = Number(process.env[name] || fallback);
225
235
  return Number.isFinite(value) && value > 0 ? value : fallback;
226
236
  }
227
237
 
238
+ function sourceStartDelayForRuntime() {
239
+ const testArgument = process.argv
240
+ .slice(2)
241
+ .find(argument => argument.startsWith(TEST_SOURCE_START_DELAY_ARG));
242
+ if (!testArgument) return DEFAULT_SOURCE_START_DELAY_MS;
243
+ const value = Number(testArgument.slice(TEST_SOURCE_START_DELAY_ARG.length));
244
+ if (!Number.isFinite(value) || value < 0) {
245
+ throw new Error('MIAODA_NEST_TEST_SOURCE_START_DELAY_INVALID');
246
+ }
247
+ return value;
248
+ }
249
+
228
250
  function validateRuntimeConfiguration() {
229
251
  if (process.env.MIAODA_NEST_BUNDLE_HANDOFF_ENABLED !== 'true') {
230
252
  throw new Error('MIAODA_NEST_BUNDLE_HANDOFF_DISABLED');
@@ -962,6 +984,36 @@ function retain(upstream) {
962
984
  };
963
985
  }
964
986
 
987
+ function observeFirstCacheHtmlTransfer(request, response, upstreamResponse) {
988
+ if (
989
+ firstHtmlTransferredAtMs ||
990
+ request.method !== 'GET' ||
991
+ activeUpstream !== cacheUpstream ||
992
+ upstreamResponse.statusCode < 200 ||
993
+ upstreamResponse.statusCode >= 400 ||
994
+ !String(upstreamResponse.headers['content-type'] || '')
995
+ .toLowerCase()
996
+ .startsWith('text/html')
997
+ ) {
998
+ return;
999
+ }
1000
+ response.once('finish', () => {
1001
+ if (firstHtmlTransferredAtMs) return;
1002
+ firstHtmlTransferredAtMs = Date.now();
1003
+ resolveFirstHtmlTransferred(firstHtmlTransferredAtMs);
1004
+ process.stderr.write(
1005
+ `${JSON.stringify({
1006
+ event: 'miaoda_nest_html_transferred',
1007
+ bootId,
1008
+ generationId: currentGenerationId,
1009
+ sourceStartDelayMs,
1010
+ atMs: firstHtmlTransferredAtMs,
1011
+ elapsedMs: firstHtmlTransferredAtMs - restoreEpochMs,
1012
+ })}\n`
1013
+ );
1014
+ });
1015
+ }
1016
+
965
1017
  function createProxy() {
966
1018
  const server = http.createServer((request, response) => {
967
1019
  const upstream = activeUpstream;
@@ -980,6 +1032,7 @@ function createProxy() {
980
1032
  headers: request.headers,
981
1033
  },
982
1034
  upstreamResponse => {
1035
+ observeFirstCacheHtmlTransfer(request, response, upstreamResponse);
983
1036
  response.writeHead(
984
1037
  upstreamResponse.statusCode || 502,
985
1038
  upstreamResponse.statusMessage || '',
@@ -1588,6 +1641,18 @@ async function waitForReceipt(expectedGenerationId) {
1588
1641
  if (shuttingDown) throw new Error('MIAODA_NEST_RUNTIME_STOPPING');
1589
1642
  }
1590
1643
 
1644
+ async function waitForSourceStartWindow() {
1645
+ if (sourceStartDelayMs === 0) return;
1646
+ const htmlTransferredAtMs = await firstHtmlTransferred;
1647
+ const deadline = htmlTransferredAtMs + sourceStartDelayMs;
1648
+ while (!shuttingDown && Date.now() < deadline) {
1649
+ await new Promise(resolve =>
1650
+ setTimeout(resolve, Math.min(50, deadline - Date.now()))
1651
+ );
1652
+ }
1653
+ if (shuttingDown) throw new Error('MIAODA_NEST_RUNTIME_STOPPING');
1654
+ }
1655
+
1591
1656
  async function runActionPluginInit(baseEnv) {
1592
1657
  const record = spawnOwned(
1593
1658
  'action-plugin-init',
@@ -1667,7 +1732,7 @@ async function startAndPromoteSource(baseEnv, expectedCapabilityIds) {
1667
1732
  candidate,
1668
1733
  sourceReadyTimeoutMs,
1669
1734
  expectedCapabilityIds,
1670
- sourceReadinessPath
1735
+ resolveSourceReadinessPath(baseEnv)
1671
1736
  );
1672
1737
  } catch (error) {
1673
1738
  try {
@@ -1706,6 +1771,21 @@ async function startAndPromoteSource(baseEnv, expectedCapabilityIds) {
1706
1771
 
1707
1772
  async function promoteSource(expectedGenerationId, expectedCapabilityIds) {
1708
1773
  await waitForReceipt(expectedGenerationId);
1774
+ await waitForSourceStartWindow();
1775
+ const sourceStartingAtMs = Date.now();
1776
+ process.stderr.write(
1777
+ `${JSON.stringify({
1778
+ event: 'miaoda_nest_source_starting',
1779
+ bootId,
1780
+ generationId: currentGenerationId,
1781
+ sourceStartDelayMs,
1782
+ atMs: sourceStartingAtMs,
1783
+ elapsedMs: sourceStartingAtMs - restoreEpochMs,
1784
+ htmlToSourceStartMs: firstHtmlTransferredAtMs
1785
+ ? sourceStartingAtMs - firstHtmlTransferredAtMs
1786
+ : 0,
1787
+ })}\n`
1788
+ );
1709
1789
  let retryDelayMs = SOURCE_RETRY_INITIAL_DELAY_MS;
1710
1790
  let baseEnv;
1711
1791
  while (!shuttingDown && !baseEnv) {