@ezetgalaxy/titan 26.9.0 → 26.9.1

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.
Files changed (42) hide show
  1. package/README.md +22 -12
  2. package/index.js +57 -15
  3. package/package.json +1 -2
  4. package/templates/{rust → rust-js}/package.json +1 -1
  5. package/templates/rust-ts/app/actions/hello.ts +10 -4
  6. package/templates/rust-ts/app/app.ts +1 -1
  7. package/templates/rust-ts/titan/bundle.js +15 -9
  8. package/templates/rust-ts/titan/dev.js +2 -2
  9. package/templates/rust-ts/titan/titan.d.ts +117 -0
  10. package/templates/rust-ts/titan/titan.js +95 -95
  11. package/templates/ts/Dockerfile +16 -42
  12. package/templates/ts/app/actions/hello.ts +2 -0
  13. package/templates/ts/app/app.ts +1 -1
  14. package/templates/ts/titan/builder.js +121 -0
  15. package/templates/ts/titan/bundle.js +9 -11
  16. package/templates/ts/titan/dev.js +2 -2
  17. package/templates/ts/titan/runtime.js +1 -0
  18. package/templates/ts/titan/titan.d.ts +117 -0
  19. package/templates/ts/titan/titan.js +95 -95
  20. package/titanpl-sdk/README.md +4 -4
  21. package/titanpl-sdk/bin/run.js +74 -77
  22. package/titanpl-sdk/package.json +1 -1
  23. package/templates/rust/app/titan.d.ts +0 -101
  24. package/templates/ts/app/titan.d.ts +0 -102
  25. /package/templates/{rust → rust-js}/Dockerfile +0 -0
  26. /package/templates/{rust → rust-js}/_dockerignore +0 -0
  27. /package/templates/{rust → rust-js}/_gitignore +0 -0
  28. /package/templates/{rust → rust-js}/app/actions/hello.js +0 -0
  29. /package/templates/{rust → rust-js}/app/actions/rust_hello.rs +0 -0
  30. /package/templates/{rust → rust-js}/app/app.js +0 -0
  31. /package/templates/{rust-ts → rust-js}/app/titan.d.ts +0 -0
  32. /package/templates/{rust → rust-js}/jsconfig.json +0 -0
  33. /package/templates/{rust → rust-js}/server/Cargo.lock +0 -0
  34. /package/templates/{rust → rust-js}/server/Cargo.toml +0 -0
  35. /package/templates/{rust → rust-js}/server/src/action_management.rs +0 -0
  36. /package/templates/{rust → rust-js}/server/src/errors.rs +0 -0
  37. /package/templates/{rust → rust-js}/server/src/extensions.rs +0 -0
  38. /package/templates/{rust → rust-js}/server/src/main.rs +0 -0
  39. /package/templates/{rust → rust-js}/server/src/utils.rs +0 -0
  40. /package/templates/{rust → rust-js}/titan/bundle.js +0 -0
  41. /package/templates/{rust → rust-js}/titan/dev.js +0 -0
  42. /package/templates/{rust → rust-js}/titan/titan.js +0 -0
package/README.md CHANGED
@@ -76,7 +76,7 @@ titan dev
76
76
 
77
77
  You'll see the Titan Dev Server spin up:
78
78
  ```
79
- Titan Planet v26.9.0 [ Dev Mode ]
79
+ Titan Planet v26.9.1 [ Dev Mode ]
80
80
 
81
81
  Type: Rust + TS Actions
82
82
  Hot Reload: Enabled
@@ -93,34 +93,45 @@ You'll see the Titan Dev Server spin up:
93
93
 
94
94
  Titan is unique because it allows you to write endpoints in **JavaScript, TypeScript, and Rust** within the same project.
95
95
 
96
+ | Feature | Status | Notes |
97
+ | :--- | :--- | :--- |
98
+ | **Standard JavaScript** | ✅ Stable | Production Ready |
99
+ | **Standard TypeScript** | 🚧 Beta | **Ready for Dev**, Production Under Testing |
100
+ | **Rust + JS (Hybrid)** | 🧪 Experimental | **Dev Only**, Production Under Testing |
101
+ | **Rust + TS (Hybrid)** | 🧪 Experimental | **Dev Only**, Production Under Testing |
102
+
96
103
  ### 🔵 TypeScript Actions (`app/actions/hello.ts`)
97
104
  Fully typed, strict, and auto-compiled.
105
+
98
106
  ```typescript
