titanpl 6.0.0 → 7.0.0

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 (227) hide show
  1. package/package.json +2 -6
  2. package/packages/cli/index.js +25 -11
  3. package/packages/cli/package.json +4 -4
  4. package/packages/cli/src/commands/build-ext.js +157 -0
  5. package/packages/cli/src/commands/build.js +12 -0
  6. package/packages/cli/src/commands/create.js +160 -0
  7. package/packages/cli/src/commands/init.js +5 -11
  8. package/packages/cli/src/commands/run-ext.js +104 -0
  9. package/{titanpl-sdk → packages/core-source}/LICENSE +1 -1
  10. package/packages/core-source/README.md +128 -0
  11. package/packages/core-source/V8_SERIALIZATION.md +125 -0
  12. package/packages/core-source/configure.js +50 -0
  13. package/packages/core-source/globals.d.ts +2238 -0
  14. package/packages/core-source/index.d.ts +515 -0
  15. package/packages/core-source/index.js +639 -0
  16. package/packages/core-source/jsconfig.json +12 -0
  17. package/packages/core-source/mkctx.config.json +7 -0
  18. package/packages/core-source/native/Cargo.lock +1559 -0
  19. package/packages/core-source/native/Cargo.toml +30 -0
  20. package/packages/core-source/native/src/crypto_impl.rs +139 -0
  21. package/packages/core-source/native/src/lib.rs +702 -0
  22. package/packages/core-source/native/src/storage_impl.rs +73 -0
  23. package/packages/core-source/native/src/v8_impl.rs +93 -0
  24. package/packages/core-source/package-lock.json +1464 -0
  25. package/packages/core-source/package.json +53 -0
  26. package/packages/core-source/tests/buffer.test.js +78 -0
  27. package/packages/core-source/tests/cookies.test.js +117 -0
  28. package/packages/core-source/tests/crypto.test.js +142 -0
  29. package/packages/core-source/tests/fs.test.js +176 -0
  30. package/packages/core-source/tests/ls.test.js +149 -0
  31. package/packages/core-source/tests/net.test.js +84 -0
  32. package/packages/core-source/tests/os.test.js +81 -0
  33. package/packages/core-source/tests/path.test.js +102 -0
  34. package/packages/core-source/tests/response.test.js +146 -0
  35. package/packages/core-source/tests/session.test.js +110 -0
  36. package/packages/core-source/tests/setup.js +325 -0
  37. package/packages/core-source/tests/time.test.js +57 -0
  38. package/packages/core-source/tests/url.test.js +82 -0
  39. package/packages/core-source/titan-ext.d.ts +2 -0
  40. package/packages/core-source/titan.json +9 -0
  41. package/packages/core-source/vitest.config.js +8 -0
  42. package/packages/engine-darwin-arm64/README.md +0 -2
  43. package/packages/engine-darwin-arm64/package.json +1 -1
  44. package/packages/engine-linux-x64/README.md +0 -2
  45. package/packages/engine-linux-x64/package.json +1 -1
  46. package/packages/engine-win32-x64/README.md +0 -1
  47. package/packages/engine-win32-x64/bin/titan-server.exe +0 -0
  48. package/packages/engine-win32-x64/package.json +1 -1
  49. package/packages/native/README.md +0 -1
  50. package/packages/native/index.d.ts +25 -4
  51. package/packages/native/index.js +7 -0
  52. package/packages/native/package.json +2 -2
  53. package/packages/native/t.native.d.ts +167 -2
  54. package/packages/packet/index.js +103 -94
  55. package/packages/packet/package.json +1 -1
  56. package/packages/route/package.json +1 -1
  57. package/packages/sdk/index.js +2 -0
  58. package/packages/sdk/package.json +18 -0
  59. package/packages/sdk/test/index.js +120 -0
  60. package/templates/common/_tanfig.json +19 -13
  61. package/templates/extension/index.d.ts +26 -22
  62. package/templates/extension/index.js +15 -15
  63. package/templates/extension/native/Cargo.toml +5 -3
  64. package/templates/extension/native/src/lib.rs +2 -3
  65. package/templates/extension/package.json +10 -20
  66. package/templates/extension/titan.json +5 -16
  67. package/templates/extension/utils/registerExtension.js +44 -0
  68. package/templates/js/package.json +8 -8
  69. package/templates/rust-js/package.json +5 -5
  70. package/templates/rust-ts/package.json +5 -5
  71. package/templates/ts/package.json +8 -8
  72. package/packages/packet/node_modules/typescript/LICENSE.txt +0 -55
  73. package/packages/packet/node_modules/typescript/README.md +0 -50
  74. package/packages/packet/node_modules/typescript/SECURITY.md +0 -41
  75. package/packages/packet/node_modules/typescript/ThirdPartyNoticeText.txt +0 -193
  76. package/packages/packet/node_modules/typescript/bin/tsc +0 -2
  77. package/packages/packet/node_modules/typescript/bin/tsserver +0 -2
  78. package/packages/packet/node_modules/typescript/lib/_tsc.js +0 -133818
  79. package/packages/packet/node_modules/typescript/lib/_tsserver.js +0 -659
  80. package/packages/packet/node_modules/typescript/lib/_typingsInstaller.js +0 -222
  81. package/packages/packet/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
  82. package/packages/packet/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
  83. package/packages/packet/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
  84. package/packages/packet/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
  85. package/packages/packet/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
  86. package/packages/packet/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
  87. package/packages/packet/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
  88. package/packages/packet/node_modules/typescript/lib/lib.d.ts +0 -22
  89. package/packages/packet/node_modules/typescript/lib/lib.decorators.d.ts +0 -384
  90. package/packages/packet/node_modules/typescript/lib/lib.decorators.legacy.d.ts +0 -22
  91. package/packages/packet/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +0 -41
  92. package/packages/packet/node_modules/typescript/lib/lib.dom.d.ts +0 -39429
  93. package/packages/packet/node_modules/typescript/lib/lib.dom.iterable.d.ts +0 -571
  94. package/packages/packet/node_modules/typescript/lib/lib.es2015.collection.d.ts +0 -147
  95. package/packages/packet/node_modules/typescript/lib/lib.es2015.core.d.ts +0 -597
  96. package/packages/packet/node_modules/typescript/lib/lib.es2015.d.ts +0 -28
  97. package/packages/packet/node_modules/typescript/lib/lib.es2015.generator.d.ts +0 -77
  98. package/packages/packet/node_modules/typescript/lib/lib.es2015.iterable.d.ts +0 -605
  99. package/packages/packet/node_modules/typescript/lib/lib.es2015.promise.d.ts +0 -81
  100. package/packages/packet/node_modules/typescript/lib/lib.es2015.proxy.d.ts +0 -128
  101. package/packages/packet/node_modules/typescript/lib/lib.es2015.reflect.d.ts +0 -144
  102. package/packages/packet/node_modules/typescript/lib/lib.es2015.symbol.d.ts +0 -46
  103. package/packages/packet/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +0 -326
  104. package/packages/packet/node_modules/typescript/lib/lib.es2016.array.include.d.ts +0 -116
  105. package/packages/packet/node_modules/typescript/lib/lib.es2016.d.ts +0 -21
  106. package/packages/packet/node_modules/typescript/lib/lib.es2016.full.d.ts +0 -23
  107. package/packages/packet/node_modules/typescript/lib/lib.es2016.intl.d.ts +0 -31
  108. package/packages/packet/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +0 -21
  109. package/packages/packet/node_modules/typescript/lib/lib.es2017.d.ts +0 -26
  110. package/packages/packet/node_modules/typescript/lib/lib.es2017.date.d.ts +0 -31
  111. package/packages/packet/node_modules/typescript/lib/lib.es2017.full.d.ts +0 -23
  112. package/packages/packet/node_modules/typescript/lib/lib.es2017.intl.d.ts +0 -44
  113. package/packages/packet/node_modules/typescript/lib/lib.es2017.object.d.ts +0 -49
  114. package/packages/packet/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +0 -135
  115. package/packages/packet/node_modules/typescript/lib/lib.es2017.string.d.ts +0 -45
  116. package/packages/packet/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +0 -53
  117. package/packages/packet/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +0 -77
  118. package/packages/packet/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +0 -53
  119. package/packages/packet/node_modules/typescript/lib/lib.es2018.d.ts +0 -24
  120. package/packages/packet/node_modules/typescript/lib/lib.es2018.full.d.ts +0 -24
  121. package/packages/packet/node_modules/typescript/lib/lib.es2018.intl.d.ts +0 -83
  122. package/packages/packet/node_modules/typescript/lib/lib.es2018.promise.d.ts +0 -30
  123. package/packages/packet/node_modules/typescript/lib/lib.es2018.regexp.d.ts +0 -37
  124. package/packages/packet/node_modules/typescript/lib/lib.es2019.array.d.ts +0 -79
  125. package/packages/packet/node_modules/typescript/lib/lib.es2019.d.ts +0 -24
  126. package/packages/packet/node_modules/typescript/lib/lib.es2019.full.d.ts +0 -24
  127. package/packages/packet/node_modules/typescript/lib/lib.es2019.intl.d.ts +0 -23
  128. package/packages/packet/node_modules/typescript/lib/lib.es2019.object.d.ts +0 -33
  129. package/packages/packet/node_modules/typescript/lib/lib.es2019.string.d.ts +0 -37
  130. package/packages/packet/node_modules/typescript/lib/lib.es2019.symbol.d.ts +0 -24
  131. package/packages/packet/node_modules/typescript/lib/lib.es2020.bigint.d.ts +0 -765
  132. package/packages/packet/node_modules/typescript/lib/lib.es2020.d.ts +0 -27
  133. package/packages/packet/node_modules/typescript/lib/lib.es2020.date.d.ts +0 -42
  134. package/packages/packet/node_modules/typescript/lib/lib.es2020.full.d.ts +0 -24
  135. package/packages/packet/node_modules/typescript/lib/lib.es2020.intl.d.ts +0 -474
  136. package/packages/packet/node_modules/typescript/lib/lib.es2020.number.d.ts +0 -28
  137. package/packages/packet/node_modules/typescript/lib/lib.es2020.promise.d.ts +0 -47
  138. package/packages/packet/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +0 -99
  139. package/packages/packet/node_modules/typescript/lib/lib.es2020.string.d.ts +0 -44
  140. package/packages/packet/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +0 -41
  141. package/packages/packet/node_modules/typescript/lib/lib.es2021.d.ts +0 -23
  142. package/packages/packet/node_modules/typescript/lib/lib.es2021.full.d.ts +0 -24
  143. package/packages/packet/node_modules/typescript/lib/lib.es2021.intl.d.ts +0 -166
  144. package/packages/packet/node_modules/typescript/lib/lib.es2021.promise.d.ts +0 -48
  145. package/packages/packet/node_modules/typescript/lib/lib.es2021.string.d.ts +0 -33
  146. package/packages/packet/node_modules/typescript/lib/lib.es2021.weakref.d.ts +0 -78
  147. package/packages/packet/node_modules/typescript/lib/lib.es2022.array.d.ts +0 -121
  148. package/packages/packet/node_modules/typescript/lib/lib.es2022.d.ts +0 -25
  149. package/packages/packet/node_modules/typescript/lib/lib.es2022.error.d.ts +0 -75
  150. package/packages/packet/node_modules/typescript/lib/lib.es2022.full.d.ts +0 -24
  151. package/packages/packet/node_modules/typescript/lib/lib.es2022.intl.d.ts +0 -145
  152. package/packages/packet/node_modules/typescript/lib/lib.es2022.object.d.ts +0 -26
  153. package/packages/packet/node_modules/typescript/lib/lib.es2022.regexp.d.ts +0 -39
  154. package/packages/packet/node_modules/typescript/lib/lib.es2022.string.d.ts +0 -25
  155. package/packages/packet/node_modules/typescript/lib/lib.es2023.array.d.ts +0 -924
  156. package/packages/packet/node_modules/typescript/lib/lib.es2023.collection.d.ts +0 -21
  157. package/packages/packet/node_modules/typescript/lib/lib.es2023.d.ts +0 -22
  158. package/packages/packet/node_modules/typescript/lib/lib.es2023.full.d.ts +0 -24
  159. package/packages/packet/node_modules/typescript/lib/lib.es2023.intl.d.ts +0 -56
  160. package/packages/packet/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +0 -65
  161. package/packages/packet/node_modules/typescript/lib/lib.es2024.collection.d.ts +0 -29
  162. package/packages/packet/node_modules/typescript/lib/lib.es2024.d.ts +0 -26
  163. package/packages/packet/node_modules/typescript/lib/lib.es2024.full.d.ts +0 -24
  164. package/packages/packet/node_modules/typescript/lib/lib.es2024.object.d.ts +0 -29
  165. package/packages/packet/node_modules/typescript/lib/lib.es2024.promise.d.ts +0 -35
  166. package/packages/packet/node_modules/typescript/lib/lib.es2024.regexp.d.ts +0 -25
  167. package/packages/packet/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +0 -68
  168. package/packages/packet/node_modules/typescript/lib/lib.es2024.string.d.ts +0 -29
  169. package/packages/packet/node_modules/typescript/lib/lib.es5.d.ts +0 -4601
  170. package/packages/packet/node_modules/typescript/lib/lib.es6.d.ts +0 -23
  171. package/packages/packet/node_modules/typescript/lib/lib.esnext.array.d.ts +0 -35
  172. package/packages/packet/node_modules/typescript/lib/lib.esnext.collection.d.ts +0 -96
  173. package/packages/packet/node_modules/typescript/lib/lib.esnext.d.ts +0 -29
  174. package/packages/packet/node_modules/typescript/lib/lib.esnext.decorators.d.ts +0 -28
  175. package/packages/packet/node_modules/typescript/lib/lib.esnext.disposable.d.ts +0 -193
  176. package/packages/packet/node_modules/typescript/lib/lib.esnext.error.d.ts +0 -24
  177. package/packages/packet/node_modules/typescript/lib/lib.esnext.float16.d.ts +0 -445
  178. package/packages/packet/node_modules/typescript/lib/lib.esnext.full.d.ts +0 -24
  179. package/packages/packet/node_modules/typescript/lib/lib.esnext.intl.d.ts +0 -21
  180. package/packages/packet/node_modules/typescript/lib/lib.esnext.iterator.d.ts +0 -148
  181. package/packages/packet/node_modules/typescript/lib/lib.esnext.promise.d.ts +0 -34
  182. package/packages/packet/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +0 -25
  183. package/packages/packet/node_modules/typescript/lib/lib.scripthost.d.ts +0 -322
  184. package/packages/packet/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +0 -41
  185. package/packages/packet/node_modules/typescript/lib/lib.webworker.d.ts +0 -13150
  186. package/packages/packet/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +0 -23
  187. package/packages/packet/node_modules/typescript/lib/lib.webworker.iterable.d.ts +0 -340
  188. package/packages/packet/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
  189. package/packages/packet/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
  190. package/packages/packet/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
  191. package/packages/packet/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
  192. package/packages/packet/node_modules/typescript/lib/tsc.js +0 -8
  193. package/packages/packet/node_modules/typescript/lib/tsserver.js +0 -8
  194. package/packages/packet/node_modules/typescript/lib/tsserverlibrary.d.ts +0 -17
  195. package/packages/packet/node_modules/typescript/lib/tsserverlibrary.js +0 -21
  196. package/packages/packet/node_modules/typescript/lib/typesMap.json +0 -497
  197. package/packages/packet/node_modules/typescript/lib/typescript.d.ts +0 -11437
  198. package/packages/packet/node_modules/typescript/lib/typescript.js +0 -200276
  199. package/packages/packet/node_modules/typescript/lib/typingsInstaller.js +0 -8
  200. package/packages/packet/node_modules/typescript/lib/watchGuard.js +0 -53
  201. package/packages/packet/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
  202. package/packages/packet/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
  203. package/packages/packet/node_modules/typescript/package.json +0 -120
  204. package/titanpl-sdk/README.md +0 -111
  205. package/titanpl-sdk/assets/titanpl-sdk.png +0 -0
  206. package/titanpl-sdk/bin/run.js +0 -274
  207. package/titanpl-sdk/index.js +0 -5
  208. package/titanpl-sdk/package-lock.json +0 -28
  209. package/titanpl-sdk/package.json +0 -40
  210. package/titanpl-sdk/templates/app/actions/hello.js +0 -5
  211. package/titanpl-sdk/templates/app/app.js +0 -7
  212. package/titanpl-sdk/templates/jsconfig.json +0 -19
  213. package/titanpl-sdk/templates/server/Cargo.toml +0 -52
  214. package/titanpl-sdk/templates/server/src/action_management.rs +0 -175
  215. package/titanpl-sdk/templates/server/src/errors.rs +0 -12
  216. package/titanpl-sdk/templates/server/src/extensions/builtin.rs +0 -1060
  217. package/titanpl-sdk/templates/server/src/extensions/external.rs +0 -338
  218. package/titanpl-sdk/templates/server/src/extensions/mod.rs +0 -580
  219. package/titanpl-sdk/templates/server/src/extensions/titan_core.js +0 -249
  220. package/titanpl-sdk/templates/server/src/fast_path.rs +0 -719
  221. package/titanpl-sdk/templates/server/src/main.rs +0 -607
  222. package/titanpl-sdk/templates/server/src/runtime.rs +0 -284
  223. package/titanpl-sdk/templates/server/src/utils.rs +0 -33
  224. package/titanpl-sdk/templates/titan/bundle.js +0 -259
  225. package/titanpl-sdk/templates/titan/dev.js +0 -390
  226. package/titanpl-sdk/templates/titan/error-box.js +0 -277
  227. package/titanpl-sdk/templates/titan/titan.js +0 -129
