@ezetgalaxy/titan 25.11.6 → 25.11.8

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { execSync, spawn } from "child_process";
@@ -43,6 +43,7 @@ ${green("tit init <project>")} Create new Titan project
43
43
  ${green("tit dev")} Dev mode (hot reload)
44
44
  ${green("tit build")} Build production Rust server
45
45
  ${green("tit start")} Start production binary
46
+ ${green("tit update")} Update to latest version
46
47
  `);
47
48
  }
48
49
 
@@ -61,6 +62,14 @@ function initProject(name) {
61
62
  console.log(cyan(`Creating Titan project → ${target}`));
62
63
 
63
64
  copyDir(templateDir, target);
65
+ copyDir(templateDir, target);
66
+
67
+ [".gitignore", ".dockerignore", "Dockerfile"].forEach((file) => {
68
+ const src = path.join(templateDir, file);
69
+ const dest = path.join(target, file);
70
+ if (fs.existsSync(src)) fs.copyFileSync(src, dest);
71
+ });
72
+
64
73
 
65
74
  console.log(green("✔ Titan project created!"));
66
75
  console.log(cyan("Installing dependencies..."));
@@ -187,6 +196,42 @@ function startProd() {
187
196
  execSync(`"${exe}"`, { stdio: "inherit" });
188
197
  }
189
198
 
199
+ // ------------------------------------------
200
+ // TITAN UPDATE — Upgrade titan/ runtime
201
+ // ------------------------------------------
202
+ function updateTitan() {
203
+ const projectTitan = path.join(process.cwd(), "titan");
204
+ const cliTitan = path.join(__dirname, "templates", "titan");
205
+
206
+ if (!fs.existsSync(projectTitan)) {
207
+ console.log(red("No titan/ folder found in this project."));
208
+ console.log(yellow("Make sure you are inside a Titan project."));
209
+ return;
210
+ }
211
+
212
+ console.log(cyan("Titan: Updating runtime files..."));
213
+
214
+ // Backup old titan folder
215
+ const backupDir = path.join(process.cwd(), `titan_backup_${Date.now()}`);
216
+ fs.renameSync(projectTitan, backupDir);
217
+
218
+ console.log(green(`✔ Backup created → ${backupDir}`));
219
+
220
+ copyDir(cliTitan, projectTitan);
221
+ copyDir(templateDir, target);
222
+
223
+ [".gitignore", ".dockerignore", "Dockerfile"].forEach((file) => {
224
+ const src = path.join(templateDir, file);
225
+ const dest = path.join(target, file);
226
+ if (fs.existsSync(src)) fs.copyFileSync(src, dest);
227
+ });
228
+
229
+
230
+ console.log(green("✔ Titan runtime updated successfully!"));
231
+ console.log(cyan("Your project now has the latest Titan features."));
232
+ }
233
+
234
+
190
235
  // ROUTER
191
236
  switch (cmd) {
192
237
  case "init":
@@ -205,6 +250,11 @@ switch (cmd) {
205
250
  startProd();
206
251
  break;
207
252
 
253
+ case "update":
254
+ updateTitan();
255
+ break;
256
+
208
257
  default:
209
258
  help();
210
259
  }
260
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezetgalaxy/titan",
3
- "version": "25.11.6",
3
+ "version": "25.11.8",
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,3 @@
1
+ node_modules
2
+ npm-debug.log
3
+ .git
@@ -0,0 +1,40 @@
1
+ # Stage 1 — Build Titan (JS → Rust)
2
+ FROM rust:1.91.1 AS builder
3
+
4
+ # Install Node for Titan CLI
5
+ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
6
+ && apt-get install -y nodejs
7
+
8
+ # Install Titan CLI AND its required dependencies
9
+ RUN npm install -g @ezetgalaxy/titan esbuild chokidar
10
+
11
+ WORKDIR /app
12
+
13
+ # Copy project
14
+ COPY . .
15
+
16
+ # Install Titan dependencies LOCALLY
17
+ RUN npm install esbuild chokidar
18
+
19
+ # Build Titan metadata (routes + bundles)
20
+ RUN tit build
21
+
22
+ # Build Rust release binary
23
+ RUN cd server && cargo build --release
24
+
25
+ # Stage 2 — Run Titan server
26
+ FROM debian:stable-slim
27
+
28
+ WORKDIR /app
29
+
30
+ # Copy final Rust binary
31
+ COPY --from=builder /app/server/target/release/server ./titan-server
32
+
33
+ # Copy Titan runtime files
34
+ COPY --from=builder /app/server/routes.json .
35
+ COPY --from=builder /app/server/action_map.json .
36
+ COPY --from=builder /app/server/actions ./actions
37
+
38
+ EXPOSE 3000
39
+
40
+ CMD ["./titan-server"]
@@ -393,6 +393,12 @@ version = "0.15.0"
393
393
  source = "registry+https://github.com/rust-lang/crates.io-index"
394
394
  checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
395
395
 
396
+ [[package]]
397
+ name = "dotenvy"
398
+ version = "0.15.7"
399
+ source = "registry+https://github.com/rust-lang/crates.io-index"
400
+ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
401
+
396
402
  [[package]]
397
403
  name = "dynify"
398
404
  version = "0.1.2"
@@ -1807,6 +1813,7 @@ dependencies = [
1807
1813
  "axum",
1808
1814
  "boa_engine",
1809
1815
  "dotenv",
1816
+ "dotenvy",
1810
1817
  "reqwest",
1811
1818
  "serde",
1812
1819
  "serde_json",
@@ -17,3 +17,4 @@ tracing = "0.1.43"
17
17
  tracing-subscriber = "0.3.22"
18
18
  anyhow = "1"
19
19
  boa_engine = "0.21.0"
20
+ dotenvy = "0.15"
@@ -9,11 +9,18 @@ use axum::{
9
9
  routing::any,
10
10
  Router,
11
11
  };
12
- use boa_engine::{Context, JsValue, Source};
12
+ use boa_engine::{
13
+ Context,
14
+ Source
15
+ };
16
+
13
17
  use serde::Deserialize;
14
18
  use serde_json::Value;
15
19
  use tokio::net::TcpListener;
16
20
 
21
+
22
+
23
+
17
24
  // ----------------------
18
25
  // Route structures
19
26
  // ----------------------
@@ -63,72 +70,88 @@ async fn dynamic_handler_inner(
63
70
  // ACTION ROUTE
64
71
  // --------------------------
65
72
  "action" => {
66
- let action_name = route.value.as_str().unwrap_or("").trim();
67
- if action_name.is_empty() {
68
- return (
69
- StatusCode::INTERNAL_SERVER_ERROR,
70
- "Invalid action name",
71
- ).into_response();
72
- }
73
+ let action_name = route.value.as_str().unwrap_or("").trim();
74
+ if action_name.is_empty() {
75
+ return (
76
+ StatusCode::INTERNAL_SERVER_ERROR,
77
+ "Invalid action name",
78
+ )
79
+ .into_response();
80
+ }
73
81
 
74
- // correct action path (bundle path)
75
- let action_path = state
76
- .project_root
77
- .join("server")
78
- .join("actions")
79
- .join(format!("{}.jsbundle", action_name));
80
-
81
- if !action_path.exists() {
82
- return (
83
- StatusCode::NOT_FOUND,
84
- format!("Action bundle not found: {:?}", action_path),
85
- )
86
- .into_response();
87
- }
82
+ // correct action path
83
+ let action_path = state
84
+ .project_root
85
+ .join("server")
86
+ .join("actions")
87
+ .join(format!("{}.jsbundle", action_name));
88
+
89
+ if !action_path.exists() {
90
+ return (
91
+ StatusCode::NOT_FOUND,
92
+ format!("Action bundle not found: {:?}", action_path),
93
+ )
94
+ .into_response();
95
+ }
88
96
 
89
- // read JS bundle
90
- let js_code = match fs::read_to_string(action_path) {
91
- Ok(v) => v,
92
- Err(e) => {
93
- return (
94
- StatusCode::INTERNAL_SERVER_ERROR,
95
- format!("Failed reading action bundle: {}", e),
96
- ).into_response()
97
- }
98
- };
99
-
100
- // inject request
101
- let injected = format!(
102
- "const __titan_req = {};\n{};\n{}(__titan_req);",
103
- body_str,
104
- js_code,
105
- action_name
106
- );
107
-
108
- // exec in Boa
109
- let mut ctx = Context::default();
110
- let result = match ctx.eval(Source::from_bytes(&injected)) {
111
- Ok(v) => v,
112
- Err(e) => {
113
- return (
114
- StatusCode::INTERNAL_SERVER_ERROR,
115
- format!("JS execution error: {}", e.to_string()),
116
- )
117
- .into_response();
118
- }
119
- };
120
-
121
- // convert JsValue -> JSON (Boa returns Option<Value>)
122
- let result_json: Value = match result.to_json(&mut ctx) {
123
- Ok(Some(v)) => v,
124
- Ok(None) => serde_json::json!({ "error": "JS returned undefined" }),
125
- Err(e) => json_error(e.to_string()),
126
- };
127
-
128
-
129
-
130
- return Json(result_json).into_response();
131
- }
97
+ // read JS bundle
98
+ let js_code = match fs::read_to_string(action_path) {
99
+ Ok(v) => v,
100
+ Err(e) => {
101
+ return (
102
+ StatusCode::INTERNAL_SERVER_ERROR,
103
+ format!("Failed reading action bundle: {}", e),
104
+ )
105
+ .into_response();
106
+ }
107
+ };
108
+
109
+ // ---------------------------
110
+ // ENV injection (correct way)
111
+ // ---------------------------
112
+ let mut env_map = serde_json::Map::new();
113
+ for (k, v) in std::env::vars() {
114
+ env_map.insert(k, Value::String(v));
115
+ }
116
+
117
+ let env_json = serde_json::Value::Object(env_map);
118
+
119
+ // Final JS code to execute in Boa
120
+ let injected = format!(
121
+ r#"
122
+ globalThis.process = {{
123
+ env: {}
124
+ }};
125
+
126
+ const __titan_req = {};
127
+ {};
128
+
129
+ {}(__titan_req);
130
+ "#,
131
+ env_json.to_string(),
132
+ body_str,
133
+ js_code,
134
+ action_name
135
+ );
136
+
137
+ // Execute JS safely
138
+ let mut ctx = Context::default();
139
+ let result = match ctx.eval(Source::from_bytes(&injected)) {
140
+ Ok(v) => v,
141
+ Err(e) => {
142
+ return Json(json_error(e.to_string())).into_response();
143
+ }
144
+ };
145
+
146
+ // Convert Boa -> JSON
147
+ let result_json: Value = match result.to_json(&mut ctx) {
148
+ Ok(Some(v)) => v,
149
+ Ok(None) => serde_json::json!({ "error": "JS returned undefined" }),
150
+ Err(e) => json_error(e.to_string()),
151
+ };
152
+
153
+ return Json(result_json).into_response();
154
+ }
132
155
 
133
156
  // --------------------------
134
157
  // STATIC JSON
@@ -154,11 +177,15 @@ fn json_error(msg: String) -> Value {
154
177
  serde_json::json!({ "error": msg })
155
178
  }
156
179
 
180
+
181
+
157
182
  // ----------------------
158
183
  // MAIN
159
184
  // ----------------------
160
185
  #[tokio::main]
161
186
  async fn main() -> Result<()> {
187
+ dotenvy::dotenv().ok();
188
+
162
189
  let raw = fs::read_to_string("./routes.json").unwrap_or_else(|_| "{}".to_string());
163
190
  let json: Value = serde_json::from_str(&raw).unwrap_or_default();
164
191
 
@@ -190,20 +217,15 @@ async fn main() -> Result<()> {
190
217
  //
191
218
  // TITAN BANNER
192
219
  //
193
- println!(
194
- "\n\x1b[38;5;208m\
195
- ████████╗██╗████████╗ █████╗ ███╗ ██╗\n\
196
- ╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗ ██║\n\
197
- ██║ ██║ ██║ ███████║██╔██╗ ██║\n\
198
- ██║ ██║ ██║ ██╔══██║██║╚██╗██║\n\
199
- ██║ ██║ ██║ ██║ ██║██║ ╚████║\n\
200
- ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝\x1b[0m\n"
201
- );
220
+ println!("\n\x1b[38;5;208m████████╗██╗████████╗ █████╗ ███╗ ██╗");
221
+ println!("╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗ ██║");
222
+ println!(" ██║ ██║ ██║ ███████║██╔██╗ ██║");
223
+ println!(" ██║ ██║ ██║ ██╔══██║██║╚██╗██║");
224
+ println!(" ██║ ██║ ██║ ██║ ██║██║ ╚████║");
225
+ println!(" ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝\x1b[0m\n");
226
+
227
+ println!("\x1b[38;5;39mTitan server running at:\x1b[0m http://localhost:{}", port);
202
228
 
203
- println!(
204
- "\x1b[38;5;39mTitan server running at:\x1b[0m \x1b[97mhttp://localhost:{}\x1b[0m\n",
205
- port
206
- );
207
229
  axum::serve(listener, app).await?;
208
230
  Ok(())
209
231
  }
@@ -1,5 +1,6 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import { bundle } from "./bundle.js";
3
4
 
4
5
  const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
5
6
  const green = (t) => `\x1b[32m${t}\x1b[0m`;
@@ -38,8 +39,15 @@ const t = {
38
39
  return addRoute("POST", route);
39
40
  },
40
41
 
41
- start(port = 3000, msg = "") {
42
+ async start(port = 3000, msg = "") {
43
+
44
+
45
+ console.log(cyan("[Titan] Bundling actions..."));
46
+ await bundle();
47
+
42
48
  const base = path.join(process.cwd(), "server");
49
+ fs.mkdirSync(base, { recursive: true });
50
+
43
51
 
44
52
  fs.writeFileSync(
45
53
  path.join(base, "routes.json"),
@@ -52,6 +60,7 @@ const t = {
52
60
  );
53
61
 
54
62
  console.log(green(`Titan: routes.json + action_map.json written -> ${base}`));
63
+
55
64
  if (msg) console.log(cyan(msg));
56
65
  }
57
66
  };