99
- import { Context } from '@ezetgalaxy/titan';
107
+ import { defineAction } from "../../titan/titan";
108
+
109
+ interface HelloResponse {
110
+ message: string;
111
+ user_name: string;
112
+ }
100
113
 
101
- export function run(req: Context) {
114
+ // "defineAction" provides automatic type inference for "req"
115
+ export const hello = defineAction((req): HelloResponse => {
102
116
  t.log("Handling request with strict types...");
103
-
104
- // Type checking allows safe property access
105
- const user = req.body as { name: string };
106
-
117
+
107
118
  return {
108
119
  message: "Hello from TypeScript!",
109
- user_name: user.name
120
+ user_name: req.body.name || "Guest"
110
121
  };
111
- }
122
+ });
112
123
  ```
113
124
 
114
125
  ### 🟡 JavaScript Actions (`app/actions/hello.js`)
115
126
  Perfect for business logic, rapid prototyping, and IO-bound tasks.
116
127
  ```javascript
117
- export function run(req) {
128
+ export const hello = defineAction((req) => {
118
129
  t.log("Handling user request...");
119
130
  return {
120
131
  message: "Hello from JavaScript!",
121
132
  user_id: req.params.id
122
133
  };
123
- }
134
+ });
124
135
  ```
125
136
 
126
137
  ### 🔴 Rust Actions (Beta)
@@ -212,4 +223,3 @@ Titan is **not** a Node.js framework. It is a Rust server that speaks JavaScript
212
223
  * Strict TypeScript Support
213
224
  * Native Rust Performance
214
225
  * Zero-Config Cloud Deployment
215
-
package/index.js CHANGED
@@ -107,7 +107,7 @@ function help() {
107
107
  console.log(`
108
108
  ${bold(cyan("Titan Planet"))} v${TITAN_VERSION}
109
109
 
110
- ${green("titan init <project>")} Create new Titan project
110
+ ${green("titan init <project> [-t <template>]")} Create new Titan project
111
111
  ${green("titan create ext <name>")} Create new Titan extension
112
112
  ${green("titan dev")} Dev mode (hot reload)
113
113
  ${green("titan build")} Build production Rust server
