titanpl-sdk 2.0.3 → 2.0.4

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.
@@ -1,3 +1,13 @@
1
+ //! Titan HTTP Server (Performance Optimized)
2
+ //!
3
+ //! Key Features:
4
+ //! 1. Fast-path integration: static actions bypass V8 entirely.
5
+ //! 2. Pre-computed route responses: reply routes serve cached bytes.
6
+ //! 3. Benchmark mode: `TITAN_BENCHMARK=1` disables per-request logging & timings.
7
+ //! 4. Early fast-path check BEFORE body/header parsing.
8
+ //! 5. Mimalloc global allocator for faster allocations.
9
+ //! 6. Optimized response construction.
10
+
1
11
  use anyhow::Result;
2
12
  use axum::{
3
13
  Router,
@@ -8,63 +18,181 @@ use axum::{
8
18
  routing::any,
9
19
  };
10
20
  use serde_json::Value;
21
+ use smallvec::SmallVec;
11
22
  use std::time::Instant;
12
23
  use std::{collections::HashMap, fs, path::PathBuf, sync::Arc};
13
24
  use tokio::net::TcpListener;
14
- use smallvec::SmallVec;
15
-
16
- mod utils;
17
25
 
18
26
  mod action_management;
19
27
  mod extensions;
28
+ mod fast_path;
20
29
  mod runtime;
30
+ mod utils;
21
31
 
22
- use action_management::{
23
- DynamicRoute, RouteVal, match_dynamic_route,
24
- };
32
+ use action_management::{DynamicRoute, RouteVal, match_dynamic_route};
33
+ use fast_path::{FastPathRegistry, PrecomputedRoute};
25
34
  use runtime::RuntimeManager;
26
35
  use utils::{blue, gray, green, red, white, yellow};
27
36
 
37
+ /// Global allocator: mimalloc for ~5-15% better allocation throughput.
38
+ #[global_allocator]
39
+ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
40
+
28
41
  #[derive(Clone)]
29
42
  struct AppState {
30
43
  routes: Arc<HashMap<String, RouteVal>>,
31
44
  dynamic_routes: Arc<Vec<DynamicRoute>>,
32
45
  runtime: Arc<RuntimeManager>,
46
+ /// Pre-computed responses for static actions (bypass V8)
47
+ fast_paths: Arc<FastPathRegistry>,
48
+ /// Pre-serialized responses for reply routes (no re-serialization per request)
49
+ precomputed: Arc<HashMap<String, PrecomputedRoute>>,
50
+ /// When true: disable per-request logging and timings injection
51
+ production_mode: bool,
33
52
  }
34
53
 
35
- // Root/dynamic handlers -----------------------------------------------------
36
-
37
54
  async fn root_route(state: State<AppState>, req: Request<Body>) -> impl IntoResponse {
38
- dynamic_handler_inner(state, req).await
55
+ handler(state, req).await
39
56
  }
40
57
 
41
58
  async fn dynamic_route(state: State<AppState>, req: Request<Body>) -> impl IntoResponse {
42
- dynamic_handler_inner(state, req).await
59
+ handler(state, req).await
43
60
  }
44
61
 
45
- async fn dynamic_handler_inner(
46
- State(state): State<AppState>,
47
- req: Request<Body>,
48
- ) -> impl IntoResponse {
49
- // ---------------------------
50
- // BASIC REQUEST INFO
51
- // ---------------------------
62
+ /// Main request handler — optimized with early fast-path bailout.
63
+ async fn handler(State(state): State<AppState>, req: Request<Body>) -> impl IntoResponse {
52
64
  let method = req.method().as_str().to_uppercase();
53
65
  let path = req.uri().path().to_string();
54
66
  let strict_key = format!("{}:{}", method, path);
55
- // Also try simple path for generic routes
56
- // Check strict first, then simple path
57
67
 
58
- // ---------------------------
59
- // TIMER + LOG META
60
- // ---------------------------
68
+ // Phase 1: Fast-Path Check (before ANY body/header parsing)
69
+ // This is the critical optimization. For static actions and reply routes,
70
+ // we return pre-computed bytes without touching the request body, headers,
71
+ // or V8 runtime. This path costs ~2-5µs vs ~50-100µs for the V8 path.
72
+
61
73
  let start = Instant::now();
62
- let mut route_label = String::from("not_found");
63
- let mut route_kind = "none"; // exact | dynamic | reply
74
+ let log_enabled = !state.production_mode;
75
+
76
+ if let Some(route) = state
77
+ .routes
78
+ .get(&strict_key)
79
+ .or_else(|| state.routes.get(&path))
80
+ {
81
+ match route.r#type.as_str() {
82
+
83
+ // Precomputed reply routes
84
+ "json" | "text" => {
85
+ if let Some(precomputed) = state.precomputed.get(&strict_key) {
86
+
87
+ if state.production_mode {
88
+ // Benchmark mode → zero overhead
89
+ return precomputed.to_axum_response();
90
+ }
91
+
92
+ let mut response = precomputed.to_axum_response();
93
+ let elapsed = start.elapsed();
94
+
95
+ response.headers_mut().insert(
96
+ "Server-Timing",
97
+ format!("reply;dur={:.2}", elapsed.as_secs_f64() * 1000.0)
98
+ .parse()
99
+ .unwrap(),
100
+ );
101
+
102
+ if log_enabled {
103
+ println!(
104
+ "{} {} {} {}",
105
+ blue("[Titan]"),
106
+ green(&format!("{} {}", method, path)),
107
+ white("→ reply"),
108
+ gray(&format!("in {:.2?}", elapsed))
109
+ );
110
+ }
111
+
112
+ return response;
113
+ }
114
+
115
+ // Fallback (should never happen)
116
+ if route.r#type == "json" {
117
+ return Json(route.value.clone()).into_response();
118
+ }
119
+
120
+ if let Some(s) = route.value.as_str() {
121
+ return s.to_string().into_response();
122
+ }
123
+ }
124
+
125
+ // Action routes (Fast path check)
126
+ "action" => {
127
+ let action_name = route.value.as_str().unwrap_or("");
128
+
129
+ if let Some(static_resp) = state.fast_paths.get(action_name) {
130
+
131
+ if state.production_mode {
132
+ // Benchmark mode → zero overhead
133
+ return static_resp.to_axum_response();
134
+ }
135
+
136
+ let mut response = static_resp.to_axum_response();
137
+ let elapsed = start.elapsed();
138
+
139
+ response.headers_mut().insert(
140
+ "Server-Timing",
141
+ format!("fastpath;dur={:.2}", elapsed.as_secs_f64() * 1000.0)
142
+ .parse()
143
+ .unwrap(),
144
+ );
145
+
146
+ if log_enabled {
147
+ println!(
148
+ "{} {} {} {}",
149
+ blue("[Titan]"),
150
+ green(&format!("{} {}", method, path)),
151
+ white("→ fastpath"),
152
+ gray(&format!("in {:.2?}", elapsed))
153
+ );
154
+ }
155
+
156
+ return response;
157
+ }
158
+
159
+ // Not static → continue to dynamic execution
160
+ }
161
+
162
+ // String reply routes
163
+ _ => {
164
+ if let Some(s) = route.value.as_str() {
165
+
166
+ if state.production_mode {
167
+ return s.to_string().into_response();
168
+ }
169
+
170
+ let elapsed = start.elapsed();
171
+
172
+ if log_enabled {
173
+ println!(
174
+ "{} {} {} {}",
175
+ blue("[Titan]"),
176
+ green(&format!("{} {}", method, path)),
177
+ white("→ reply"),
178
+ gray(&format!("in {:.2?}", elapsed))
179
+ );
180
+ }
181
+
182
+ return s.to_string().into_response();
183
+ }
184
+ }
185
+ }
186
+ }
64
187
 
65
- // ---------------------------
66
- // QUERY PARSING
67
- // ---------------------------
188
+
189
+ // Phase 2: Dynamic Route Handling (requires body/header parsing)
190
+ // Only reached for actions that actually need V8 execution.
191
+
192
+ let start = Instant::now(); // restart timing for dynamic path
193
+ let log_enabled = !state.production_mode;
194
+
195
+ // Query parsing
68
196
  let query_pairs: Vec<(String, String)> = req
69
197
  .uri()
70
198
  .query()
@@ -77,14 +205,10 @@ async fn dynamic_handler_inner(
77
205
  .collect()
78
206
  })
