@lark-apaas/coding-template-nestjs-react-fullstack 0.1.36-alpha.20260904132704 → 0.1.36-alpha.20260904234124
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/template/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"dev": "./scripts/dev.sh",
|
|
13
|
-
"dev:server": "NODE_ENV=development nest
|
|
13
|
+
"dev:server": "NODE_ENV=development node scripts/nest-type-check.cjs",
|
|
14
14
|
"dev:client": "NODE_ENV=development vite --config vite.config.ts",
|
|
15
15
|
"gen:db-schema": "npx -y @lark-apaas/db-schema-sync@latest --output server/database/schema.ts --export-custom-types",
|
|
16
16
|
"gen:openapi": "echo 'UNSUPPORTED, SKIP'",
|
package/template/scripts/dev.js
CHANGED
|
@@ -5,7 +5,6 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { spawn, execSync } = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
|
-
const { buildNestServerArgs } = require('./nest-type-check.cjs');
|
|
9
8
|
|
|
10
9
|
// ── Project root ──────────────────────────────────────────────────────────────
|
|
11
10
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
@@ -32,10 +31,9 @@ loadEnv();
|
|
|
32
31
|
|
|
33
32
|
// ── Configuration ─────────────────────────────────────────────────────────────
|
|
34
33
|
const LOG_DIR = process.env.LOG_DIR || 'logs';
|
|
35
|
-
const MAX_RESTART_COUNT =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
: Infinity;
|
|
34
|
+
const MAX_RESTART_COUNT = process.env.MAX_RESTART_COUNT != null && process.env.MAX_RESTART_COUNT !== ''
|
|
35
|
+
? parseInt(process.env.MAX_RESTART_COUNT, 10)
|
|
36
|
+
: Infinity;
|
|
39
37
|
const RESTART_DELAY = parseInt(process.env.RESTART_DELAY, 10) || 2;
|
|
40
38
|
const MAX_DELAY = 8;
|
|
41
39
|
const SERVER_PORT = process.env.SERVER_PORT || '3000';
|
|
@@ -52,37 +50,26 @@ const devLogFd = fs.openSync(devLogPath, 'a');
|
|
|
52
50
|
function timestamp() {
|
|
53
51
|
const now = new Date();
|
|
54
52
|
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
|
-
':' +
|
|
53
|
+
now.getFullYear() + '-' +
|
|
54
|
+
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
|
55
|
+
String(now.getDate()).padStart(2, '0') + ' ' +
|
|
56
|
+
String(now.getHours()).padStart(2, '0') + ':' +
|
|
57
|
+
String(now.getMinutes()).padStart(2, '0') + ':' +
|
|
65
58
|
String(now.getSeconds()).padStart(2, '0')
|
|
66
59
|
);
|
|
67
60
|
}
|
|
68
61
|
|
|
69
62
|
/** Write to terminal + dev.std.log */
|
|
70
63
|
function writeOutput(msg) {
|
|
71
|
-
try {
|
|
72
|
-
|
|
73
|
-
} catch {}
|
|
74
|
-
try {
|
|
75
|
-
fs.writeSync(devStdLogFd, msg);
|
|
76
|
-
} catch {}
|
|
64
|
+
try { process.stdout.write(msg); } catch {}
|
|
65
|
+
try { fs.writeSync(devStdLogFd, msg); } catch {}
|
|
77
66
|
}
|
|
78
67
|
|
|
79
68
|
/** Structured event log → terminal + dev.std.log + dev.log */
|
|
80
69
|
function logEvent(level, name, message) {
|
|
81
70
|
const msg = `[${timestamp()}] [${level}] [${name}] ${message}\n`;
|
|
82
71
|
writeOutput(msg);
|
|
83
|
-
try {
|
|
84
|
-
fs.writeSync(devLogFd, msg);
|
|
85
|
-
} catch {}
|
|
72
|
+
try { fs.writeSync(devLogFd, msg); } catch {}
|
|
86
73
|
}
|
|
87
74
|
|
|
88
75
|
// ── Process group management ──────────────────────────────────────────────────
|
|
@@ -94,16 +81,11 @@ function killProcessGroup(pid, signal) {
|
|
|
94
81
|
|
|
95
82
|
function killOrphansByPort(port) {
|
|
96
83
|
try {
|
|
97
|
-
const pids = execSync(`lsof -ti :${port}`, {
|
|
98
|
-
encoding: 'utf8',
|
|
99
|
-
timeout: 5000,
|
|
100
|
-
}).trim();
|
|
84
|
+
const pids = execSync(`lsof -ti :${port}`, { encoding: 'utf8', timeout: 5000 }).trim();
|
|
101
85
|
if (pids) {
|
|
102
86
|
const pidList = pids.split('\n').filter(Boolean);
|
|
103
87
|
for (const p of pidList) {
|
|
104
|
-
try {
|
|
105
|
-
process.kill(parseInt(p, 10), 'SIGKILL');
|
|
106
|
-
} catch {}
|
|
88
|
+
try { process.kill(parseInt(p, 10), 'SIGKILL'); } catch {}
|
|
107
89
|
}
|
|
108
90
|
return pidList;
|
|
109
91
|
}
|
|
@@ -146,23 +128,14 @@ function startProcess({ name, command, args, cleanupPort }) {
|
|
|
146
128
|
entry.child = child;
|
|
147
129
|
|
|
148
130
|
const startTime = Date.now();
|
|
149
|
-
logEvent(
|
|
150
|
-
'INFO',
|
|
151
|
-
name,
|
|
152
|
-
`Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`,
|
|
153
|
-
);
|
|
131
|
+
logEvent('INFO', name, `Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`);
|
|
154
132
|
|
|
155
133
|
// Pipe stdout and stderr through readline for timestamped logging
|
|
156
134
|
const pipeLines = (stream) => {
|
|
157
|
-
const rl = readline.createInterface({
|
|
158
|
-
input: stream,
|
|
159
|
-
crlfDelay: Infinity,
|
|
160
|
-
});
|
|
135
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
161
136
|
rl.on('line', (line) => {
|
|
162
137
|
const msg = `[${timestamp()}] [${name}] ${line}\n`;
|
|
163
|
-
try {
|
|
164
|
-
fs.writeSync(logFd, msg);
|
|
165
|
-
} catch {}
|
|
138
|
+
try { fs.writeSync(logFd, msg); } catch {}
|
|
166
139
|
writeOutput(msg);
|
|
167
140
|
});
|
|
168
141
|
};
|
|
@@ -191,11 +164,7 @@ function startProcess({ name, command, args, cleanupPort }) {
|
|
|
191
164
|
if (cleanupPort) {
|
|
192
165
|
const orphans = killOrphansByPort(cleanupPort);
|
|
193
166
|
if (orphans.length > 0) {
|
|
194
|
-
logEvent(
|
|
195
|
-
'WARN',
|
|
196
|
-
name,
|
|
197
|
-
`Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`,
|
|
198
|
-
);
|
|
167
|
+
logEvent('WARN', name, `Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`);
|
|
199
168
|
await sleep(500);
|
|
200
169
|
}
|
|
201
170
|
}
|
|
@@ -205,38 +174,21 @@ function startProcess({ name, command, args, cleanupPort }) {
|
|
|
205
174
|
const runDuration = (Date.now() - startTime) / 1000;
|
|
206
175
|
if (runDuration >= 60) {
|
|
207
176
|
restartCount = 0;
|
|
208
|
-
logEvent(
|
|
209
|
-
'INFO',
|
|
210
|
-
name,
|
|
211
|
-
`Ran for ${Math.round(runDuration)}s, resetting restart counter`,
|
|
212
|
-
);
|
|
177
|
+
logEvent('INFO', name, `Ran for ${Math.round(runDuration)}s, resetting restart counter`);
|
|
213
178
|
} else {
|
|
214
179
|
restartCount++;
|
|
215
180
|
}
|
|
216
181
|
if (restartCount >= MAX_RESTART_COUNT) {
|
|
217
|
-
logEvent(
|
|
218
|
-
'ERROR',
|
|
219
|
-
name,
|
|
220
|
-
`Max restart count (${MAX_RESTART_COUNT}) reached, giving up`,
|
|
221
|
-
);
|
|
182
|
+
logEvent('ERROR', name, `Max restart count (${MAX_RESTART_COUNT}) reached, giving up`);
|
|
222
183
|
break;
|
|
223
184
|
}
|
|
224
185
|
|
|
225
|
-
const delay = Math.min(
|
|
226
|
-
|
|
227
|
-
MAX_DELAY,
|
|
228
|
-
);
|
|
229
|
-
logEvent(
|
|
230
|
-
'WARN',
|
|
231
|
-
name,
|
|
232
|
-
`Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`,
|
|
233
|
-
);
|
|
186
|
+
const delay = Math.min(RESTART_DELAY * (1 << Math.max(0, restartCount - 1)), MAX_DELAY);
|
|
187
|
+
logEvent('WARN', name, `Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`);
|
|
234
188
|
await sleep(delay * 1000);
|
|
235
189
|
}
|
|
236
190
|
|
|
237
|
-
try {
|
|
238
|
-
fs.closeSync(logFd);
|
|
239
|
-
} catch {}
|
|
191
|
+
try { fs.closeSync(logFd); } catch {}
|
|
240
192
|
};
|
|
241
193
|
|
|
242
194
|
return run();
|
|
@@ -266,11 +218,7 @@ async function cleanup() {
|
|
|
266
218
|
// Force kill any remaining
|
|
267
219
|
for (const entry of managedProcesses) {
|
|
268
220
|
if (entry.pid) {
|
|
269
|
-
logEvent(
|
|
270
|
-
'WARN',
|
|
271
|
-
'main',
|
|
272
|
-
`Force killing process group (PGID: ${entry.pid})`,
|
|
273
|
-
);
|
|
221
|
+
logEvent('WARN', 'main', `Force killing process group (PGID: ${entry.pid})`);
|
|
274
222
|
killProcessGroup(entry.pid, 'SIGKILL');
|
|
275
223
|
}
|
|
276
224
|
}
|
|
@@ -281,12 +229,8 @@ async function cleanup() {
|
|
|
281
229
|
|
|
282
230
|
logEvent('INFO', 'main', 'All processes stopped');
|
|
283
231
|
|
|
284
|
-
try {
|
|
285
|
-
|
|
286
|
-
} catch {}
|
|
287
|
-
try {
|
|
288
|
-
fs.closeSync(devLogFd);
|
|
289
|
-
} catch {}
|
|
232
|
+
try { fs.closeSync(devStdLogFd); } catch {}
|
|
233
|
+
try { fs.closeSync(devLogFd); } catch {}
|
|
290
234
|
|
|
291
235
|
process.exit(0);
|
|
292
236
|
}
|
|
@@ -313,22 +257,17 @@ async function main() {
|
|
|
313
257
|
// Initialize action plugins
|
|
314
258
|
writeOutput('\n🔌 Initializing action plugins...\n');
|
|
315
259
|
try {
|
|
316
|
-
execSync('fullstack-cli action-plugin init', {
|
|
317
|
-
cwd: PROJECT_ROOT,
|
|
318
|
-
stdio: 'inherit',
|
|
319
|
-
});
|
|
260
|
+
execSync('fullstack-cli action-plugin init', { cwd: PROJECT_ROOT, stdio: 'inherit' });
|
|
320
261
|
writeOutput('✅ Action plugins initialized\n\n');
|
|
321
262
|
} catch {
|
|
322
|
-
writeOutput(
|
|
323
|
-
'⚠️ Action plugin initialization failed, continuing anyway...\n\n',
|
|
324
|
-
);
|
|
263
|
+
writeOutput('⚠️ Action plugin initialization failed, continuing anyway...\n\n');
|
|
325
264
|
}
|
|
326
265
|
|
|
327
266
|
// Start server and client
|
|
328
267
|
const serverPromise = startProcess({
|
|
329
268
|
name: 'server',
|
|
330
269
|
command: 'npm',
|
|
331
|
-
args:
|
|
270
|
+
args: ['run', 'dev:server'],
|
|
332
271
|
cleanupPort: SERVER_PORT,
|
|
333
272
|
});
|
|
334
273
|
|
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
5
6
|
|
|
6
7
|
const TYPE_CHECK_ENV = 'MIAODA_NEST_TYPE_CHECK_ENABLED';
|
|
7
8
|
const DISABLED_CONFIG_RELATIVE_PATH = 'logs/nest-cli.type-check-disabled.json';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
* Build the
|
|
11
|
+
* Build the Nest CLI arguments used to launch the development server.
|
|
11
12
|
*
|
|
12
13
|
* Missing or malformed values deliberately preserve the project's existing
|
|
13
14
|
* nest-cli.json behavior. The platform injects an exact true/false value;
|
|
14
15
|
* exact true forces the checker on, while exact false writes a narrow runtime
|
|
15
16
|
* config override so the checked-in project config is never mutated.
|
|
16
17
|
*/
|
|
17
|
-
function
|
|
18
|
+
function buildNestCliArgs({ projectRoot, env = process.env }) {
|
|
18
19
|
const enabled = env[TYPE_CHECK_ENV];
|
|
19
20
|
if (enabled === 'true') {
|
|
20
|
-
return ['
|
|
21
|
+
return ['start', '--watch', '--type-check'];
|
|
21
22
|
}
|
|
22
23
|
if (enabled !== 'false') {
|
|
23
|
-
return ['
|
|
24
|
+
return ['start', '--watch'];
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
const sourcePath = path.join(projectRoot, 'nest-cli.json');
|
|
@@ -47,7 +48,38 @@ function buildNestServerArgs({ projectRoot, env = process.env }) {
|
|
|
47
48
|
);
|
|
48
49
|
fs.renameSync(temporaryPath, runtimePath);
|
|
49
50
|
|
|
50
|
-
return ['
|
|
51
|
+
return ['start', '--watch', '--config', DISABLED_CONFIG_RELATIVE_PATH];
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
function runNestServer({ projectRoot = path.resolve(__dirname, '..'), env = process.env } = {}) {
|
|
55
|
+
const executable = path.join(
|
|
56
|
+
projectRoot,
|
|
57
|
+
'node_modules',
|
|
58
|
+
'.bin',
|
|
59
|
+
process.platform === 'win32' ? 'nest.cmd' : 'nest',
|
|
60
|
+
);
|
|
61
|
+
const child = spawn(executable, buildNestCliArgs({ projectRoot, env }), {
|
|
62
|
+
cwd: projectRoot,
|
|
63
|
+
env,
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
child.once('error', (error) => {
|
|
68
|
+
console.error(`[nest-type-check] failed to start Nest CLI: ${error.message}`);
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
});
|
|
71
|
+
child.once('exit', (code, signal) => {
|
|
72
|
+
if (signal) {
|
|
73
|
+
process.kill(process.pid, signal);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
process.exitCode = code ?? 1;
|
|
77
|
+
});
|
|
78
|
+
return child;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (require.main === module) {
|
|
82
|
+
runNestServer();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = { buildNestCliArgs, runNestServer };
|