@ezetgalaxy/titan 26.8.0 → 26.8.3

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 CHANGED
@@ -131,17 +131,8 @@ Both JS and Rust actions have access to the powerful `t` namespace:
131
131
  * `t.log(msg)` — Sandboxed, structured logging
132
132
  * `t.jwt.sign / verify` — Fast JWT operations
133
133
  * `t.password.hash / verify` — Secure password handling
134
- * `t.db` — Database access (coming soon)
135
-
136
- ### 🛣 Intelligent Routing
137
- Define your routes in `routes.json`. Titan maps them to the correct action, regardless of language.
138
-
139
- ```json
140
- {
141
- "/hello": "hello", // variable name matches filename (hello.js)
142
- "/compute": "compute" // variable name matches filename (compute.rs)
143
- }
144
- ```
134
+ * `t.db` — Database access
135
+ ---
145
136
 
146
137
  ### 🧩 Extensions System
147
138
  Extend the runtime with custom Rust engines using **Titan Extensions**.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezetgalaxy/titan",
3
- "version": "26.8.0",
3
+ "version": "26.8.3",
4
4
  "description": "Titan Planet is a JavaScript-first backend framework that embeds JS actions into a Rust + Axum server and ships as a single native binary. Routes are compiled to static metadata; only actions run in the embedded JS runtime. No Node.js. No event loop in production.",
5
5
  "license": "ISC",
6
6
  "author": "ezetgalaxy",
@@ -52,4 +52,4 @@
52
52
  "esbuild": "^0.27.2",
53
53
  "prompts": "^2.4.2"
54
54
  }
