@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
package/templates/scripts/dev.js
CHANGED
|
@@ -931,7 +931,11 @@ async function main() {
|
|
|
931
931
|
initializeActionPlugins();
|
|
932
932
|
}
|
|
933
933
|
|
|
934
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
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: '
|
|
1949
|
-
error:
|
|
1950
|
-
cacheContinuesServing:
|
|
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;
|