@lark-apaas/fullstack-cli 1.1.66-alpha.20260904133055 → 1.1.66-alpha.20260904154137
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 +1 -1
- package/templates/scripts/dev.js +34 -100
package/package.json
CHANGED
package/templates/scripts/dev.js
CHANGED
|
@@ -32,10 +32,9 @@ loadEnv();
|
|
|
32
32
|
|
|
33
33
|
// ── Configuration ─────────────────────────────────────────────────────────────
|
|
34
34
|
const LOG_DIR = process.env.LOG_DIR || 'logs';
|
|
35
|
-
const MAX_RESTART_COUNT =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
: Infinity;
|
|
35
|
+
const MAX_RESTART_COUNT = process.env.MAX_RESTART_COUNT != null && process.env.MAX_RESTART_COUNT !== ''
|
|
36
|
+
? parseInt(process.env.MAX_RESTART_COUNT, 10)
|
|
37
|
+
: Infinity;
|
|
39
38
|
const RESTART_DELAY = parseInt(process.env.RESTART_DELAY, 10) || 2;
|
|
40
39
|
const MAX_DELAY = 8;
|
|
41
40
|
const SERVER_PORT = process.env.SERVER_PORT || '3000';
|
|
@@ -52,16 +51,11 @@ const devLogFd = fs.openSync(devLogPath, 'a');
|
|
|
52
51
|
function timestamp() {
|
|
53
52
|
const now = new Date();
|
|
54
53
|
return (
|
|
55
|
-
now.getFullYear() +
|
|
56
|
-
'-' +
|
|
57
|
-
String(now.
|
|
58
|
-
'
|
|
59
|
-
String(now.
|
|
60
|
-
' ' +
|
|
61
|
-
String(now.getHours()).padStart(2, '0') +
|
|
62
|
-
':' +
|
|
63
|
-
String(now.getMinutes()).padStart(2, '0') +
|
|
64
|
-
':' +
|
|
54
|
+
now.getFullYear() + '-' +
|
|
55
|
+
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
|
56
|
+
String(now.getDate()).padStart(2, '0') + ' ' +
|
|
57
|
+
String(now.getHours()).padStart(2, '0') + ':' +
|
|
58
|
+
String(now.getMinutes()).padStart(2, '0') + ':' +
|
|
65
59
|
String(now.getSeconds()).padStart(2, '0')
|
|
66
60
|
);
|
|
67
61
|
}
|
|
@@ -75,33 +69,21 @@ const STDOUT_MAX_INFLIGHT = 1000;
|
|
|
75
69
|
function writeOutput(msg) {
|
|
76
70
|
// File first and synchronously — read-logs reads this file; it must never be gated
|
|
77
71
|
// by the terminal consumer.
|
|
78
|
-
try {
|
|
79
|
-
fs.writeSync(devStdLogFd, msg);
|
|
80
|
-
} catch {}
|
|
72
|
+
try { fs.writeSync(devStdLogFd, msg); } catch {}
|
|
81
73
|
// stdout mirror via async fs.write: if process.stdout is a pty/pipe whose consumer
|
|
82
74
|
// stalls, the block happens on the libuv threadpool, NOT the event loop — so the
|
|
83
75
|
// synchronous log-FILE writes above (and subsequent readline callbacks) keep running.
|
|
84
76
|
// Drop overflow when too many writes are already pending (best-effort mirror).
|
|
85
|
-
if (_stdoutInFlight >= STDOUT_MAX_INFLIGHT) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
77
|
+
if (_stdoutInFlight >= STDOUT_MAX_INFLIGHT) { return; }
|
|
88
78
|
_stdoutInFlight++;
|
|
89
|
-
try {
|
|
90
|
-
fs.write(1, msg, () => {
|
|
91
|
-
_stdoutInFlight--;
|
|
92
|
-
});
|
|
93
|
-
} catch {
|
|
94
|
-
_stdoutInFlight--;
|
|
95
|
-
}
|
|
79
|
+
try { fs.write(1, msg, () => { _stdoutInFlight--; }); } catch { _stdoutInFlight--; }
|
|
96
80
|
}
|
|
97
81
|
|
|
98
82
|
/** Structured event log → terminal + dev.std.log + dev.log */
|
|
99
83
|
function logEvent(level, name, message) {
|
|
100
84
|
const msg = `[${timestamp()}] [${level}] [${name}] ${message}\n`;
|
|
101
85
|
writeOutput(msg);
|
|
102
|
-
try {
|
|
103
|
-
fs.writeSync(devLogFd, msg);
|
|
104
|
-
} catch {}
|
|
86
|
+
try { fs.writeSync(devLogFd, msg); } catch {}
|
|
105
87
|
}
|
|
106
88
|
|
|
107
89
|
// ── Process group management ──────────────────────────────────────────────────
|
|
@@ -113,16 +95,11 @@ function killProcessGroup(pid, signal) {
|
|
|
113
95
|
|
|
114
96
|
function killOrphansByPort(port) {
|
|
115
97
|
try {
|
|
116
|
-
const pids = execSync(`lsof -ti :${port}`, {
|
|
117
|
-
encoding: 'utf8',
|
|
118
|
-
timeout: 5000,
|
|
119
|
-
}).trim();
|
|
98
|
+
const pids = execSync(`lsof -ti :${port}`, { encoding: 'utf8', timeout: 5000 }).trim();
|
|
120
99
|
if (pids) {
|
|
121
100
|
const pidList = pids.split('\n').filter(Boolean);
|
|
122
101
|
for (const p of pidList) {
|
|
123
|
-
try {
|
|
124
|
-
process.kill(parseInt(p, 10), 'SIGKILL');
|
|
125
|
-
} catch {}
|
|
102
|
+
try { process.kill(parseInt(p, 10), 'SIGKILL'); } catch {}
|
|
126
103
|
}
|
|
127
104
|
return pidList;
|
|
128
105
|
}
|
|
@@ -135,7 +112,7 @@ let stopping = false;
|
|
|
135
112
|
const managedProcesses = []; // { name, pid, child }
|
|
136
113
|
|
|
137
114
|
function sleep(ms) {
|
|
138
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
115
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
139
116
|
}
|
|
140
117
|
|
|
141
118
|
/**
|
|
@@ -165,23 +142,14 @@ function startProcess({ name, command, args, cleanupPort }) {
|
|
|
165
142
|
entry.child = child;
|
|
166
143
|
|
|
167
144
|
const startTime = Date.now();
|
|
168
|
-
logEvent(
|
|
169
|
-
'INFO',
|
|
170
|
-
name,
|
|
171
|
-
`Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`
|
|
172
|
-
);
|
|
145
|
+
logEvent('INFO', name, `Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`);
|
|
173
146
|
|
|
174
147
|
// Pipe stdout and stderr through readline for timestamped logging
|
|
175
|
-
const pipeLines = stream => {
|
|
176
|
-
const rl = readline.createInterface({
|
|
177
|
-
|
|
178
|
-
crlfDelay: Infinity,
|
|
179
|
-
});
|
|
180
|
-
rl.on('line', line => {
|
|
148
|
+
const pipeLines = (stream) => {
|
|
149
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
150
|
+
rl.on('line', (line) => {
|
|
181
151
|
const msg = `[${timestamp()}] [${name}] ${line}\n`;
|
|
182
|
-
try {
|
|
183
|
-
fs.writeSync(logFd, msg);
|
|
184
|
-
} catch {}
|
|
152
|
+
try { fs.writeSync(logFd, msg); } catch {}
|
|
185
153
|
writeOutput(msg);
|
|
186
154
|
});
|
|
187
155
|
};
|
|
@@ -192,8 +160,8 @@ function startProcess({ name, command, args, cleanupPort }) {
|
|
|
192
160
|
// NOTE: must use 'exit', not 'close'. With shell:true, grandchild processes
|
|
193
161
|
// (e.g. nest's server) inherit stdout pipes. 'close' won't fire until ALL
|
|
194
162
|
// pipe holders exit, causing dev.js to hang when npm/nest dies but server survives.
|
|
195
|
-
const exitCode = await new Promise(resolve => {
|
|
196
|
-
child.on('exit', code => resolve(code ?? 1));
|
|
163
|
+
const exitCode = await new Promise((resolve) => {
|
|
164
|
+
child.on('exit', (code) => resolve(code ?? 1));
|
|
197
165
|
child.on('error', () => resolve(1));
|
|
198
166
|
});
|
|
199
167
|
|
|
@@ -210,11 +178,7 @@ function startProcess({ name, command, args, cleanupPort }) {
|
|
|
210
178
|
if (cleanupPort) {
|
|
211
179
|
const orphans = killOrphansByPort(cleanupPort);
|
|
212
180
|
if (orphans.length > 0) {
|
|
213
|
-
logEvent(
|
|
214
|
-
'WARN',
|
|
215
|
-
name,
|
|
216
|
-
`Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`
|
|
217
|
-
);
|
|
181
|
+
logEvent('WARN', name, `Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`);
|
|
218
182
|
await sleep(500);
|
|
219
183
|
}
|
|
220
184
|
}
|
|
@@ -224,38 +188,21 @@ function startProcess({ name, command, args, cleanupPort }) {
|
|
|
224
188
|
const runDuration = (Date.now() - startTime) / 1000;
|
|
225
189
|
if (runDuration >= 60) {
|
|
226
190
|
restartCount = 0;
|
|
227
|
-
logEvent(
|
|
228
|
-
'INFO',
|
|
229
|
-
name,
|
|
230
|
-
`Ran for ${Math.round(runDuration)}s, resetting restart counter`
|
|
231
|
-
);
|
|
191
|
+
logEvent('INFO', name, `Ran for ${Math.round(runDuration)}s, resetting restart counter`);
|
|
232
192
|
} else {
|
|
233
193
|
restartCount++;
|
|
234
194
|
}
|
|
235
195
|
if (restartCount >= MAX_RESTART_COUNT) {
|
|
236
|
-
logEvent(
|
|
237
|
-
'ERROR',
|
|
238
|
-
name,
|
|
239
|
-
`Max restart count (${MAX_RESTART_COUNT}) reached, giving up`
|
|
240
|
-
);
|
|
196
|
+
logEvent('ERROR', name, `Max restart count (${MAX_RESTART_COUNT}) reached, giving up`);
|
|
241
197
|
break;
|
|
242
198
|
}
|
|
243
199
|
|
|
244
|
-
const delay = Math.min(
|
|
245
|
-
|
|
246
|
-
MAX_DELAY
|
|
247
|
-
);
|
|
248
|
-
logEvent(
|
|
249
|
-
'WARN',
|
|
250
|
-
name,
|
|
251
|
-
`Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`
|
|
252
|
-
);
|
|
200
|
+
const delay = Math.min(RESTART_DELAY * (1 << Math.max(0, restartCount - 1)), MAX_DELAY);
|
|
201
|
+
logEvent('WARN', name, `Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`);
|
|
253
202
|
await sleep(delay * 1000);
|
|
254
203
|
}
|
|
255
204
|
|
|
256
|
-
try {
|
|
257
|
-
fs.closeSync(logFd);
|
|
258
|
-
} catch {}
|
|
205
|
+
try { fs.closeSync(logFd); } catch {}
|
|
259
206
|
};
|
|
260
207
|
|
|
261
208
|
return run();
|
|
@@ -285,11 +232,7 @@ async function cleanup() {
|
|
|
285
232
|
// Force kill any remaining
|
|
286
233
|
for (const entry of managedProcesses) {
|
|
287
234
|
if (entry.pid) {
|
|
288
|
-
logEvent(
|
|
289
|
-
'WARN',
|
|
290
|
-
'main',
|
|
291
|
-
`Force killing process group (PGID: ${entry.pid})`
|
|
292
|
-
);
|
|
235
|
+
logEvent('WARN', 'main', `Force killing process group (PGID: ${entry.pid})`);
|
|
293
236
|
killProcessGroup(entry.pid, 'SIGKILL');
|
|
294
237
|
}
|
|
295
238
|
}
|
|
@@ -300,12 +243,8 @@ async function cleanup() {
|
|
|
300
243
|
|
|
301
244
|
logEvent('INFO', 'main', 'All processes stopped');
|
|
302
245
|
|
|
303
|
-
try {
|
|
304
|
-
|
|
305
|
-
} catch {}
|
|
306
|
-
try {
|
|
307
|
-
fs.closeSync(devLogFd);
|
|
308
|
-
} catch {}
|
|
246
|
+
try { fs.closeSync(devStdLogFd); } catch {}
|
|
247
|
+
try { fs.closeSync(devLogFd); } catch {}
|
|
309
248
|
|
|
310
249
|
process.exit(0);
|
|
311
250
|
}
|
|
@@ -332,15 +271,10 @@ async function main() {
|
|
|
332
271
|
// Initialize action plugins
|
|
333
272
|
writeOutput('\n🔌 Initializing action plugins...\n');
|
|
334
273
|
try {
|
|
335
|
-
execSync('fullstack-cli action-plugin init', {
|
|
336
|
-
cwd: PROJECT_ROOT,
|
|
337
|
-
stdio: 'inherit',
|
|
338
|
-
});
|
|
274
|
+
execSync('fullstack-cli action-plugin init', { cwd: PROJECT_ROOT, stdio: 'inherit' });
|
|
339
275
|
writeOutput('✅ Action plugins initialized\n\n');
|
|
340
276
|
} catch {
|
|
341
|
-
writeOutput(
|
|
342
|
-
'⚠️ Action plugin initialization failed, continuing anyway...\n\n'
|
|
343
|
-
);
|
|
277
|
+
writeOutput('⚠️ Action plugin initialization failed, continuing anyway...\n\n');
|
|
344
278
|
}
|
|
345
279
|
|
|
346
280
|
// Start server and client
|
|
@@ -369,7 +303,7 @@ async function main() {
|
|
|
369
303
|
}
|
|
370
304
|
}
|
|
371
305
|
|
|
372
|
-
main().catch(err => {
|
|
306
|
+
main().catch((err) => {
|
|
373
307
|
console.error('Fatal error:', err);
|
|
374
308
|
process.exit(1);
|
|
375
309
|
});
|