@lark-apaas/fullstack-cli 1.1.60 → 1.1.61-alpha.20260818172555
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/dist/client-dependency-graph.d.ts +37 -0
- package/dist/client-dependency-graph.js +466 -0
- package/dist/index.js +3482 -143
- package/package.json +4 -1
- package/templates/scripts/cache-generation-preflight.mjs +1346 -0
- package/templates/scripts/cache-generation-prune.mjs +174 -0
- package/templates/scripts/cache-runtime-coordinator.mjs +487 -0
- package/templates/scripts/dev.js +433 -59
- package/templates/scripts/dev.sh +2 -0
- package/templates/scripts/lint.js +2 -40
- package/templates/scripts/patch-vite-dependency-graph-hash.mjs +141 -0
- package/templates/scripts/server-cache-module-resolver.cjs +63 -0
- package/templates/scripts/server-cache-runtime.mjs +681 -0
- package/templates/scripts/server-startup-runtime.mjs +496 -0
- package/templates/scripts/server-transition-runtime.mjs +655 -0
- package/templates/scripts/vite-cache-runtime.mjs +3100 -0
- package/templates/scripts/workspace-client-runtime.mjs +176 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import net from 'node:net';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
const projectRoot = path.resolve(
|
|
9
|
+
process.env.MIAODA_WORKSPACE_ROOT || process.cwd()
|
|
10
|
+
);
|
|
11
|
+
const workspaceReadyFile = path.resolve(
|
|
12
|
+
process.env.MIAODA_WORKSPACE_READY_FILE || '/tmp/event/WORKSPACE_READY'
|
|
13
|
+
);
|
|
14
|
+
const clientHost = process.env.CLIENT_DEV_HOST || '0.0.0.0';
|
|
15
|
+
const clientPort = Number(process.env.CLIENT_DEV_PORT || 8080);
|
|
16
|
+
const defaultViteWarmupFiles = [
|
|
17
|
+
'./client/src/*.tsx',
|
|
18
|
+
'./client/src/*.ts',
|
|
19
|
+
'./client/src/**/*.css',
|
|
20
|
+
].join(',');
|
|
21
|
+
const viteWarmupFiles =
|
|
22
|
+
process.env.MIAODA_VITE_WARMUP_FILES ?? defaultViteWarmupFiles;
|
|
23
|
+
const runtimeStartedAtEpochMs = Date.now();
|
|
24
|
+
const startedAt = process.hrtime.bigint();
|
|
25
|
+
let stopping = false;
|
|
26
|
+
let child;
|
|
27
|
+
|
|
28
|
+
function durationMs(since = startedAt) {
|
|
29
|
+
return Number(process.hrtime.bigint() - since) / 1_000_000;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function emit(phase, since, details = {}) {
|
|
33
|
+
const atEpochMs = Date.now();
|
|
34
|
+
const phaseDurationMs = Number(durationMs(since).toFixed(3));
|
|
35
|
+
process.stdout.write(
|
|
36
|
+
`${JSON.stringify({
|
|
37
|
+
event: 'miaoda_workspace_runtime_phase',
|
|
38
|
+
runtime: 'vite',
|
|
39
|
+
phase,
|
|
40
|
+
atEpochMs,
|
|
41
|
+
phaseStartedAtEpochMs: Number((atEpochMs - phaseDurationMs).toFixed(3)),
|
|
42
|
+
runtimeStartedAtEpochMs,
|
|
43
|
+
durationMs: phaseDurationMs,
|
|
44
|
+
...details,
|
|
45
|
+
})}\n`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function delay(ms) {
|
|
50
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function workspaceDependenciesReady() {
|
|
54
|
+
try {
|
|
55
|
+
return (
|
|
56
|
+
fs.statSync(workspaceReadyFile).isFile() &&
|
|
57
|
+
fs.statSync(path.join(projectRoot, 'node_modules')).isDirectory()
|
|
58
|
+
);
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function waitForDependencies() {
|
|
65
|
+
const waitStartedAt = process.hrtime.bigint();
|
|
66
|
+
while (!stopping && !workspaceDependenciesReady()) await delay(100);
|
|
67
|
+
if (stopping) throw new Error('MIAODA_WORKSPACE_CLIENT_STOPPING');
|
|
68
|
+
emit('dependency_wait', waitStartedAt, { workspaceReadyFile });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function probeHost(host) {
|
|
72
|
+
if (!host || host === '0.0.0.0') return '127.0.0.1';
|
|
73
|
+
if (host === '::' || host === '[::]') return '::1';
|
|
74
|
+
return host.replace(/^\[|\]$/g, '');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function waitForPort() {
|
|
78
|
+
const listenStartedAt = process.hrtime.bigint();
|
|
79
|
+
const deadline = Date.now() + 60_000;
|
|
80
|
+
const host = probeHost(clientHost);
|
|
81
|
+
while (!stopping && Date.now() < deadline) {
|
|
82
|
+
if (child && (child.exitCode !== null || child.signalCode !== null)) {
|
|
83
|
+
throw new Error('MIAODA_WORKSPACE_VITE_EXITED_BEFORE_READY');
|
|
84
|
+
}
|
|
85
|
+
const connected = await new Promise(resolve => {
|
|
86
|
+
const socket = net.createConnection({ host, port: clientPort });
|
|
87
|
+
socket.once('connect', () => {
|
|
88
|
+
socket.destroy();
|
|
89
|
+
resolve(true);
|
|
90
|
+
});
|
|
91
|
+
socket.once('error', () => resolve(false));
|
|
92
|
+
});
|
|
93
|
+
if (connected) {
|
|
94
|
+
// Public Vite readiness is intentionally independent from Nest. The
|
|
95
|
+
// Vite /dev/health endpoint still checks both processes before the
|
|
96
|
+
// control plane publishes the preview URL.
|
|
97
|
+
emit('listen_ready', listenStartedAt, {
|
|
98
|
+
host: clientHost,
|
|
99
|
+
port: clientPort,
|
|
100
|
+
readinessScope: 'vite-only',
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
await delay(100);
|
|
105
|
+
}
|
|
106
|
+
throw new Error('MIAODA_WORKSPACE_VITE_READY_TIMEOUT');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function terminate(signal = 'SIGTERM') {
|
|
110
|
+
if (!child?.pid) return;
|
|
111
|
+
try {
|
|
112
|
+
process.kill(-child.pid, signal);
|
|
113
|
+
} catch {
|
|
114
|
+
try {
|
|
115
|
+
child.kill(signal);
|
|
116
|
+
} catch {}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function close(signal) {
|
|
121
|
+
if (stopping) return;
|
|
122
|
+
stopping = true;
|
|
123
|
+
terminate(signal);
|
|
124
|
+
setTimeout(() => terminate('SIGKILL'), 1_000).unref();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
|
|
128
|
+
process.once(signal, () => void close(signal));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
!Number.isSafeInteger(clientPort) ||
|
|
133
|
+
clientPort <= 0 ||
|
|
134
|
+
clientPort > 65_535
|
|
135
|
+
) {
|
|
136
|
+
throw new Error('MIAODA_WORKSPACE_VITE_PORT_INVALID');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await waitForDependencies();
|
|
140
|
+
const spawnStartedAt = process.hrtime.bigint();
|
|
141
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
142
|
+
child = spawn(npmCommand, ['run', 'dev:client'], {
|
|
143
|
+
cwd: projectRoot,
|
|
144
|
+
detached: true,
|
|
145
|
+
stdio: 'inherit',
|
|
146
|
+
env: {
|
|
147
|
+
...process.env,
|
|
148
|
+
MIAODA_CACHE_BOOTSTRAP_ENABLED: 'false',
|
|
149
|
+
MIAODA_VITE_WARMUP_FILES: viteWarmupFiles,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
emit('process_spawn', spawnStartedAt, {
|
|
153
|
+
pid: child.pid,
|
|
154
|
+
warmupFiles: viteWarmupFiles,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const exitPromise = new Promise((resolve, reject) => {
|
|
158
|
+
child.once('error', reject);
|
|
159
|
+
child.once('exit', (code, signal) => resolve({ code, signal }));
|
|
160
|
+
});
|
|
161
|
+
await Promise.race([
|
|
162
|
+
waitForPort(),
|
|
163
|
+
exitPromise.then(exit => {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`MIAODA_WORKSPACE_VITE_EXITED_BEFORE_READY: code=${exit.code} signal=${exit.signal}`
|
|
166
|
+
);
|
|
167
|
+
}),
|
|
168
|
+
]);
|
|
169
|
+
emit('startup', startedAt, {
|
|
170
|
+
pid: child.pid,
|
|
171
|
+
port: clientPort,
|
|
172
|
+
readinessScope: 'vite-only',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const exit = await exitPromise;
|
|
176
|
+
process.exit(exit.code ?? (stopping ? 0 : 1));
|