@titanpl/packet 1.0.0 → 1.4.0
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/README.md +11 -0
- package/index.js +30 -9
- package/js/titan/builder.js +7 -30
- package/js/titan/bundle.js +25 -122
- package/js/titan/dev.js +113 -26
- package/js/titan/error-box.js +2 -2
- package/package.json +7 -2
- package/ts/titan/builder.js +66 -113
- package/ts/titan/bundle.js +227 -123
- package/ts/titan/dev.js +102 -381
- package/ts/titan/error-box.js +277 -0
- package/js/titan/titan.js +0 -129
- package/ts/titan/titan.d.ts +0 -17
- package/ts/titan/titan.js +0 -124
package/ts/titan/dev.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import chokidar from "chokidar";
|
|
2
2
|
import { spawn, execSync } from "child_process";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
4
|
import fs from "fs";
|
|
6
|
-
import
|
|
5
|
+
import { bundle } from "./bundle.js";
|
|
6
|
+
import { buildMetadata } from "./builder.js";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
|
+
import os from "os";
|
|
8
9
|
|
|
9
|
-
// Required for __dirname in ES modules
|
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
const __dirname = path.dirname(__filename);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// Colors
|
|
15
10
|
const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
|
|
16
11
|
const green = (t) => `\x1b[32m${t}\x1b[0m`;
|
|
17
12
|
const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
|
|
@@ -22,371 +17,102 @@ const bold = (t) => `\x1b[1m${t}\x1b[0m`;
|
|
|
22
17
|
function getTitanVersion() {
|
|
23
18
|
try {
|
|
24
19
|
const require = createRequire(import.meta.url);
|
|
25
|
-
const pkgPath = require.resolve("@
|
|
20
|
+
const pkgPath = require.resolve("@titanpl/cli/package.json");
|
|
26
21
|
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
27
22
|
} catch (e) {
|
|
28
|
-
|
|
29
|
-
// Check levels up to find the framework root
|
|
30
|
-
let cur = __dirname;
|
|
31
|
-
for (let i = 0; i < 5; i++) {
|
|
32
|
-
const pkgPath = path.join(cur, "package.json");
|
|
33
|
-
if (fs.existsSync(pkgPath)) {
|
|
34
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
35
|
-
if (pkg.name === "@ezetgalaxy/titan") return pkg.version;
|
|
36
|
-
}
|
|
37
|
-
cur = path.join(cur, "..");
|
|
38
|
-
}
|
|
39
|
-
} catch (e2) { }
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
// Fallback to calling tit --version
|
|
43
|
-
const output = execSync("tit --version", { encoding: "utf-8" }).trim();
|
|
44
|
-
const match = output.match(/v(\d+\.\d+\.\d+)/);
|
|
45
|
-
if (match) return match[1];
|
|
46
|
-
} catch (e3) { }
|
|
23
|
+
return "1.0.0";
|
|
47
24
|
}
|
|
48
|
-
return "0.1.0";
|
|
49
25
|
}
|
|
50
26
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
if (process.platform === "win32") {
|
|
66
|
-
try {
|
|
67
|
-
execSync(`taskkill /pid ${pid} /f /t`, { stdio: 'ignore' });
|
|
68
|
-
} catch (e) {
|
|
69
|
-
// Ignore errors if process is already dead
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
serverProcess.kill();
|
|
27
|
+
function getEngineBinaryPath(root) {
|
|
28
|
+
const platform = os.platform();
|
|
29
|
+
const arch = os.arch();
|
|
30
|
+
const binName = platform === 'win32' ? 'titan-server.exe' : 'titan-server';
|
|
31
|
+
const pkgName = `@titanpl/engine-${platform}-${arch}`;
|
|
32
|
+
|
|
33
|
+
// Monorepo search
|
|
34
|
+
let current = root;
|
|
35
|
+
for (let i = 0; i < 5; i++) {
|
|
36
|
+
const potential = path.join(current, 'engine', 'target', 'release', binName);
|
|
37
|
+
if (fs.existsSync(potential)) return potential;
|
|
38
|
+
current = path.dirname(current);
|
|
73
39
|
}
|
|
74
40
|
|
|
41
|
+
// Node modules search
|
|
75
42
|
try {
|
|
76
|
-
|
|
43
|
+
const require = createRequire(import.meta.url);
|
|
44
|
+
const pkgPath = require.resolve(`${pkgName}/package.json`);
|
|
45
|
+
const binPath = path.join(path.dirname(pkgPath), 'bin', binName);
|
|
46
|
+
if (fs.existsSync(binPath)) return binPath;
|
|
77
47
|
} catch (e) { }
|
|
78
|
-
serverProcess = null;
|
|
79
|
-
isKilling = false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const delay = (ms) => new Promise(res => setTimeout(res, ms));
|
|
83
|
-
|
|
84
|
-
let spinnerTimer = null;
|
|
85
|
-
const frames = ["⏣", "⟐", "⟡", "⟠", "⟡", "⟐"];
|
|
86
|
-
let frameIdx = 0;
|
|
87
|
-
|
|
88
|
-
function startSpinner(text) {
|
|
89
|
-
if (spinnerTimer) clearInterval(spinnerTimer);
|
|
90
|
-
process.stdout.write("\x1B[?25l"); // Hide cursor
|
|
91
|
-
spinnerTimer = setInterval(() => {
|
|
92
|
-
process.stdout.write(`\r ${cyan(frames[frameIdx])} ${gray(text)}`);
|
|
93
|
-
frameIdx = (frameIdx + 1) % frames.length;
|
|
94
|
-
}, 80);
|
|
95
|
-
}
|
|
96
48
|
|
|
97
|
-
|
|
98
|
-
if (spinnerTimer) {
|
|
99
|
-
clearInterval(spinnerTimer);
|
|
100
|
-
spinnerTimer = null;
|
|
101
|
-
}
|
|
102
|
-
process.stdout.write("\r\x1B[K"); // Clear line
|
|
103
|
-
process.stdout.write("\x1B[?25h"); // Show cursor
|
|
104
|
-
if (text) {
|
|
105
|
-
if (success) {
|
|
106
|
-
console.log(` ${green("✔")} ${green(text)}`);
|
|
107
|
-
} else {
|
|
108
|
-
console.log(` ${red("✖")} ${red(text)}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
49
|
+
return null;
|
|
111
50
|
}
|
|
112
51
|
|
|
113
|
-
|
|
114
|
-
// If TS is broken, don't start
|
|
115
|
-
if (isTs && !isTsHealthy) {
|
|
116
|
-
stopSpinner(false, "Waiting for TypeScript errors to be fixed...");
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const waitTime = retryCount > 0 ? 1000 : 500;
|
|
121
|
-
|
|
122
|
-
await killServer();
|
|
123
|
-
await delay(waitTime);
|
|
124
|
-
|
|
125
|
-
const serverPath = path.join(process.cwd(), "server");
|
|
126
|
-
const startTime = Date.now();
|
|
127
|
-
|
|
128
|
-
startSpinner("Stabilizing your app on its orbit...");
|
|
129
|
-
|
|
130
|
-
let isReady = false;
|
|
131
|
-
let stdoutBuffer = "";
|
|
132
|
-
let buildLogs = "";
|
|
133
|
-
let stderrBuffer = "";
|
|
134
|
-
|
|
135
|
-
// If it takes more than 30s, update the message
|
|
136
|
-
const slowTimer = setTimeout(() => {
|
|
137
|
-
if (!isReady && !isKilling) {
|
|
138
|
-
startSpinner("Still stabilizing... (the first orbit takes longer)");
|
|
139
|
-
}
|
|
140
|
-
}, 30000);
|
|
141
|
-
|
|
142
|
-
serverProcess = spawn("cargo", ["run", "--quiet"], {
|
|
143
|
-
cwd: serverPath,
|
|
144
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
145
|
-
env: { ...process.env, CARGO_INCREMENTAL: "1" }
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
serverProcess.on("error", (err) => {
|
|
149
|
-
stopSpinner(false, "Failed to start orbit");
|
|
150
|
-
console.error(red(`[Titan] Error: ${err.message}`));
|
|
151
|
-
});
|
|
52
|
+
let serverProcess = null;
|
|
152
53
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (isReady) {
|
|
156
|
-
process.stderr.write(data);
|
|
157
|
-
} else {
|
|
158
|
-
buildLogs += str;
|
|
159
|
-
}
|
|
160
|
-
});
|
|
54
|
+
async function killServer() {
|
|
55
|
+
if (!serverProcess) return;
|
|
161
56
|
|
|
162
|
-
|
|
163
|
-
const
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const onExit = () => {
|
|
59
|
+
serverProcess = null;
|
|
60
|
+
resolve();
|
|
61
|
+
};
|
|
164
62
|
|
|
165
|
-
|
|
166
|
-
stdoutBuffer += out;
|
|
167
|
-
if (stdoutBuffer.includes("Titan server running") || stdoutBuffer.includes("████████╗")) {
|
|
168
|
-
isReady = true;
|
|
169
|
-
clearTimeout(slowTimer);
|
|
170
|
-
stopSpinner(true, "Your app is now orbiting Titan Planet");
|
|
63
|
+
serverProcess.on('exit', onExit);
|
|
171
64
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// On subsequent reloads, only print non-banner lines from the buffer
|
|
177
|
-
const lines = stdoutBuffer.split("\n");
|
|
178
|
-
for (const line of lines) {
|
|
179
|
-
const isBanner = line.includes("Titan server running") ||
|
|
180
|
-
line.includes("████████╗") ||
|
|
181
|
-
line.includes("╚══") ||
|
|
182
|
-
line.includes(" ██║") ||
|
|
183
|
-
line.includes(" ╚═╝");
|
|
184
|
-
if (!isBanner && line.trim()) {
|
|
185
|
-
process.stdout.write(line + "\n");
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
stdoutBuffer = "";
|
|
190
|
-
}
|
|
65
|
+
if (os.platform() === 'win32') {
|
|
66
|
+
try {
|
|
67
|
+
execSync(`taskkill /pid ${serverProcess.pid} /f /t`, { stdio: 'ignore' });
|
|
68
|
+
} catch (e) { }
|
|
191
69
|
} else {
|
|
192
|
-
|
|
70
|
+
serverProcess.kill('SIGKILL');
|
|
193
71
|
}
|
|
194
|
-
});
|
|
195
72
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
stderrBuffer += data.toString();
|
|
73
|
+
// Failsafe format
|
|
74
|
+
setTimeout(onExit, 500);
|
|
199
75
|
});
|
|
200
|
-
|
|
201
|
-
serverProcess.on("close", async (code) => {
|
|
202
|
-
clearTimeout(slowTimer);
|
|
203
|
-
if (isKilling) return;
|
|
204
|
-
const runTime = Date.now() - startTime;
|
|
205
|
-
|
|
206
|
-
if (code !== 0 && code !== null) {
|
|
207
|
-
// Check for port binding errors
|
|
208
|
-
const isPortError = stderrBuffer.includes("Address already in use") ||
|
|
209
|
-
stderrBuffer.includes("address in use") ||
|
|
210
|
-
stderrBuffer.includes("os error 10048") || // Windows
|
|
211
|
-
stderrBuffer.includes("EADDRINUSE") ||
|
|
212
|
-
stderrBuffer.includes("AddrInUse");
|
|
213
|
-
|
|
214
|
-
if (isPortError) {
|
|
215
|
-
if (retryCount < 3) {
|
|
216
|
-
// It's likely the previous process hasn't fully released the port
|
|
217
|
-
await delay(1000);
|
|
218
|
-
await startRustServer(retryCount + 1);
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
stopSpinner(false, "Orbit stabilization failed");
|
|
223
|
-
|
|
224
|
-
// Try to read intended port
|
|
225
|
-
let port = 3000;
|
|
226
|
-
try {
|
|
227
|
-
const routesConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), "server", "routes.json"), "utf8"));
|
|
228
|
-
if (routesConfig && routesConfig.__config && routesConfig.__config.port) {
|
|
229
|
-
port = routesConfig.__config.port;
|
|
230
|
-
}
|
|
231
|
-
} catch (e) { }
|
|
232
|
-
|
|
233
|
-
console.log("");
|
|
234
|
-
|
|
235
|
-
console.log(red("⏣ Your application cannot enter this orbit"));
|
|
236
|
-
console.log(red(`↳ Another application is already bound to port ${port}.`));
|
|
237
|
-
console.log("");
|
|
238
|
-
|
|
239
|
-
console.log(yellow("Recommended Actions:"));
|
|
240
|
-
console.log(yellow(" 1.") + " Release the occupied orbit (stop the other service).");
|
|
241
|
-
console.log(yellow(" 2.") + " Assign your application to a new orbit in " + cyan("app/app.js"));
|
|
242
|
-
console.log(yellow(" Example: ") + cyan(`t.start(${port + 1}, "Titan Running!")`));
|
|
243
|
-
console.log("");
|
|
244
|
-
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
stopSpinner(false, "Orbit stabilization failed");
|
|
250
|
-
|
|
251
|
-
// Debug: Show stderr if it's not empty and not a port error
|
|
252
|
-
if (stderrBuffer && stderrBuffer.trim()) {
|
|
253
|
-
console.log(gray("\n[Debug] Cargo stderr:"));
|
|
254
|
-
console.log(gray(stderrBuffer.substring(0, 500))); // Show first 500 chars
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (runTime < 15000 && retryCount < maxRetries) {
|
|
258
|
-
await delay(2000);
|
|
259
|
-
await startRustServer(retryCount + 1);
|
|
260
|
-
} else if (retryCount >= maxRetries) {
|
|
261
|
-
console.log(gray("\n[Titan] Waiting for changes to retry..."));
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async function rebuild() {
|
|
269
|
-
if (isTs && !isTsHealthy) return; // Don't rebuild if TS is broken
|
|
270
|
-
|
|
271
|
-
try {
|
|
272
|
-
const root = process.cwd();
|
|
273
|
-
const appTs = path.join(root, "app", "app.ts");
|
|
274
|
-
const dotTitan = path.join(root, ".titan");
|
|
275
|
-
const compiledApp = path.join(dotTitan, "app.js");
|
|
276
|
-
|
|
277
|
-
if (fs.existsSync(appTs)) {
|
|
278
|
-
if (!fs.existsSync(dotTitan)) fs.mkdirSync(dotTitan, { recursive: true });
|
|
279
|
-
|
|
280
|
-
await esbuild.build({
|
|
281
|
-
entryPoints: [appTs],
|
|
282
|
-
outfile: compiledApp,
|
|
283
|
-
bundle: true,
|
|
284
|
-
platform: "node",
|
|
285
|
-
format: "esm",
|
|
286
|
-
external: ["fs", "path", "esbuild", "chokidar", "typescript"],
|
|
287
|
-
logLevel: "silent"
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
execSync(`node "${compiledApp}"`, { stdio: "inherit" });
|
|
291
|
-
} else {
|
|
292
|
-
execSync("node app/app.js", { stdio: "ignore" });
|
|
293
|
-
}
|
|
294
|
-
} catch (e) {
|
|
295
|
-
stopSpinner(false, "Failed to prepare runtime");
|
|
296
|
-
console.log(red(`[Titan] Error: ${e.message}`));
|
|
297
|
-
}
|
|
298
76
|
}
|
|
299
77
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const root = process.cwd();
|
|
305
|
-
if (!fs.existsSync(path.join(root, "tsconfig.json"))) return;
|
|
306
|
-
|
|
307
|
-
let tscPath;
|
|
308
|
-
try {
|
|
309
|
-
const require = createRequire(import.meta.url);
|
|
310
|
-
tscPath = require.resolve("typescript/bin/tsc");
|
|
311
|
-
} catch (e) {
|
|
312
|
-
tscPath = path.join(root, "node_modules", "typescript", "bin", "tsc");
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (!fs.existsSync(tscPath)) {
|
|
78
|
+
function startServer(root, outDir) {
|
|
79
|
+
const binaryPath = getEngineBinaryPath(root);
|
|
80
|
+
if (!binaryPath) {
|
|
81
|
+
console.error(red("[TitanPL] Error: Could not find engine binary. Ensure you have installed the correct engine package."));
|
|
316
82
|
return;
|
|
317
83
|
}
|
|
318
84
|
|
|
319
|
-
const
|
|
85
|
+
const distPath = path.resolve(root, "dist");
|
|
320
86
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
tsProcess.stdout.on("data", (data) => {
|
|
328
|
-
const lines = data.toString().split("\n");
|
|
329
|
-
for (const line of lines) {
|
|
330
|
-
if (line.trim().includes("File change detected") || line.trim().includes("Starting compilation")) {
|
|
331
|
-
isTsHealthy = false;
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
if (line.includes("Found 0 errors")) {
|
|
335
|
-
isTsHealthy = true;
|
|
336
|
-
// TS is happy, so we rebuild and restart (or start) the server
|
|
337
|
-
rebuild().then(startRustServer);
|
|
338
|
-
|
|
339
|
-
} else if (line.includes("error TS")) {
|
|
340
|
-
isTsHealthy = false;
|
|
341
|
-
if (serverProcess) {
|
|
342
|
-
console.log(red(`[Titan] TypeScript error detected. Stopping server...`));
|
|
343
|
-
killServer();
|
|
344
|
-
}
|
|
345
|
-
process.stdout.write(line + "\n");
|
|
346
|
-
} else if (line.match(/Found [1-9]\d* error/)) {
|
|
347
|
-
isTsHealthy = false;
|
|
348
|
-
if (serverProcess) {
|
|
349
|
-
console.log(red(`[Titan] TypeScript compilation failed. Stopping server...`));
|
|
350
|
-
killServer();
|
|
351
|
-
}
|
|
352
|
-
process.stdout.write(line + "\n");
|
|
353
|
-
} else if (line.trim()) {
|
|
354
|
-
process.stdout.write(gray(`[TS] ${line}\n`));
|
|
355
|
-
}
|
|
87
|
+
serverProcess = spawn(binaryPath, ['run', distPath, '--watch'], {
|
|
88
|
+
stdio: 'inherit',
|
|
89
|
+
env: {
|
|
90
|
+
...process.env,
|
|
91
|
+
TITAN_ENV: 'development',
|
|
92
|
+
Titan_Dev: '1'
|
|
356
93
|
}
|
|
357
94
|
});
|
|
358
95
|
|
|
359
|
-
|
|
360
|
-
|
|
96
|
+
serverProcess.on('error', (err) => {
|
|
97
|
+
if (err.code === 'ENOENT') {
|
|
98
|
+
console.error(red("[TitanPL] Failed to start engine: Binary not found."));
|
|
99
|
+
} else {
|
|
100
|
+
console.error(red(`[TitanPL] Engine error: ${err.message}`));
|
|
101
|
+
}
|
|
361
102
|
});
|
|
362
103
|
}
|
|
363
104
|
|
|
364
|
-
|
|
105
|
+
export async function dev(options) {
|
|
106
|
+
const root = options.root || process.cwd();
|
|
107
|
+
const outDir = options.outDir || path.join(root, "dist");
|
|
365
108
|
|
|
366
|
-
async function startDev() {
|
|
367
|
-
const root = process.cwd();
|
|
368
|
-
const actionsDir = path.join(root, "app", "actions");
|
|
369
|
-
let hasRust = false;
|
|
370
|
-
if (fs.existsSync(actionsDir)) {
|
|
371
|
-
hasRust = fs.readdirSync(actionsDir).some(f => f.endsWith(".rs"));
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
isTs = fs.existsSync(path.join(root, "tsconfig.json")) ||
|
|
375
|
-
fs.existsSync(path.join(root, "app", "app.ts"));
|
|
376
|
-
|
|
377
|
-
let mode = "";
|
|
378
|
-
if (hasRust) {
|
|
379
|
-
mode = isTs ? "Rust + TS Actions" : "Rust + JS Actions";
|
|
380
|
-
} else {
|
|
381
|
-
mode = isTs ? "TS Actions" : "JS Actions";
|
|
382
|
-
}
|
|
383
109
|
const version = getTitanVersion();
|
|
384
110
|
|
|
385
111
|
console.clear();
|
|
386
112
|
console.log("");
|
|
387
113
|
console.log(` ${bold(cyan("⏣ Titan Planet"))} ${gray("v" + version)} ${yellow("[ Dev Mode ]")}`);
|
|
388
114
|
console.log("");
|
|
389
|
-
console.log(` ${gray("Type: ")}
|
|
115
|
+
console.log(` ${gray("Type: ")} TS Actions`);
|
|
390
116
|
console.log(` ${gray("Hot Reload: ")} ${green("Enabled")}`);
|
|
391
117
|
|
|
392
118
|
if (fs.existsSync(path.join(root, ".env"))) {
|
|
@@ -394,61 +120,56 @@ async function startDev() {
|
|
|
394
120
|
}
|
|
395
121
|
console.log("");
|
|
396
122
|
|
|
397
|
-
|
|
398
|
-
startTypeChecker();
|
|
399
|
-
} else {
|
|
400
|
-
// If no TS, start immediately
|
|
123
|
+
const runBuildCycle = async () => {
|
|
401
124
|
try {
|
|
402
|
-
await
|
|
403
|
-
|
|
404
|
-
|
|
125
|
+
await killServer();
|
|
126
|
+
|
|
127
|
+
// Re-check TS health via bundle() which now calls checkTypes()
|
|
128
|
+
await buildMetadata(root, outDir);
|
|
129
|
+
await bundle({ root, outDir });
|
|
130
|
+
|
|
131
|
+
console.log(cyan(`\n[TitanPL] Starting Engine...`));
|
|
132
|
+
startServer(root, outDir);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (err.message !== '__TITAN_BUNDLE_FAILED__') {
|
|
135
|
+
console.error(red(`[TitanPL] Build failed: ${err.message}`));
|
|
136
|
+
}
|
|
137
|
+
console.log(yellow("\n[TitanPL] Waiting for fixes before restarting orbit..."));
|
|
405
138
|
}
|
|
406
|
-
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Initial build
|
|
142
|
+
await runBuildCycle();
|
|
407
143
|
|
|
408
|
-
|
|
144
|
+
// Watch for changes inside app/
|
|
145
|
+
const appDir = path.join(root, "app");
|
|
146
|
+
const envFile = path.join(root, ".env");
|
|
147
|
+
const tsConfig = path.join(root, "tsconfig.json");
|
|
148
|
+
|
|
149
|
+
const watcher = chokidar.watch([appDir, envFile, tsConfig], {
|
|
409
150
|
ignoreInitial: true,
|
|
410
|
-
awaitWriteFinish: { stabilityThreshold:
|
|
151
|
+
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
411
152
|
});
|
|
412
153
|
|
|
413
|
-
let
|
|
154
|
+
let buildTimer = null;
|
|
414
155
|
watcher.on("all", async (event, file) => {
|
|
415
|
-
if (
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
156
|
+
if (!file) return;
|
|
157
|
+
const relPath = path.relative(root, file);
|
|
158
|
+
if (relPath.startsWith("dist") || relPath.startsWith(".titan") || relPath.startsWith("server") || relPath.startsWith("node_modules")) return;
|
|
159
|
+
|
|
160
|
+
if (buildTimer) clearTimeout(buildTimer);
|
|
161
|
+
buildTimer = setTimeout(() => {
|
|
162
|
+
console.clear();
|
|
163
|
+
console.log(cyan(`[TitanPL] File changed: ${relPath}. Rebuilding...`));
|
|
164
|
+
runBuildCycle();
|
|
165
|
+
}, 300); // Debounce
|
|
166
|
+
});
|
|
425
167
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
} catch (e) {
|
|
431
|
-
// console.log(red("[Titan] Build failed -- waiting for changes..."));
|
|
432
|
-
}
|
|
433
|
-
}, 300);
|
|
168
|
+
// Cleanup on exit
|
|
169
|
+
process.on('SIGINT', async () => {
|
|
170
|
+
await killServer();
|
|
171
|
+
process.exit(0);
|
|
434
172
|
});
|
|
435
|
-
}
|
|
436
173
|
|
|
437
|
-
|
|
438
|
-
stopSpinner();
|
|
439
|
-
console.log(gray("\n[Titan] Stopping server..."));
|
|
440
|
-
await killServer();
|
|
441
|
-
if (tsProcess) {
|
|
442
|
-
if (process.platform === "win32") {
|
|
443
|
-
try { execSync(`taskkill /pid ${tsProcess.pid} /f /t`, { stdio: 'ignore' }); } catch (e) { }
|
|
444
|
-
} else {
|
|
445
|
-
tsProcess.kill();
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
process.exit(0);
|
|
174
|
+
return watcher;
|
|
449
175
|
}
|
|
450
|
-
|
|
451
|
-
process.on("SIGINT", handleExit);
|
|
452
|
-
process.on("SIGTERM", handleExit);
|
|
453
|
-
|
|
454
|
-
startDev();
|