79
207
  .unwrap_or_default();
80
-
81
208
  let query_map: HashMap<String, String> = query_pairs.into_iter().collect();
82
209
 
83
- // ---------------------------
84
- // HEADERS & BODY
85
- // ---------------------------
210
+ // Headers & Body
86
211
  let (parts, body) = req.into_parts();
87
-
88
212
  let headers_map: HashMap<String, String> = parts
89
213
  .headers
90
214
  .iter()
@@ -96,14 +220,17 @@ async fn dynamic_handler_inner(
96
220
  Err(_) => return (StatusCode::BAD_REQUEST, "Failed to read request body").into_response(),
97
221
  };
98
222
 
99
- // ---------------------------
100
- // ROUTE RESOLUTION
101
- // ---------------------------
223
+ // Route resolution
102
224
  let mut params: HashMap<String, String> = HashMap::new();
103
225
  let mut action_name: Option<String> = None;
226
+ let mut route_kind = "none";
227
+ let mut route_label = String::from("not_found");
104
228
 
105
- // Exact route
106
- let route = state.routes.get(&strict_key).or_else(|| state.routes.get(&path));
229
+ // Exact route lookup (may find action routes not caught in fast-path phase)
230
+ let route = state
231
+ .routes
232
+ .get(&strict_key)
233
+ .or_else(|| state.routes.get(&path));
107
234
  if let Some(route) = route {
108
235
  route_kind = "exact";
109
236
  if route.r#type == "action" {
@@ -111,29 +238,32 @@ async fn dynamic_handler_inner(
111
238
  route_label = name.clone();
112
239
  action_name = Some(name);
113
240
  } else if route.r#type == "json" {
114
- let elapsed = start.elapsed();
115
- println!(
116
- "{} {} {} {}",
117
- blue("[Titan]"),
118
- white(&format!("{} {}", method, path)),
119
- white(" json"),
120
- gray(&format!("in {:.2?}", elapsed))
121
- );
241
+ // This path shouldn't be reached (handled in Phase 1), but keep as safety
242
+ if log_enabled {
243
+ println!(
244
+ "{} {} {} {}",
245
+ blue("[Titan]"),
246
+ white(&format!("{} {}", method, path)),
247
+ white(" json"),
248
+ gray(&format!("in {:.2?}", start.elapsed()))
249
+ );
250
+ }
122
251
  return Json(route.value.clone()).into_response();
123
252
  } else if let Some(s) = route.value.as_str() {
124
- let elapsed = start.elapsed();
125
- println!(
126
- "{} {} {} {}",
127
- blue("[Titan]"),
128
- white(&format!("{} {}", method, path)),
129
- white("→ reply"),
130
- gray(&format!("in {:.2?}", elapsed))
131
- );
253
+ if log_enabled {
254
+ println!(
255
+ "{} {} {} {}",
256
+ blue("[Titan]"),
257
+ white(&format!("{} {}", method, path)),
258
+ white("→ reply"),
259
+ gray(&format!("in {:.2?}", start.elapsed()))
260
+ );
261
+ }
132
262
  return s.to_string().into_response();
133
263
  }