@@ -188,7 +188,7 @@ async function initProject(name, templateName) {
188
188
  const arch = archRes.value;
189
189
 
190
190
  if (lang === 'js') {
191
- selectedTemplate = arch === 'standard' ? 'js' : 'rust';
191
+ selectedTemplate = arch === 'standard' ? 'js' : 'rust-js';
192
192
  } else {
193
193
  selectedTemplate = arch === 'standard' ? 'ts' : 'rust-ts';
194
194
  }
@@ -289,7 +289,7 @@ async function devServer() {
289
289
  /* -------------------------------------------------------
290
290
  * BUILD
291
291
  * ----------------------------------------------------- */
292
- function buildProd() {
292
+ async function buildProd() {
293
293
  console.log(cyan("Titan: Building production output..."));
294
294
 
295
295
  const root = process.cwd();
@@ -298,11 +298,34 @@ function buildProd() {
298
298
  const actionsOut = path.join(serverDir, "actions");
299
299
 
300
300
  // BASIC CHECKS
301
- if (!fs.existsSync(appJs)) {
302
- console.log(red("ERROR: app/app.js not found."));
301
+ if (!fs.existsSync(appJs) && !fs.existsSync(path.join(root, "app", "app.ts"))) {
302
+ console.log(red("ERROR: app/app.js or app/app.ts not found."));
303
303
  process.exit(1);
304
304
  }
305
305
 
306
+ // COMPILE TYPESCRIPT IF NEEDED
307
+ if (fs.existsSync(path.join(root, "tsconfig.json"))) {
308
+ console.log(cyan("→ Compiling TypeScript..."));
309
+ try {
310
+ // We use esbuild for speed and consistency with dev mode
311
+ const { buildSync } = await import("esbuild");
312
+ buildSync({
313
+ entryPoints: [path.join(root, "app", "app.ts")],
314
+ outfile: appJs,
315
+ bundle: true,
316
+ platform: "node",
317
+ format: "esm",
318
+ external: ["fs", "path", "esbuild", "chokidar", "typescript"],
319
+ packages: "external",
320
+ });
321
+ console.log(green("✔ TypeScript compiled"));
322
+ } catch (e) {
323
+ console.log(red("ERROR: Failed to compile TypeScript."));
324
+ console.error(e);
325
+ process.exit(1);
326
+ }
327
+ }
328
+
306
329
  // ----------------------------------------------------
307
330
  // 1) BUILD METADATA + BUNDLE ACTIONS (ONE TIME ONLY)
308
331
  // ----------------------------------------------------
@@ -330,23 +353,42 @@ function buildProd() {
330
353
  // 2) BUILD RUST BINARY
331
354
  // ----------------------------------------------------
332
355
  console.log(cyan("→ Building Rust release binary..."));
333
- execSync("cargo build --release", {
334
- cwd: serverDir,
335
- stdio: "inherit"
336
- });
337
356
 
338
- console.log(green("✔ Titan production build complete!"));
357
+ // Only build rust if it's a rust project (check Cargo.toml)
358
+ if (fs.existsSync(path.join(serverDir, "Cargo.toml"))) {
359
+ execSync("cargo build --release", {
360
+ cwd: serverDir,
361
+ stdio: "inherit"
362
+ });
363
+ console.log(green("✔ Titan production build complete!"));
364
+ } else {
365
+ console.log(green("✔ Titan production build complete (pure JS/TS)!"));
366
+ }
339
367
  }
340
368
 
341
369
  /* -------------------------------------------------------
342
370
  * START
343
371
  * ----------------------------------------------------- */
344
- function startProd() {
372
+ async function startProd() {
345
373
  const isWin = process.platform === "win32";
346
374
  const bin = isWin ? "titan-server.exe" : "titan-server";
375
+ const root = process.cwd();
376
+
377
+ const exe = path.join(root, "server", "target", "release", bin);
378
+
379
+ if (fs.existsSync(exe)) {
380
+ execSync(`"${exe}"`, { stdio: "inherit" });
381
+ } else {
382
+ // Fallback to pure node start if no rust binary
383
+ const appJs = path.join(root, "app", "app.js");
384
+ // Actually, typically we run the bundled/compiled app if we don't have rust server?
385
+ // But wait, the pure TS template runs `node .titan/app.js` in Docker.
386
+ // But locally `titan start` relies on `app/app.js` being compiled?
387
+ // In `buildProd` above we compiled to `app/app.js`.
388
+ // Let's check for `.titan/app.js` which is dev artifact? No, use the prod build artifact.
389
+ execSync(`node "${appJs}"`, { stdio: "inherit" });
390
+ }
347
391
 
348
- const exe = path.join(process.cwd(), "server", "target", "release", bin);
349
- execSync(`"${exe}"`, { stdio: "inherit" });
350
392
  }
351
393
 
352
394
  /* -------------------------------------------------------
@@ -586,8 +628,8 @@ if (cmd === "create" && args[1] === "ext") {
586
628
  break;
587
629
  }
588
630
  case "dev": devServer(); break;
589
- case "build": buildProd(); break;
590
- case "start": startProd(); break;
631
+ case "build": await buildProd(); break;
632
+ case "start": await startProd(); break;
591
633
  case "update": updateTitan(); break;
592
634
  case "--version":
593
635
  case "-v":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezetgalaxy/titan",
3
- "version": "26.9.0",
3
+ "version": "26.9.1",
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",
@@ -13,7 +13,6 @@
13
13
  "files": [
14
14
  "index.js",
15
15
  "templates/",
16
- "titan",
17
16
  "titanpl-sdk",
18
17
  "README.md"
19
18
  ],
@@ -4,7 +4,7 @@
4
4
  "description": "A Titan Planet server",
5
5
  "type": "module",
6
6
  "titan": {
7
- "template": "rust"
7
+ "template": "rust-js"
8
8
  },
9
9
  "dependencies": {
10
10
  "chokidar": "^5.0.0",
@@ -1,5 +1,11 @@
1
- export const hello = (req) => {
2
- return {
3
- message: `Hello from Titan ${req.body.name}`,
4
- };
1
+ interface HelloResponse {
2
+ message: string;
5
3
  }
4
+
5
+ import { defineAction } from "../../titan/titan";
6
+
7
+ export const hello = defineAction((req): HelloResponse => {
8
+ return {
9
+ message: `Hello from Titan ${req.body.name || "World"}`,
10
+ };
11
+ });
@@ -1,4 +1,4 @@
1
- import t from "../titan/titan.js";
1
+ import t from "../titan/titan";
2
2
 
3
3
 
4
4
 
@@ -2,19 +2,19 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import esbuild from "esbuild";
4
4
 
5
- const root = process.cwd();
6
- const actionsDir = path.join(root, "app", "actions");
7
- const outDir = path.join(root, "server", "actions");
8
- const rustOutDir = path.join(root, "server", "src", "actions_rust");
9
-
10
5
  export async function bundle() {
6
+ const root = process.cwd();
7
+ const actionsDir = path.join(root, "app", "actions");
8
+ const outDir = path.join(root, "server", "actions");
9
+ const rustOutDir = path.join(root, "server", "src", "actions_rust");
10
+
11
11
  const start = Date.now();
12
- await bundleJs();
13
- await bundleRust();
12
+ await bundleJs(actionsDir, outDir);
13
+ await bundleRust(rustOutDir, actionsDir);
14
14
  // console.log(`[Titan] Bundle finished in ${((Date.now() - start) / 1000).toFixed(2)}s`);
15
15
  }
16
16
 
17
- async function bundleJs() {
17
+ async function bundleJs(actionsDir, outDir) {
18
18
  // console.log("[Titan] Bundling JS actions...");
19
19
 
20
20
  fs.mkdirSync(outDir, { recursive: true });
@@ -77,9 +77,15 @@ async function bundleJs() {
77
77
  // console.log("[Titan] JS Bundling finished.");
78
78
  }
79
79
 
80
- export async function bundleRust() {
80
+ export async function bundleRust(rustOutDir, actionsDir) {
81
81
  // console.log("[Titan] Bundling Rust actions...");
82
82
 
83
+ // Fallback if called directly (though typically called via bundle)
84
+ const root = process.cwd();
85
+ if (!actionsDir) actionsDir = path.join(root, "app", "actions");
86
+ if (!rustOutDir) rustOutDir = path.join(root, "server", "src", "actions_rust");
87
+
88
+
83
89
  if (!fs.existsSync(rustOutDir)) {
84
90
  fs.mkdirSync(rustOutDir, { recursive: true });
85
91
  }
@@ -231,11 +231,11 @@ async function rebuild() {
231
231
  bundle: true,
232
232
  platform: "node",
233
233
  format: "esm",
234
- packages: "external",
234
+ external: ["fs", "path", "esbuild", "chokidar", "typescript"],
235
235
  logLevel: "silent"
236
236
  });
237
237
 
238
- execSync(`node "${compiledApp}"`, { stdio: "ignore" });
238
+ execSync(`node "${compiledApp}"`, { stdio: "inherit" });
239
239
  } else {
240
240
  execSync("node app/app.js", { stdio: "ignore" });
241
241
  }
@@ -0,0 +1,117 @@
1
+
2
+ // -- Module Definitions (for imports from "titan") --
3
+
4
+ export interface RouteHandler {
5
+ reply(value: any): void;
6
+ action(name: string): void;
7
+ }
8
+
9
+ export interface TitanBuilder {
10
+ get(route: string): RouteHandler;
11
+ post(route: string): RouteHandler;
12
+ log(module: string, msg: string): void;
13
+ start(port?: number, msg?: string): Promise<void>;
14
+ }
15
+
16
+ // The default export from titan.js is the Builder
17
+ declare const builder: TitanBuilder;
18
+ export default builder;
19
+
20
+ /**
21
+ * Define a Titan Action with type inference.
22
+ */
23
+ export declare function defineAction<T>(actionFn: (req: TitanRequest) => T): (req: TitanRequest) => T;
24
+
25
+
26
+ // -- Global Definitions (Runtime Environment) --
27
+
28
+ declare global {
29
+ /**
30
+ * The Titan Request Object passed to actions.
31
+ */
32
+ interface TitanRequest {
33
+ body: any;
34
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
35
+ path: string;
36
+ headers: {
37
+ host?: string;
38
+ "content-type"?: string;
39
+ "user-agent"?: string;
40
+ authorization?: string;
41
+ [key: string]: string | undefined;
42
+ };
43
+ params: Record<string, string>;
44
+ query: Record<string, string>;
45
+ }
46
+
47
+ interface DbConnection {
48
+ /**
49
+ * Execute a SQL query.
50
+ * @param sql The SQL query string.
51
+ * @param params (Optional) Parameters for the query ($1, $2, etc).
52
+ */
53
+ query(sql: string, params?: any[]): any[];
54
+ }
55
+
56
+ /**
57
+ * Global defineAction (available without import in runtime, though imports are preferred in TS)
58
+ */
59
+ function defineAction<T>(actionFn: (req: TitanRequest) => T): (req: TitanRequest) => T;
60
+
61
+ /**
62
+ * Global Request Object
63
+ * Available automatically in actions.
64
+ */
65
+ var req: TitanRequest;
66
+
67
+ /**
68
+ * Titan Runtime Utilities
69
+ * (Available globally in the runtime, e.g. inside actions)
70
+ */
71
+ const t: {
72
+ /**
73
+ * Log messages to the server console with Titan formatting.
74
+ */
75
+ log(...args: any[]): void;
76
+
77
+ /**
78
+ * Read a file contents as string.
79
+ * @param path Relative path to the file from project root.
80
+ */
81
+ read(path: string): string;
82
+
83
+ fetch(url: string, options?: {
84
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
85
+ headers?: Record<string, string>;
86
+ body?: string | object;
87
+ }): {
88
+ ok: boolean;
89
+ status?: number;
90
+ body?: string;
91
+ error?: string;
92
+ };
93
+
94
+ jwt: {
95
+ sign(
96
+ payload: object,
97
+ secret: string,
98
+ options?: { expiresIn?: string | number }
99
+ ): string;
100
+ verify(token: string, secret: string): any;
101
+ };
102
+
103
+ password: {
104
+ hash(password: string): string;
105
+ verify(password: string, hash: string): boolean;
106
+ };
107
+
108
+ db: {
109
+ connect(url: string): DbConnection;
110
+ };
111
+
112
+ /**
113
+ * Titan Validator (Zod-compatible)
114
+ */
115
+ valid: any;
116
+ };
117
+ }
@@ -1,6 +1,8 @@
1
+ import { bundle } from "./bundle.js";
1
2
  import fs from "fs";
2
3
  import path from "path";
3
- import { bundle } from "./bundle.js";
4
+
5
+ export const defineAction = (handler) => handler;
4
6
 
5
7
  const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
6
8
  const green = (t) => `\x1b[32m${t}\x1b[0m`;
@@ -10,34 +12,33 @@ const dynamicRoutes = {};
10
12
  const actionMap = {};
11
13
 
12
14
  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
- };
15
+ const key = `${method.toUpperCase()}:${route}`;
16
+
17
+ return {
18
+ reply(value) {
19
+ routes[key] = {
20
+ type: typeof value === "object" ? "json" : "text",
21
+ value
22
+ };
23
+ },
24
+
25
+ action(name) {
26
+ if (route.includes(":")) {
27
+ if (!dynamicRoutes[method]) dynamicRoutes[method] = [];
28
+ dynamicRoutes[method].push({
29
+ method: method.toUpperCase(),
30
+ pattern: route,
31
+ action: name
32
+ });
33
+ } else {
34
+ routes[key] = {
35
+ type: "action",
36
+ value: name
37
+ };
38
+ actionMap[key] = name;
39
+ }
40
+ }
41
+ };
41
42
  }
42
43
 
43
44
  /**
@@ -50,73 +51,72 @@ function addRoute(method, route) {
50
51
  * Titan App Builder
51
52
  */
52
53
  const t = {
53
- /**
54
- * Define a GET route
55
- * @param {string} route
56
- * @returns {RouteHandler}
57
- */
58
- get(route) {
59
- return addRoute("GET", route);
60
- },
61
-
62
- /**
63
- * Define a POST route
64
- * @param {string} route
65
- * @returns {RouteHandler}
66
- */
67
- post(route) {
68
- return addRoute("POST", route);
69
- },
70
-
71
- log(module, msg) {
72
- console.log(`[\x1b[35m${module}\x1b[0m] ${msg}`);
73
- },
74
-
75
- /**
76
- * Start the Titan Server
77
- * @param {number} [port=3000]
78
- * @param {string} [msg=""]
79
- */
80
- async start(port = 3000, msg = "") {
81
- try {
82
- console.log(cyan("[Titan] Preparing runtime..."));
83
- await bundle();
84
-
85
- const base = path.join(process.cwd(), "server");
86
- if (!fs.existsSync(base)) {
87
- fs.mkdirSync(base, { recursive: true });
88
- }
89
-
90
- const routesPath = path.join(base, "routes.json");
91
- const actionMapPath = path.join(base, "action_map.json");
92
-
93
- fs.writeFileSync(
94
- routesPath,
95
- JSON.stringify(
96
- {
97
- __config: { port },
98
- routes,
99
- __dynamic_routes: Object.values(dynamicRoutes).flat()
100
- },
101
- null,
102
- 2
103
- )
104
- );
105
-
106
- fs.writeFileSync(
107
- actionMapPath,
108
- JSON.stringify(actionMap, null, 2)
109
- );
110
-
111
- console.log(green("✔ Titan metadata written successfully"));
112
- if (msg) console.log(cyan(msg));
113
-
114
- } catch (e) {
115
- console.error(`\x1b[31m[Titan] Build Error: ${e.message}\x1b[0m`);
116
- process.exit(1);
54
+ /**
55
+ * Define a GET route
56
+ * @param {string} route
57
+ * @returns {RouteHandler}
58
+ */
59
+ get(route) {
60
+ return addRoute("GET", route);
61
+ },
62
+
63
+ /**
64
+ * Define a POST route
65
+ * @param {string} route
66
+ * @returns {RouteHandler}
67
+ */
68
+ post(route) {
69
+ return addRoute("POST", route);
70
+ },
71
+
72
+ log(module, msg) {
73
+ console.log(`[\x1b[35m${module}\x1b[0m] ${msg}`);
74
+ },
75
+
76
+ /**
77
+ * Start the Titan Server
78
+ * @param {number} [port=3000]
79
+ * @param {string} [msg=""]
80
+ */
81
+ async start(port = 3000, msg = "") {
82
+ try {
83
+ console.log(cyan("[Titan] Preparing runtime..."));
84
+ await bundle();
85
+
86
+ const base = path.join(process.cwd(), "server");
87
+ if (!fs.existsSync(base)) {
88
+ fs.mkdirSync(base, { recursive: true });
89
+ }
90
+
91
+ const routesPath = path.join(base, "routes.json");
92
+ const actionMapPath = path.join(base, "action_map.json");
93
+
94
+ fs.writeFileSync(
95
+ routesPath,
96
+ JSON.stringify(
97
+ {
98
+ __config: { port },
99
+ routes,
100
+ __dynamic_routes: Object.values(dynamicRoutes).flat()
101
+ },
102
+ null,
103
+ 2
104
+ )
105
+ );
106
+
107
+ fs.writeFileSync(
108
+ actionMapPath,
109
+ JSON.stringify(actionMap, null, 2)
110
+ );
111
+
112
+ console.log(green("✔ Titan metadata written successfully"));
113
+ if (msg) console.log(cyan(msg));
114
+
115
+ } catch (e) {
116
+ console.error(`\x1b[31m[Titan] Build Error: ${e.message}\x1b[0m`);
117
+ process.exit(1);
118
+ }
117
119
  }
118
- }
119
120
  };
120
121
 
121
-
122
122
  export default t;