55
- }
55
+ }
@@ -368,12 +368,7 @@ async fn main() -> Result<()> {
368
368
 
369
369
  let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await?;
370
370
 
371
- println!("\n\x1b[38;5;208m████████╗██╗████████╗ █████╗ ███╗ ██╗");
372
- println!("╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗ ██║");
373
- println!(" ██║ ██║ ██║ ███████║██╔██╗ ██║");
374
- println!(" ██║ ██║ ██║ ██╔══██║██║╚██╗██║");
375
- println!(" ██║ ██║ ██║ ██║ ██║██║ ╚████║");
376
- println!(" ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝\x1b[0m\n");
371
+
377
372
  println!(
378
373
  "\x1b[38;5;39mTitan server running at:\x1b[0m http://localhost:{}",
379
374
  port
@@ -28,7 +28,7 @@ async function bundleJs() {
28
28
  const files = fs.readdirSync(actionsDir).filter(f => f.endsWith(".js") || f.endsWith(".ts"));
29
29
  if (files.length === 0) return;
30
30
 
31
- console.log(`[Titan] Bundling ${files.length} JS actions...`);
31
+ // console.log(`[Titan] Bundling ${files.length} JS actions...`);
32
32
 
33
33
  for (const file of files) {
34
34
  const actionName = path.basename(file, path.extname(file));
@@ -1,194 +1,282 @@
1
- import chokidar from "chokidar";
2
- import { spawn, execSync } from "child_process";
3
- import path from "path";
4
- import { fileURLToPath } from "url";
5
- import fs from "fs";
6
- import { bundle } from "./bundle.js";
7
-
8
- // Required for __dirname in ES modules
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = path.dirname(__filename);
11
-
12
-
13
- // Colors
14
- import { createRequire } from "module";
15
-
16
- // Colors
17
- const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
18
- const green = (t) => `\x1b[32m${t}\x1b[0m`;
19
- const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
20
- const red = (t) => `\x1b[31m${t}\x1b[0m`;
21
- const gray = (t) => `\x1b[90m${t}\x1b[0m`;
22
- const bold = (t) => `\x1b[1m${t}\x1b[0m`;
23
-
24
- function getTitanVersion() {
25
- try {
26
- // 1. Try resolving from node_modules (standard user case)
27
- const require = createRequire(import.meta.url);
28
- // We look for @ezetgalaxy/titan/package.json
29
- const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
30
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
31
- return pkg.version;
32
- } catch (e) {
33
- try {
34
- // 2. Fallback for local dev (path to repo root)
35
- const localPath = path.join(__dirname, "..", "..", "..", "package.json");
36
- if (fs.existsSync(localPath)) {
37
- const pkg = JSON.parse(fs.readFileSync(localPath, "utf-8"));
38
- if (pkg.name === "@ezetgalaxy/titan") {
39
- return pkg.version;
40
- }
41
- }
42
- } catch (e2) { }
43
- }
44
- return "0.1.0"; // Fallback
45
- }
46
-
47
- let serverProcess = null;
48
- let isKilling = false;
49
-
50
- // ... (killServer same as before)
51
- async function killServer() {
52
- if (!serverProcess) return;
53
-
54
- isKilling = true;
55
- const pid = serverProcess.pid;
56
- const killPromise = new Promise((resolve) => {
57
- if (serverProcess.exitCode !== null) return resolve();
58
- serverProcess.once("close", resolve);
59
- });
60
-
61
- if (process.platform === "win32") {
62
- try {
63
- execSync(`taskkill /pid ${pid} /f /t`, { stdio: 'ignore' });
64
- } catch (e) {
65
- // Ignore errors if process is already dead
66
- }
67
- } else {
68
- serverProcess.kill();
69
- }
70
-
71
- try {
72
- await killPromise;
73
- } catch (e) { }
74
- serverProcess = null;
75
- isKilling = false;
76
- }
77
-
78
- async function startRustServer(retryCount = 0) {
79
- const waitTime = retryCount > 0 ? 2000 : 1000;
80
-
81
- await killServer();
82
- await new Promise(r => setTimeout(r, waitTime));
83
-
84
- const serverPath = path.join(process.cwd(), "server");
85
- const startTime = Date.now();
86
-
87
- if (retryCount > 0) {
88
- console.log(yellow(`[Titan] Retrying Rust server (Attempt ${retryCount})...`));
89
- }
90
-
91
- serverProcess = spawn("cargo", ["run", "--jobs", "1"], {
92
- cwd: serverPath,
93
- stdio: "inherit",
94
- shell: true,
95
- env: { ...process.env, CARGO_INCREMENTAL: "0" }
96
- });
97
-
98
- serverProcess.on("close", async (code) => {
99
- if (isKilling) return;
100
- const runTime = Date.now() - startTime;
101
- if (code !== 0 && code !== null && runTime < 15000 && retryCount < 5) {
102
- await startRustServer(retryCount + 1);
103
- } else if (code !== 0 && code !== null && retryCount >= 5) {
104
- console.log(red(`[Titan] Server failed to start after multiple attempts.`));
105
- }
106
- });
107
- }
108
-
109
- async function rebuild() {
110
- // process.stdout.write(gray("[Titan] Preparing runtime... "));
111
- const start = Date.now();
112
- try {
113
- execSync("node app/app.js", { stdio: "ignore" });
114
- await bundle();
115
- // console.log(green("Done"));
116
- const elapsed = ((Date.now() - start) / 1000).toFixed(1);
117
- console.log(gray(` A new orbit is ready for your app in ${elapsed}s`));
118
- console.log(green(` Your app is now orbiting Titan Planet`));
119
- } catch (e) {
120
- console.log(red("Failed"));
121
- console.log(red("[Titan] Failed to prepare runtime. Check your app/app.js"));
122
- }
123
- }
124
-
125
- async function startDev() {
126
- const root = process.cwd();
127
- // Check if Rust actions exist by looking for .rs files in app/actions
128
- const actionsDir = path.join(root, "app", "actions");
129
- let hasRust = false;
130
- if (fs.existsSync(actionsDir)) {
131
- hasRust = fs.readdirSync(actionsDir).some(f => f.endsWith(".rs"));
132
- }
133
-
134
- const mode = hasRust ? "Rust + JS Actions" : "JS Actions";
135
- const version = getTitanVersion();
136
-
137
- console.clear();
138
- console.log("");
139
- console.log(` ${bold(cyan("Titan Planet"))} ${gray("v" + version)} ${yellow("[ Dev Mode ]")}`);
140
- console.log("");
141
- console.log(` ${gray("Type: ")} ${mode}`);
142
- console.log(` ${gray("Hot Reload: ")} ${green("Enabled")}`);
143
-
144
- if (fs.existsSync(path.join(root, ".env"))) {
145
- console.log(` ${gray("Env: ")} ${yellow("Loaded")}`);
146
- }
147
- console.log(""); // Spacer
148
-
149
- // FIRST BUILD
150
- try {
151
- await rebuild();
152
- await startRustServer();
153
- } catch (e) {
154
- console.log(red("[Titan] Initial build failed. Waiting for changes..."));
155
- }
156
-
157
- // ... watcher logic same as before but using color vars ...
158
- const watcher = chokidar.watch(["app", ".env"], {
159
- ignoreInitial: true,
160
- awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
161
- });
162
-
163
- let timer = null;
164
- watcher.on("all", async (event, file) => {
165
- if (timer) clearTimeout(timer);
166
- timer = setTimeout(async () => {
167
- console.log(""); // Spacer before reload logs
168
- if (file.includes(".env")) {
169
- console.log(yellow("[Titan] Env Refreshed"));
170
- } else {
171
- console.log(cyan(`[Titan] Change: ${path.basename(file)}`));
172
- }
173
- try {
174
- await killServer();
175
- await rebuild();
176
- await startRustServer();
177
- } catch (e) {
178
- console.log(red("[Titan] Build failed -- waiting for changes..."));
179
- }
180
- }, 1000);
181
- });
182
- }
183
-
184
- // Handle graceful exit to release file locks
185
- async function handleExit() {
186
- console.log("\n[Titan] Stopping server...");
187
- await killServer();
188
- process.exit(0);
189
- }
190
-
191
- process.on("SIGINT", handleExit);
192
- process.on("SIGTERM", handleExit);
193
-
194
- startDev();
1
+ import chokidar from "chokidar";
2
+ import { spawn, execSync } from "child_process";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import fs from "fs";
6
+
7
+ // Required for __dirname in ES modules
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+
12
+ // Colors
13
+ import { createRequire } from "module";
14
+
15
+ // Colors
16
+ const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
17
+ const green = (t) => `\x1b[32m${t}\x1b[0m`;
18
+ const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
19
+ const red = (t) => `\x1b[31m${t}\x1b[0m`;
20
+ const gray = (t) => `\x1b[90m${t}\x1b[0m`;
21
+ const bold = (t) => `\x1b[1m${t}\x1b[0m`;
22
+
23
+ function getTitanVersion() {
24
+ try {
25
+ const require = createRequire(import.meta.url);
26
+ const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
27
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
28
+ } catch (e) {
29
+ try {
30
+ // Check levels up to find the framework root
31
+ let cur = __dirname;
32
+ for (let i = 0; i < 5; i++) {
33
+ const pkgPath = path.join(cur, "package.json");
34
+ if (fs.existsSync(pkgPath)) {
35
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
36
+ if (pkg.name === "@ezetgalaxy/titan") return pkg.version;
37
+ }
38
+ cur = path.join(cur, "..");
39
+ }
40
+ } catch (e2) { }
41
+
42
+ try {
43
+ // Fallback to calling tit --version
44
+ const output = execSync("tit --version", { encoding: "utf-8" }).trim();
45
+ const match = output.match(/v(\d+\.\d+\.\d+)/);
46
+ if (match) return match[1];
47
+ } catch (e3) { }
48
+ }
49
+ return "0.1.0";
50
+ }
51
+
52
+ let serverProcess = null;
53
+ let isKilling = false;
54
+ let isFirstBoot = true;
55
+
56
+ // ... (killServer same as before)
57
+ async function killServer() {
58
+ if (!serverProcess) return;
59
+
60
+ isKilling = true;
61
+ const pid = serverProcess.pid;
62
+ const killPromise = new Promise((resolve) => {
63
+ if (serverProcess.exitCode !== null) return resolve();
64
+ serverProcess.once("close", resolve);
65
+ });
66
+
67
+ if (process.platform === "win32") {
68
+ try {
69
+ execSync(`taskkill /pid ${pid} /f /t`, { stdio: 'ignore' });
70
+ } catch (e) {
71
+ // Ignore errors if process is already dead
72
+ }
73
+ } else {
74
+ serverProcess.kill();
75
+ }
76
+
77
+ try {
78
+ await killPromise;
79
+ } catch (e) { }
80
+ serverProcess = null;
81
+ isKilling = false;
82
+ }
83
+
84
+ const delay = (ms) => new Promise(res => setTimeout(res, ms));
85
+
86
+ let spinnerTimer = null;
87
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
88
+ let frameIdx = 0;
89
+
90
+ function startSpinner(text) {
91
+ if (spinnerTimer) clearInterval(spinnerTimer);
92
+ process.stdout.write("\x1B[?25l"); // Hide cursor
93
+ spinnerTimer = setInterval(() => {
94
+ process.stdout.write(`\r ${cyan(frames[frameIdx])} ${gray(text)}`);
95
+ frameIdx = (frameIdx + 1) % frames.length;
96
+ }, 80);
97
+ }
98
+
99
+ function stopSpinner(success = true, text = "") {
100
+ if (spinnerTimer) {
101
+ clearInterval(spinnerTimer);
102
+ spinnerTimer = null;
103
+ }
104
+ process.stdout.write("\r\x1B[K"); // Clear line
105
+ process.stdout.write("\x1B[?25h"); // Show cursor
106
+ if (text) {
107
+ if (success) {
108
+ console.log(` ${green("✔")} ${green(text)}`);
109
+ } else {
110
+ console.log(` ${red("")} ${red(text)}`);
111
+ }
112
+ }
113
+ }
114
+
115
+ async function startRustServer(retryCount = 0) {
116
+ const waitTime = retryCount > 0 ? 500 : 200;
117
+
118
+ await killServer();
119
+ await delay(waitTime);
120
+
121
+ const serverPath = path.join(process.cwd(), "server");
122
+ const startTime = Date.now();
123
+
124
+ startSpinner("Stabilizing your app on its orbit...");
125
+
126
+ let isReady = false;
127
+ let stdoutBuffer = "";
128
+ let buildLogs = "";
129
+
130
+ // If it takes more than 15s, update the message
131
+ const slowTimer = setTimeout(() => {
132
+ if (!isReady && !isKilling) {
133
+ startSpinner("Still stabilizing... (the first orbit takes longer)");
134
+ }
135
+ }, 15000);
136
+
137
+ serverProcess = spawn("cargo", ["run", "--quiet"], {
138
+ cwd: serverPath,
139
+ stdio: ["ignore", "pipe", "pipe"],
140
+ env: { ...process.env, CARGO_INCREMENTAL: "1" }
141
+ });
142
+
143
+ serverProcess.on("error", (err) => {
144
+ stopSpinner(false, "Failed to start orbit");
145
+ console.error(red(`[Titan] Error: ${err.message}`));
146
+ });
147
+
148
+ serverProcess.stderr.on("data", (data) => {
149
+ const str = data.toString();
150
+ if (isReady) {
151
+ process.stderr.write(data);
152
+ } else {
153
+ buildLogs += str;
154
+ }
155
+ });
156
+
157
+ serverProcess.stdout.on("data", (data) => {
158
+ const out = data.toString();
159
+
160
+ if (!isReady) {
161
+ stdoutBuffer += out;
162
+ if (stdoutBuffer.includes("Titan server running") || stdoutBuffer.includes("████████╗")) {
163
+ isReady = true;
164
+ clearTimeout(slowTimer);
165
+ stopSpinner(true, "Your app is now orbiting Titan Planet");
166
+
167
+ if (isFirstBoot) {
168
+ process.stdout.write(stdoutBuffer);
169
+ isFirstBoot = false;
170
+ } else {
171
+ // On subsequent reloads, only print non-banner lines from the buffer
172
+ const lines = stdoutBuffer.split("\n");
173
+ for (const line of lines) {
174
+ const isBanner = line.includes("Titan server running") ||
175
+ line.includes("████████╗") ||
176
+ line.includes("╚══") ||
177
+ line.includes(" ██║") ||
178
+ line.includes(" ╚═╝");
179
+ if (!isBanner && line.trim()) {
180
+ process.stdout.write(line + "\n");
181
+ }
182
+ }
183
+ }
184
+ stdoutBuffer = "";
185
+ }
186
+ } else {
187
+ process.stdout.write(data);
188
+ }
189
+ });
190
+
191
+ serverProcess.on("close", async (code) => {
192
+ clearTimeout(slowTimer);
193
+ if (isKilling) return;
194
+ const runTime = Date.now() - startTime;
195
+
196
+ if (code !== 0 && code !== null) {
197
+ stopSpinner(false, "Orbit stabilization failed");
198
+ if (!isReady) {
199
+ console.log(gray("\n--- Build Logs ---"));
200
+ console.log(buildLogs);
201
+ console.log(gray("------------------\n"));
202
+ }
203
+
204
+ if (runTime < 15000 && retryCount < 5) {
205
+ await delay(2000);
206
+ await startRustServer(retryCount + 1);
207
+ }
208
+ }
209
+ });
210
+ }
211
+
212
+ async function rebuild() {
213
+ try {
214
+ execSync("node app/app.js", { stdio: "ignore" });
215
+ // bundle is called inside app.js (t.start)
216
+ } catch (e) {
217
+ stopSpinner(false, "Failed to prepare runtime");
218
+ console.log(red(`[Titan] Error: ${e.message}`));
219
+ }
220
+ }
221
+
222
+ async function startDev() {
223
+ const root = process.cwd();
224
+ const actionsDir = path.join(root, "app", "actions");
225
+ let hasRust = false;
226
+ if (fs.existsSync(actionsDir)) {
227
+ hasRust = fs.readdirSync(actionsDir).some(f => f.endsWith(".rs"));
228
+ }
229
+
230
+ const mode = hasRust ? "Rust + JS Actions" : "JS Actions";
231
+ const version = getTitanVersion();
232
+
233
+ console.clear();
234
+ console.log("");
235
+ console.log(` ${bold(cyan("Titan Planet"))} ${gray("v" + version)} ${yellow("[ Dev Mode ]")}`);
236
+ console.log("");
237
+ console.log(` ${gray("Type: ")} ${mode}`);
238
+ console.log(` ${gray("Hot Reload: ")} ${green("Enabled")}`);
239
+
240
+ if (fs.existsSync(path.join(root, ".env"))) {
241
+ console.log(` ${gray("Env: ")} ${yellow("Loaded")}`);
242
+ }
243
+ console.log("");
244
+
245
+ try {
246
+ await rebuild();
247
+ await startRustServer();
248
+ } catch (e) {
249
+ // console.log(red("[Titan] Initial build failed. Waiting for changes..."));
250
+ }
251
+
252
+ const watcher = chokidar.watch(["app", ".env"], {
253
+ ignoreInitial: true,
254
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
255
+ });
256
+
257
+ let timer = null;
258
+ watcher.on("all", async (event, file) => {
259
+ if (timer) clearTimeout(timer);
260
+ timer = setTimeout(async () => {
261
+ try {
262
+ await killServer();
263
+ await rebuild();
264
+ await startRustServer();
265
+ } catch (e) {
266
+ // console.log(red("[Titan] Build failed -- waiting for changes..."));
267
+ }
268
+ }, 300);
269
+ });
270
+ }
271
+
272
+ async function handleExit() {
273
+ stopSpinner();
274
+ console.log(gray("\n[Titan] Stopping server..."));
275
+ await killServer();
276
+ process.exit(0);
277
+ }
278
+
279
+ process.on("SIGINT", handleExit);
280
+ process.on("SIGTERM", handleExit);
281
+
282
+ startDev();
@@ -25,3 +25,15 @@ jsonwebtoken = "9"
25
25
  postgres = { version = "0.19", features = ["with-serde_json-1"] }
26
26
  libloading = "0.8"
27
27
  walkdir = "2"
28
+
29
+ [profile.dev]
30
+ opt-level = 0
31
+ debug = 1
32
+ incremental = true
33
+
34
+ [profile.release]
35
+ opt-level = 3
36
+ lto = true
37
+ codegen-units = 1
38
+ panic = "abort"
39
+ strip = true
@@ -400,12 +400,6 @@ async fn main() -> Result<()> {
400
400
 
401
401
  let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await?;
402
402
 
403
- println!("\n\x1b[38;5;208m████████╗██╗████████╗ █████╗ ███╗ ██╗");
404
- println!("╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗ ██║");
405
- println!(" ██║ ██║ ██║ ███████║██╔██╗ ██║");
406
- println!(" ██║ ██║ ██║ ██╔══██║██║╚██╗██║");
407
- println!(" ██║ ██║ ██║ ██║ ██║██║ ╚████║");
408
- println!(" ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝\x1b[0m\n");
409
403
  println!(
410
404
  "\x1b[38;5;39mTitan server running at:\x1b[0m http://localhost:{}",
411
405
  port