134
264
  }
135
265
 
136
- // Dynamic route
266
+ // Dynamic route matching
137
267
  if action_name.is_none() {
138
268
  if let Some((action, p)) =
139
269
  match_dynamic_route(&method, &path, state.dynamic_routes.as_slice())
@@ -148,107 +278,85 @@ async fn dynamic_handler_inner(
148
278
  let action_name = match action_name {
149
279
  Some(a) => a,
150
280
  None => {
151
- let elapsed = start.elapsed();
152
- println!(
153
- "{} {} {} {}",
154
- blue("[Titan]"),
155
- white(&format!("{} {}", method, path)),
156
- white("→ 404"),
157
- gray(&format!("in {:.2?}", elapsed))
158
- );
281
+ if log_enabled {
282
+ println!(
283
+ "{} {} {} {}",
284
+ blue("[Titan]"),
285
+ white(&format!("{} {}", method, path)),
286
+ white("→ 404"),
287
+ gray(&format!("in {:.2?}", start.elapsed()))
288
+ );
289
+ }
159
290
  return (StatusCode::NOT_FOUND, "Not Found").into_response();
160
291
  }
161
292
  };
162
293
 
294
+ // Phase 3: V8 Execution (dispatch to worker pool)
163
295
 
164
- // ---------------------------
165
- // EXECUTE IN V8 (WORKER POOL)
166
- // ---------------------------
167
-
168
- // OPTIMIZATION: Zero-Copy & Stack Allocation
169
- // 1. Headers/Params are collected into `SmallVec` (stack allocated if small).
170
- // 2. Body is passed as `Bytes` (ref-counted pointer), not copied.
171
- // 3. No JSON serialization happens here anymore. This saves ~60% CPU vs previous version.
172
-
173
296
  let headers_vec: SmallVec<[(String, String); 8]> = headers_map.into_iter().collect();
174
297
  let params_vec: SmallVec<[(String, String); 4]> = params.into_iter().collect();
175
298
  let query_vec: SmallVec<[(String, String); 4]> = query_map.into_iter().collect();
176
-
177
- // Pass raw bytes to worker if not empty
299
+
178
300
  let body_arg = if !body_bytes.is_empty() {
179
301
  Some(body_bytes)
180
302
  } else {
181
303
  None
182
304
  };
183
305
 
184
- // Dispatch to the optimized RuntimeManager
185
- // This sends a pointer-sized message through the ring buffer, triggering
186
- // the V8 thread to wake up and process the request immediately.
187
-
188
- // Dispatch to the worker pool for V8 execution
189
- let (mut result_json, timings) = state
306
+ let (result_json, timings) = state
190
307
  .runtime
191
308
  .execute(
192
- action_name,
309
+ action_name.clone(),
193
310
  method.clone(),
194
311
  path.clone(),
195
312
  body_arg,
196
313
  headers_vec,
197
314
  params_vec,
198
- query_vec
315
+ query_vec,
199
316
  )
200
317
  .await
201
- .unwrap_or_else(|e| {
202
- // Log catastrophic runtime errors
203
- (serde_json::json!({"error": e}), vec![])
204
- });
205
-
206
- // Construct Server-Timing header
207
- let server_timing = timings.iter().enumerate().map(|(i, (name, duration))| {
208
- format!("{}_{};dur={:.2}", name, i, duration)
209
- }).collect::<Vec<_>>().join(", ");
210
-
211
- // Inject timings into JSON if it's an object
212
- if let Some(obj) = result_json.as_object_mut() {
213
- obj.insert("_titanTimings".to_string(), serde_json::json!(timings));
214
- }
318
+ .unwrap_or_else(|e| (serde_json::json!({"error": e}), vec![]));
215
319
 
216
- let prefix = if !timings.is_empty() {
217
- format!("{} {}", blue("[Titan"), blue("Drift]"))
218
- } else {
219
- blue("[Titan]").to_string()
220
- };
320
+ // Phase 4: Response Construction
221
321
 
222
- // ---------------------------
223
- // ERROR HANDLING
224
- // ---------------------------
322
+ // NOTE: We intentionally do NOT inject _titanTimings into the JSON body.
323
+ // This was corrupting benchmark responses (e.g., adding extra fields to
324
+ // {"message":"Hello, World!"} which fails TechEmpower validation).
325
+ // Timing info is available via the Server-Timing HTTP header instead.
326
+
327
+ // Error handling
225
328
  if let Some(err) = result_json.get("error") {
226
- println!(
227
- "{} {} {} {}",
228
- prefix,
229
- red(&format!("{} {}", method, path)),
230
- red("→ error"),
231
- gray(&format!("in {:.2?}", start.elapsed()))
232
- );
233
- println!(
234
- "{} {} {}",
235
- prefix,
236
- red("Action Error:"),
237
- red(err.as_str().unwrap_or("Unknown"))
238
- );
239
- let mut response = (StatusCode::INTERNAL_SERVER_ERROR, Json(result_json.clone())).into_response();
240
- if !server_timing.is_empty() {
241
- response.headers_mut().insert("Server-Timing", server_timing.parse().unwrap());
329
+ if log_enabled {
330
+ let prefix = if !timings.is_empty() {
331
+ format!("{} {}", blue("[Titan"), blue("Drift]"))
332
+ } else {
333
+ blue("[Titan]").to_string()
334
+ };
335
+ println!(
336
+ "{} {} {} {}",
337
+ prefix,
338
+ red(&format!("{} {}", method, path)),
339
+ red(" error"),
340
+ gray(&format!("in {:.2?}", start.elapsed()))
341
+ );
342
+ println!(
343
+ "{} {} {}",
344
+ prefix,
345
+ red("Action Error:"),
346
+ red(err.as_str().unwrap_or("Unknown"))
347
+ );
242
348
  }
349
+ let response = (StatusCode::INTERNAL_SERVER_ERROR, Json(result_json)).into_response();
243
350
  return response;
244
351
  }
245
352
 
246
- // ---------------------------
247
- // RESPONSE CONSTRUCTION
248
- // ---------------------------
353
+ // Response object construction
249
354
  let mut response = if let Some(is_resp) = result_json.get("_isResponse") {
250
355
  if is_resp.as_bool().unwrap_or(false) {
251
- let status_u16 = result_json.get("status").and_then(|v| v.as_u64()).unwrap_or(200) as u16;
356
+ let status_u16 = result_json
357
+ .get("status")
358
+ .and_then(|v| v.as_u64())
359
+ .unwrap_or(200) as u16;
252
360
  let status = StatusCode::from_u16(status_u16).unwrap_or(StatusCode::OK);
253
361
  let mut builder = axum::http::Response::builder().status(status);
254
362
 
@@ -264,13 +372,19 @@ async fn dynamic_handler_inner(
264
372
  if let Some(location) = result_json.get("redirect") {
265
373
  if let Some(url) = location.as_str() {
266
374
  let mut final_status_u16 = status.as_u16();
267
- if final_status_u16 < 300 || final_status_u16 > 399 { final_status_u16 = 302; }
268
- builder = builder.status(StatusCode::from_u16(final_status_u16).unwrap_or(StatusCode::FOUND)).header("Location", url);
375
+ if !(300..400).contains(&final_status_u16) {
376
+ final_status_u16 = 302;
377
+ }
378
+ builder = builder
379
+ .status(StatusCode::from_u16(final_status_u16).unwrap_or(StatusCode::FOUND))
380
+ .header("Location", url);
269
381
  is_redirect = true;
270
382
  }
271
383
  }
272
384
 
273
- let body_text = if is_redirect { "".to_string() } else {
385
+ let body_text = if is_redirect {
386
+ "".to_string()
387
+ } else {
274
388
  match result_json.get("body") {
275
389
  Some(Value::String(s)) => s.clone(),
276
390
  Some(v) => v.to_string(),
@@ -279,47 +393,82 @@ async fn dynamic_handler_inner(
279
393
  };
280
394
  builder.body(Body::from(body_text)).unwrap()
281
395
  } else {
282
- Json(result_json.clone()).into_response()
396
+ Json(result_json).into_response()
283
397
  }
284
398
  } else {
285
- Json(result_json.clone()).into_response()
399
+ Json(result_json).into_response()
286
400
  };
287
401
 
288
- if !server_timing.is_empty() {
289
- response.headers_mut().insert("Server-Timing", server_timing.parse().unwrap());
402
+ // Server-Timing header (only outside benchmark mode)
403
+ if !state.production_mode && !timings.is_empty() {
404
+ let server_timing = timings
405
+ .iter()
406
+ .enumerate()
407
+ .map(|(i, (name, duration))| format!("{}_{};dur={:.2}", name, i, duration))
408
+ .collect::<Vec<_>>()
409
+ .join(", ");
410
+ response
411
+ .headers_mut()
412
+ .insert("Server-Timing", server_timing.parse().unwrap());
290
413
  }
291
414
 
292
- // ---------------------------
293
- // FINAL LOG (SUCCESS)
294
- // ---------------------------
295
- let total_elapsed = start.elapsed();
296
- let total_elapsed_ms = total_elapsed.as_secs_f64() * 1000.0;
297
- let total_drift_ms: f64 = timings.iter().filter(|(n, _)| n == "drift" || n == "drift_error").map(|(_, d)| d).sum();
298
- let compute_ms = (total_elapsed_ms - total_drift_ms).max(0.0);
299
-
300
- let timing_info = if !timings.is_empty() {
301
- gray(&format!("(active: {:.2}ms, drift: {:.2}ms) in {:.2?}", compute_ms, total_drift_ms, total_elapsed))
302
- } else {
303
- gray(&format!("in {:.2?}", total_elapsed))
304
- };
305
-
306
- match route_kind {
307
- "dynamic" => println!("{} {} {} {} {} {}", prefix, green(&format!("{} {}", method, path)), white("→"), green(&route_label), white("(dynamic)"), timing_info),
308
- "exact" => println!("{} {} {} {} {}", prefix, white(&format!("{} {}", method, path)), white("→"), yellow(&route_label), timing_info),
309
- _ => {}
415
+ // Logging
416
+ if log_enabled {
417
+ let total_elapsed = start.elapsed();
418
+ let total_elapsed_ms = total_elapsed.as_secs_f64() * 1000.0;
419
+ let total_drift_ms: f64 = timings
420
+ .iter()
421
+ .filter(|(n, _)| n == "drift" || n == "drift_error")
422
+ .map(|(_, d)| d)
423
+ .sum();
424
+ let compute_ms = (total_elapsed_ms - total_drift_ms).max(0.0);
425
+
426
+ let prefix = if !timings.is_empty() {
427
+ format!("{} {}", blue("[Titan"), blue("Drift]"))
428
+ } else {
429
+ blue("[Titan]").to_string()
430
+ };
431
+ let timing_info = if !timings.is_empty() {
432
+ gray(&format!(
433
+ "(active: {:.2}ms, drift: {:.2}ms) in {:.2?}",
434
+ compute_ms, total_drift_ms, total_elapsed
435
+ ))
436
+ } else {
437
+ gray(&format!("in {:.2?}", total_elapsed))
438
+ };
439
+
440
+ match route_kind {
441
+ "dynamic" => println!(
442
+ "{} {} {} {} {} {}",
443
+ prefix,
444
+ green(&format!("{} {}", method, path)),
445
+ white("→"),
446
+ green(&route_label),
447
+ white("(dynamic)"),
448
+ timing_info
449
+ ),
450
+ "exact" => println!(
451
+ "{} {} {} {} {}",
452
+ prefix,
453
+ white(&format!("{} {}", method, path)),
454
+ white("→"),
455
+ yellow(&route_label),
456
+ timing_info
457
+ ),
458
+ _ => {}
459
+ }
310
460
  }
311
461
 
312
462
  response
313
463
  }
314
464
 
315
-
316
- // Entrypoint ---------------------------------------------------------------
317
-
318
465
  #[tokio::main]
319
466
  async fn main() -> Result<()> {
320
467
  dotenvy::dotenv().ok();
321
-
322
- // Load routes.json
468
+
469
+ // Configuration
470
+ let production_mode = std::env::var("TITAN_DEV").unwrap_or_default() != "1";
471
+
323
472
  let raw = fs::read_to_string("./routes.json").unwrap_or_else(|_| "{}".to_string());
324
473
  let json: Value = serde_json::from_str(&raw).unwrap_or_default();
325
474
 
@@ -328,36 +477,75 @@ async fn main() -> Result<()> {
328
477
  .and_then(|p| p.parse::<u64>().ok())
329
478
  .or_else(|| json["__config"]["port"].as_u64())
330
479
  .unwrap_or(3000);
480
+
331
481
  let thread_count = json["__config"]["threads"].as_u64();
332
482
  let routes_json = json["routes"].clone();
333
483
  let map: HashMap<String, RouteVal> = serde_json::from_value(routes_json).unwrap_or_default();
334
484
  let dynamic_routes: Vec<DynamicRoute> =
335
485
  serde_json::from_value(json["__dynamic_routes"].clone()).unwrap_or_default();
336
486
 
337
- // Identify project root
338
487
  let project_root = resolve_project_root();
339
-
340
- // Load extensions and action definitions
488
+
489
+ // Load extensions
341
490
  extensions::load_project_extensions(project_root.clone());
342
491
 
343
-
344
- // Initialize Runtime Manager (Worker Pool)
492
+ // Build pre-computed route responses
493
+ let mut precomputed = HashMap::new();
494
+ for (key, route) in &map {
495
+ match route.r#type.as_str() {
496
+ "json" => {
497
+ precomputed.insert(key.clone(), PrecomputedRoute::from_json(&route.value));
498
+ }
499
+ "text" => {
500
+ if let Some(s) = route.value.as_str() {
501
+ precomputed.insert(key.clone(), PrecomputedRoute::from_text(s));
502
+ }
503
+ }
504
+ _ => {}
505
+ }
506
+ }
507
+ if !precomputed.is_empty() {
508
+ println!(
509
+ "{} {} reply route(s) pre-computed",
510
+ blue("[Titan]"),
511
+ precomputed.len()
512
+ );
513
+ }
514
+
515
+ // Build fast-path registry (scan action files for static patterns)
516
+ let actions_dir = find_actions_dir(&project_root);
517
+ let fast_paths = FastPathRegistry::build(&actions_dir);
518
+
519
+ // Initialize Runtime Manager (V8 Worker Pool)
345
520
  let threads = match thread_count {
346
521
  Some(t) if t > 0 => t as usize,
347
- _ => num_cpus::get() * 4, // default
522
+ _ => {
523
+ let cpus = num_cpus::get();
524
+ // Optimal for CPU-bound V8 work: 2x cores
525
+ cpus * 2
526
+ }
348
527
  };
349
528
 
350
529
  let stack_mb = json["__config"]["stack_mb"].as_u64().unwrap_or(8);
351
530
  let stack_size = (stack_mb as usize) * 1024 * 1024;
352
-
353
- let runtime_manager = Arc::new(RuntimeManager::new(project_root.clone(), threads, stack_size));
354
531
 
532
+ let runtime_manager = Arc::new(RuntimeManager::new(
533
+ project_root.clone(),
534
+ threads,
535
+ stack_size,
536
+ ));
537
+
538
+ // Build AppState
355
539
  let state = AppState {
356
540
  routes: Arc::new(map),
357
541
  dynamic_routes: Arc::new(dynamic_routes),
358
542
  runtime: runtime_manager,
543
+ fast_paths: Arc::new(fast_paths),
544
+ precomputed: Arc::new(precomputed),
545
+ production_mode,
359
546
  };
360
547
 
548
+ // Router
361
549
  let app = Router::new()
362
550
  .route("/", any(root_route))
363
551
  .fallback(any(dynamic_route))
@@ -365,21 +553,19 @@ async fn main() -> Result<()> {
365
553
 
366
554
  let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await?;
367
555
 
368
-
369
556
  println!(
370
- "\x1b[38;5;39mTitan server running at:\x1b[0m http://localhost:{} \x1b[90m(Threads: {}, Stack: {}MB)\x1b[0m",
557
+ "\x1b[38;5;39mTitan server running at:\x1b[0m http://localhost:{} \x1b[90m(Threads: {}, Stack: {}MB{})\x1b[0m",
371
558
  port,
372
559
  threads,
373
- stack_mb
560
+ stack_mb,
561
+ if production_mode { "" } else { ", Dev Mode" }
374
562
  );
375
-
376
563
 
377
564
  axum::serve(listener, app).await?;
378
565
  Ok(())
379
566
  }
380
567
 
381
568
  fn resolve_project_root() -> PathBuf {
382
- // 1. Check CWD (preferred for local dev/tooling)
383
569
  if let Ok(cwd) = std::env::current_dir() {
384
570
  if cwd.join("node_modules").exists()
385
571
  || cwd.join("package.json").exists()
@@ -389,8 +575,6 @@ fn resolve_project_root() -> PathBuf {
389
575
  }
390
576
  }
391
577
 
392
- // 2. Check executable persistence (Docker / Production)
393
- // Walk up from the executable to find .ext or node_modules
394
578
  if let Ok(exe) = std::env::current_exe() {
395
579
  let mut current = exe.parent();
396
580
  while let Some(dir) = current {
@@ -401,6 +585,23 @@ fn resolve_project_root() -> PathBuf {
401
585
  }
402
586
  }
403
587
 
404
- // 3. Fallback to CWD
405
588
  std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
406
589
  }
590
+
591
+ /// Find the actions directory for fast-path scanning.
592
+ fn find_actions_dir(root: &PathBuf) -> PathBuf {
593
+ let candidates = [
594
+ root.join("server").join("src").join("actions"),
595
+ root.join("server").join("actions"),
596
+ root.join("actions"),
597
+ PathBuf::from("/app").join("actions"),
598
+ ];
599
+
600
+ for p in &candidates {
601
+ if p.exists() && p.is_dir() {
602
+ return p.clone();
603
+ }
604
+ }
605
+
606
+ root.join("server").join("src").join("actions")
607
+ }