@ezetgalaxy/titan 25.15.5 → 25.15.6

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezetgalaxy/titan",
3
- "version": "25.15.5",
3
+ "version": "25.15.6",
4
4
  "description": "JavaScript backend framework that compiles your JS into a Rust + Axum server.",
5
5
  "license": "ISC",
6
6
  "author": "ezetgalaxy",
@@ -0,0 +1,62 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import esbuild from "esbuild";
4
+
5
+ const root = process.cwd();
6
+ const actionsDir = path.join(root, "app", "actions");
7
+ const outDir = path.join(root, "server", "actions");
8
+
9
+ export async function bundle() {
10
+ console.log("[Titan] Bundling actions...");
11
+
12
+ fs.mkdirSync(outDir, { recursive: true });
13
+
14
+ // Clean old bundles
15
+ for (const file of fs.readdirSync(outDir)) {
16
+ fs.unlinkSync(path.join(outDir, file));
17
+ }
18
+
19
+ const files = fs.readdirSync(actionsDir).filter(f => f.endsWith(".js"));
20
+
21
+ for (const file of files) {
22
+ const actionName = path.basename(file, ".js");
23
+
24
+ const entry = path.join(actionsDir, file);
25
+
26
+ // Rust runtime expects `.jsbundle` extension — consistent with previous design
27
+ const outfile = path.join(outDir, file.replace(".js", ".jsbundle"));
28
+
29
+ console.log(`[Titan] Bundling ${entry} → ${outfile}`);
30
+
31
+ await esbuild.build({
32
+ entryPoints: [entry],
33
+ outfile,
34
+ bundle: true,
35
+ format: "iife",
36
+ globalName: "__titan_exports",
37
+ platform: "neutral",
38
+ target: "es2020",
39
+
40
+ footer: {
41
+ js: `
42
+ (function () {
43
+ const fn =
44
+ __titan_exports["${actionName}"] ||
45
+ __titan_exports.default;
46
+
47
+ if (typeof fn !== "function") {
48
+ throw new Error("[Titan] Action '${actionName}' not found or not a function");
49
+ }
50
+
51
+ globalThis["${actionName}"] = fn;
52
+ })();
53
+ `
54
+ }
55
+ });
56
+
57
+
58
+
59
+ }
60
+
61
+ console.log("[Titan] Bundling finished.");
62
+ }
@@ -0,0 +1,67 @@
1
+ import chokidar from "chokidar";
2
+ import { spawn, execSync } from "child_process";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { bundle } from "./bundle.js";
6
+
7
+ // Required for __dirname in ES modules
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ let serverProcess = null;
12
+
13
+ function startRustServer() {
14
+ if (serverProcess) {
15
+ serverProcess.kill();
16
+ }
17
+
18
+ const serverPath = path.join(process.cwd(), "server");
19
+
20
+ serverProcess = spawn("cargo", ["run"], {
21
+ cwd: serverPath,
22
+ stdio: "inherit",
23
+ shell: true
24
+ });
25
+
26
+ serverProcess.on("close", (code) => {
27
+ console.log(`[Titan] Rust server exited: ${code}`);
28
+ });
29
+ }
30
+
31
+ async function rebuild() {
32
+ console.log("[Titan] Regenerating routes.json & action_map.json...");
33
+ execSync("node app/app.js", { stdio: "inherit" });
34
+
35
+ console.log("[Titan] Bundling JS actions...");
36
+ await bundle();
37
+ }
38
+
39
+ async function startDev() {
40
+ console.log("[Titan] Dev mode starting...");
41
+
42
+ // FIRST BUILD
43
+ await rebuild();
44
+ startRustServer();
45
+
46
+ const watcher = chokidar.watch("app", {
47
+ ignoreInitial: true
48
+ });
49
+
50
+ let timer = null;
51
+
52
+ watcher.on("all", async (event, file) => {
53
+ if (timer) clearTimeout(timer);
54
+
55
+ timer = setTimeout(async () => {
56
+ console.log(`[Titan] Change detected: ${file}`);
57
+
58
+ await rebuild();
59
+
60
+ console.log("[Titan] Restarting Rust server...");
61
+ startRustServer();
62
+
63
+ }, 200);
64
+ });
65
+ }
66
+
67
+ startDev();
@@ -0,0 +1,82 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { bundle } from "./bundle.js";
4
+
5
+ const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
6
+ const green = (t) => `\x1b[32m${t}\x1b[0m`;
7
+
8
+ const routes = {};
9
+ const dynamicRoutes = {};
10
+ const actionMap = {};
11
+
12
+ function addRoute(method, route) {
13
+ const key = `${method.toUpperCase()}:${route}`;
14
+
15
+
16
+ return {
17
+ reply(value) {
18
+ routes[key] = {
19
+ type: typeof value === "object" ? "json" : "text",
20
+ value
21
+ };
22
+ },
23
+
24
+ action(name) {
25
+ if (route.includes(":")) {
26
+ if (!dynamicRoutes[method]) dynamicRoutes[method] = [];
27
+ dynamicRoutes[method].push({
28
+ method: method.toUpperCase(),
29
+ pattern: route,
30
+ action: name
31
+ });
32
+ } else {
33
+ routes[key] = {
34
+ type: "action",
35
+ value: name
36
+ };
37
+ actionMap[key] = name;
38
+ }
39
+ }
40
+ };
41
+ }
42
+
43
+ const t = {
44
+ get(route) {
45
+ return addRoute("GET", route);
46
+ },
47
+
48
+ post(route) {
49
+ return addRoute("POST", route);
50
+ },
51
+
52
+ async start(port = 3000, msg = "") {
53
+ console.log(cyan("[Titan] Bundling actions..."));
54
+ await bundle();
55
+
56
+ const base = path.join(process.cwd(), "server");
57
+ fs.mkdirSync(base, { recursive: true });
58
+
59
+ fs.writeFileSync(
60
+ path.join(base, "routes.json"),
61
+ JSON.stringify(
62
+ {
63
+ __config: { port },
64
+ routes,
65
+ __dynamic_routes: Object.values(dynamicRoutes).flat()
66
+ },
67
+ null,
68
+ 2
69
+ )
70
+ );
71
+
72
+ fs.writeFileSync(
73
+ path.join(base, "action_map.json"),
74
+ JSON.stringify(actionMap, null, 2)
75
+ );
76
+
77
+ console.log(green(`Titan: routes.json + action_map.json written -> ${base}`));
78
+ if (msg) console.log(cyan(msg));
79
+ }
80
+ };
81
+
82
+ export default t;