@@ -1,607 +0,0 @@
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
-
11
- use anyhow::Result;
12
- use axum::{
13
- Router,
14
- body::{Body, to_bytes},
15
- extract::State,
16
- http::{Request, StatusCode},
17
- response::{IntoResponse, Json},
18
- routing::any,
19
- };
20
- use serde_json::Value;
21
- use smallvec::SmallVec;
22
- use std::time::Instant;
23
- use std::{collections::HashMap, fs, path::PathBuf, sync::Arc};
24
- use tokio::net::TcpListener;
25
-
26
- mod action_management;
27
- mod extensions;
28
- mod fast_path;
29
- mod runtime;
30
- mod utils;
31
-
32
- use action_management::{DynamicRoute, RouteVal, match_dynamic_route};
33
- use fast_path::{FastPathRegistry, PrecomputedRoute};
34
- use runtime::RuntimeManager;
35
- use utils::{blue, gray, green, red, white, yellow};
36
-
37
- /// Global allocator: mimalloc for ~5-15% better allocation throughput.
38
- #[global_allocator]
39
- static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
40
-
41
- #[derive(Clone)]
42
- struct AppState {
43
- routes: Arc<HashMap<String, RouteVal>>,
44
- dynamic_routes: Arc<Vec<DynamicRoute>>,
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,
52
- }
53
-
54
- async fn root_route(state: State<AppState>, req: Request<Body>) -> impl IntoResponse {
55
- handler(state, req).await
56
- }
57
-
58
- async fn dynamic_route(state: State<AppState>, req: Request<Body>) -> impl IntoResponse {
59
- handler(state, req).await
60
- }
61
-
62
- /// Main request handler — optimized with early fast-path bailout.
63
- async fn handler(State(state): State<AppState>, req: Request<Body>) -> impl IntoResponse {
64
- let method = req.method().as_str().to_uppercase();
65
- let path = req.uri().path().to_string();
66
- let strict_key = format!("{}:{}", method, path);
67
-
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
-
73
- let start = Instant::now();
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_or_else(|_| axum::http::HeaderValue::from_static("")),
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_or_else(|_| axum::http::HeaderValue::from_static("")),
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
- }
187
-
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
196
- let query_pairs: Vec<(String, String)> = req
197
- .uri()
198
- .query()
199
- .map(|q| {
200
- q.split('&')
201
- .filter_map(|pair| {
202
- let mut it = pair.splitn(2, '=');
203
- Some((it.next()?.to_string(), it.next().unwrap_or("").to_string()))
204
- })
205
- .collect()
206
- })
207
- .unwrap_or_default();
208
- let query_map: HashMap<String, String> = query_pairs.into_iter().collect();
209
-
210
- // Headers & Body
211
- let (parts, body) = req.into_parts();
212
- let headers_map: HashMap<String, String> = parts
213
- .headers
214
- .iter()
215
- .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
216
- .collect();
217
-
218
- let body_bytes = match to_bytes(body, usize::MAX).await {
219
- Ok(b) => b,
220
- Err(_) => return (StatusCode::BAD_REQUEST, "Failed to read request body").into_response(),
221
- };
222
-
223
- // Route resolution
224
- let mut params: HashMap<String, String> = HashMap::new();
225
- let mut action_name: Option<String> = None;
226
- let mut route_kind = "none";
227
- let mut route_label = String::from("not_found");
228
-
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));
234
- if let Some(route) = route {
235
- route_kind = "exact";
236
- if route.r#type == "action" {
237
- let name = route.value.as_str().unwrap_or("unknown").to_string();
238
- route_label = name.clone();
239
- action_name = Some(name);
240
- } else if route.r#type == "json" {
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
- }
251
- return Json(route.value.clone()).into_response();
252
- } else if let Some(s) = route.value.as_str() {
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
- }
262
- return s.to_string().into_response();
263
- }
264
- }
265
-
266
- // Dynamic route matching
267
- if action_name.is_none() {
268
- if let Some((action, p)) =
269
- match_dynamic_route(&method, &path, state.dynamic_routes.as_slice())
270
- {
271
- route_kind = "dynamic";
272
- route_label = action.clone();
273
- action_name = Some(action);
274
- params = p;
275
- }
276
- }
277
-
278
- let action_name = match action_name {
279
- Some(a) => a,
280
- None => {
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
- }
290
- return (StatusCode::NOT_FOUND, "Not Found").into_response();
291
- }
292
- };
293
-
294
- // Phase 3: V8 Execution (dispatch to worker pool)
295
-
296
- let headers_vec: SmallVec<[(String, String); 8]> = headers_map.into_iter().collect();
297
- let params_vec: SmallVec<[(String, String); 4]> = params.into_iter().collect();
298
- let query_vec: SmallVec<[(String, String); 4]> = query_map.into_iter().collect();
299
-
300
- let body_arg = if !body_bytes.is_empty() {
301
- Some(body_bytes)
302
- } else {
303
- None
304
- };
305
-
306
- let (result_json, timings) = state
307
- .runtime
308
- .execute(
309
- action_name.clone(),
310
- method.clone(),
311
- path.clone(),
312
- body_arg,
313
- headers_vec,
314
- params_vec,
315
- query_vec,
316
- )
317
- .await
318
- .unwrap_or_else(|e| (serde_json::json!({"error": e}), vec![]));
319
-
320
- // Phase 4: Response Construction
321
-
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
328
- if let Some(err) = result_json.get("error") {
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
- );
348
- }
349
- let response = (StatusCode::INTERNAL_SERVER_ERROR, Json(result_json)).into_response();
350
- return response;
351
- }
352
-
353
- // Response object construction
354
- let mut response = if let Some(is_resp) = result_json.get("_isResponse") {
355
- if is_resp.as_bool().unwrap_or(false) {
356
- let status_u16 = result_json
357
- .get("status")
358
- .and_then(|v| v.as_u64())
359
- .unwrap_or(200) as u16;
360
- let status = StatusCode::from_u16(status_u16).unwrap_or(StatusCode::OK);
361
- let mut builder = axum::http::Response::builder().status(status);
362
-
363
- if let Some(hmap) = result_json.get("headers").and_then(|v| v.as_object()) {
364
- for (k, v) in hmap {
365
- if let Some(vs) = v.as_str() {
366
- builder = builder.header(k, vs);
367
- }
368
- }
369
- }
370
-
371
- let mut is_redirect = false;
372
- if let Some(location) = result_json.get("redirect") {
373
- if let Some(url) = location.as_str() {
374
- let mut final_status_u16 = status.as_u16();
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);
381
- is_redirect = true;
382
- }
383
- }
384
-
385
- let body_text = if is_redirect {
386
- "".to_string()
387
- } else {
388
- match result_json.get("body") {
389
- Some(Value::String(s)) => s.clone(),
390
- Some(v) => v.to_string(),
391
- None => "".to_string(),
392
- }
393
- };
394
- builder.body(Body::from(body_text)).unwrap()
395
- } else {
396
- Json(result_json).into_response()
397
- }
398
- } else {
399
- Json(result_json).into_response()
400
- };
401
-
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_or_else(|_| axum::http::HeaderValue::from_static("")));
413
- }
414
-
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
- }
460
- }
461
-
462
- response
463
- }
464
-
465
- #[tokio::main]
466
- async fn main() -> Result<()> {
467
- dotenvy::dotenv().ok();
468
-
469
- // Configuration
470
- let production_mode = std::env::var("TITAN_DEV").unwrap_or_default() != "1";
471
-
472
- let raw = fs::read_to_string("./routes.json").unwrap_or_else(|_| "{}".to_string());
473
- let json: Value = serde_json::from_str(&raw).unwrap_or_default();
474
-
475
- let port = std::env::var("PORT")
476
- .ok()
477
- .and_then(|p| p.parse::<u64>().ok())
478
- .or_else(|| json["__config"]["port"].as_u64())
479
- .unwrap_or(3000);
480
-
481
- let thread_count = json["__config"]["threads"].as_u64();
482
- let routes_json = json["routes"].clone();
483
- let map: HashMap<String, RouteVal> = serde_json::from_value(routes_json).unwrap_or_default();
484
- let dynamic_routes: Vec<DynamicRoute> =
485
- serde_json::from_value(json["__dynamic_routes"].clone()).unwrap_or_default();
486
-
487
- let project_root = resolve_project_root();
488
-
489
- // Load extensions
490
- extensions::load_project_extensions(project_root.clone());
491
-
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)
520
- let threads = match thread_count {
521
- Some(t) if t > 0 => t as usize,
522
- _ => {
523
- let cpus = num_cpus::get();
524
- // Optimal for CPU-bound V8 work: 2x cores
525
- cpus * 2
526
- }
527
- };
528
-
529
- let stack_mb = json["__config"]["stack_mb"].as_u64().unwrap_or(8);
530
- let stack_size = (stack_mb as usize) * 1024 * 1024;
531
-
532
- let runtime_manager = Arc::new(RuntimeManager::new(
533
- project_root.clone(),
534
- threads,
535
- stack_size,
536
- ));
537
-
538
- // Build AppState
539
- let state = AppState {
540
- routes: Arc::new(map),
541
- dynamic_routes: Arc::new(dynamic_routes),
542
- runtime: runtime_manager,
543
- fast_paths: Arc::new(fast_paths),
544
- precomputed: Arc::new(precomputed),
545
- production_mode,
546
- };
547
-
548
- // Router
549
- let app = Router::new()
550
- .route("/", any(root_route))
551
- .fallback(any(dynamic_route))
552
- .with_state(state);
553
-
554
- let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await?;
555
-
556
- println!(
557
- "\x1b[38;5;39mTitan server running at:\x1b[0m http://localhost:{} \x1b[90m(Threads: {}, Stack: {}MB{})\x1b[0m",
558
- port,
559
- threads,
560
- stack_mb,
561
- if production_mode { "" } else { ", Dev Mode" }
562
- );
563
-
564
- axum::serve(listener, app).await?;
565
- Ok(())
566
- }
567
-
568
- fn resolve_project_root() -> PathBuf {
569
- if let Ok(cwd) = std::env::current_dir() {
570
- if cwd.join("node_modules").exists()
571
- || cwd.join("package.json").exists()
572
- || cwd.join(".ext").exists()
573
- {
574
- return cwd;
575
- }
576
- }
577
-
578
- if let Ok(exe) = std::env::current_exe() {
579
- let mut current = exe.parent();
580
- while let Some(dir) = current {
581
- if dir.join(".ext").exists() || dir.join("node_modules").exists() {
582
- return dir.to_path_buf();
583
- }
584
- current = dir.parent();
585
- }
586
- }
587
-
588
- std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
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
- }