@ezetgalaxy/titan 25.14.6 → 25.14.7
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/README.md +344 -339
- package/package.json +1 -1
- package/templates/server/src/main.rs +56 -25
package/README.md
CHANGED
|
@@ -1,339 +1,344 @@
|
|
|
1
|
-
|
|
2
|
-
```
|
|
3
|
-
████████╗██╗████████╗ █████╗ ███╗ ██╗
|
|
4
|
-
╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗ ██║
|
|
5
|
-
██║ ██║ ██║ ███████║██╔██╗ ██║
|
|
6
|
-
██║ ██║ ██║ ██╔══██║██║╚██╗██║
|
|
7
|
-
██║ ██║ ██║ ██║ ██║██║ ╚████║
|
|
8
|
-
╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Titan
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
───────────────────────────────────────────────────────────────
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
│
|
|
121
|
-
├──
|
|
122
|
-
│
|
|
123
|
-
│
|
|
124
|
-
|
|
125
|
-
│ ├──
|
|
126
|
-
│ ├──
|
|
127
|
-
│ ├──
|
|
128
|
-
│
|
|
129
|
-
│
|
|
130
|
-
├──
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
t
|
|
148
|
-
|
|
149
|
-
t.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
*
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
git
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
*
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
1
|
+
|
|
2
|
+
```
|
|
3
|
+
████████╗██╗████████╗ █████╗ ███╗ ██╗
|
|
4
|
+
╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗ ██║
|
|
5
|
+
██║ ██║ ██║ ███████║██╔██╗ ██║
|
|
6
|
+
██║ ██║ ██║ ██╔══██║██║╚██╗██║
|
|
7
|
+
██║ ██║ ██║ ██║ ██║██║ ╚████║
|
|
8
|
+
╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝
|
|
9
|
+
```
|
|
10
|
+
# Notice
|
|
11
|
+
**Production mode is under development 😞**
|
|
12
|
+
**Enjoy development mode `tit dev` 💙**
|
|
13
|
+
|
|
14
|
+
# TITAN PLANET 🚀
|
|
15
|
+
|
|
16
|
+
**JavaScript Simplicity. Rust Power. Zero Configuration.**
|
|
17
|
+
|
|
18
|
+
Titan Planet is a JavaScript-first backend framework that compiles your JavaScript routes and actions into a **native Rust + Axum server**.
|
|
19
|
+
|
|
20
|
+
You write **zero Rust**.
|
|
21
|
+
Titan ships a full backend engine, dev server, bundler, router, action runtime, and Docker deploy pipeline — all powered by Rust under the hood.
|
|
22
|
+
|
|
23
|
+
Titan = JavaScript productivity × Rust performance × Zero DevOps.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# 🌌 Why Titan?
|
|
28
|
+
|
|
29
|
+
| Feature | Titan | Express/Nest | FastAPI | Bun |
|
|
30
|
+
| ------------------------------------ | ----- | ------------ | ------- | --------- |
|
|
31
|
+
| Native binary output | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
32
|
+
| Rust-level performance | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
33
|
+
| Pure JavaScript developer experience | ✅ Yes | ✅ Yes | ❌ No | ❌ Partial |
|
|
34
|
+
| Zero-config Docker deploy | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
35
|
+
| Action-based architecture | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
36
|
+
| Hot reload dev server | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
37
|
+
|
|
38
|
+
Titan gives you:
|
|
39
|
+
|
|
40
|
+
* Native speed
|
|
41
|
+
* JS comfort
|
|
42
|
+
* Cloud-first deployment
|
|
43
|
+
* Full environment variable support
|
|
44
|
+
* Built-in HTTP client (`t.fetch`)
|
|
45
|
+
* Lightweight serverless-like actions
|
|
46
|
+
* Instant hot reload
|
|
47
|
+
* Zero configuration
|
|
48
|
+
* Single deployable binary
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# 🚀 Quick Start
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ⚙ Requirements
|
|
56
|
+
|
|
57
|
+
Install before using Titan:
|
|
58
|
+
|
|
59
|
+
### 1. Rust (latest stable)
|
|
60
|
+
|
|
61
|
+
[https://rust-lang.org/tools/install/](https://rust-lang.org/tools/install/)
|
|
62
|
+
|
|
63
|
+
### 2. Node.js (v18+)
|
|
64
|
+
|
|
65
|
+
Required for:
|
|
66
|
+
|
|
67
|
+
* Titan CLI
|
|
68
|
+
* esbuild
|
|
69
|
+
* JS → Rust compilation pipeline
|
|
70
|
+
|
|
71
|
+
Verify:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
node -v
|
|
75
|
+
npm -v
|
|
76
|
+
rustc -V
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### Install Titan CLI
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm install -g @ezetgalaxy/titan
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Create a new project
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
tit init my-app
|
|
91
|
+
cd my-app
|
|
92
|
+
tit dev
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Titan will:
|
|
96
|
+
|
|
97
|
+
* Build routes
|
|
98
|
+
* Bundle actions
|
|
99
|
+
* Start Rust dev server
|
|
100
|
+
* Watch file changes
|
|
101
|
+
* Trigger instant reload
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
# 📁 Project Layout
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
my-app/
|
|
109
|
+
├── app/ # You develop ONLY this folder
|
|
110
|
+
│ ├── app.js # Titan routes (DSL)
|
|
111
|
+
│ └── actions/ # Your custom JS actions
|
|
112
|
+
│ └── hello.js # Example Titan action
|
|
113
|
+
|
|
114
|
+
───────────────────────────────────────────────────────────────
|
|
115
|
+
Everything below is auto-generated by `tit init`
|
|
116
|
+
You never modify these folders manually
|
|
117
|
+
───────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
├── titan/ # Titan's internal JS engine
|
|
120
|
+
│ ├── titan.js # Titan DSL runtime
|
|
121
|
+
│ ├── bundle.js # JS → .jsbundle bundler
|
|
122
|
+
│ └── dev.js # Hot Reload system
|
|
123
|
+
│
|
|
124
|
+
├── server/ # Auto-generated Rust backend
|
|
125
|
+
│ ├── Cargo.toml # Rust project config
|
|
126
|
+
│ ├── src/ # Rust source code
|
|
127
|
+
│ ├── actions/ # Compiled .jsbundle actions
|
|
128
|
+
│ ├── titan/ # Internal Rust runtime files
|
|
129
|
+
│ ├── routes.json # Generated route metadata
|
|
130
|
+
│ ├── action_map.json # Maps actions to bundles
|
|
131
|
+
│ └── titan-server # Final production Rust binary
|
|
132
|
+
│
|
|
133
|
+
├── Dockerfile # Auto-generated production Dockerfile
|
|
134
|
+
├── .dockerignore # Auto-generated Docker ignore rules
|
|
135
|
+
├── package.json # JS project config (auto)
|
|
136
|
+
└── .gitignore # Auto-generated by `tit init`
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
# 🛣 Example Route
|
|
143
|
+
|
|
144
|
+
**app/app.js**
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import t from "../titan/titan.js";
|
|
148
|
+
|
|
149
|
+
t.post("/hello").action("hello");
|
|
150
|
+
t.get("/").reply("Welcome to Titan Planet");
|
|
151
|
+
|
|
152
|
+
t.start(3000, "Ready to land on Titan 🚀");
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
# 🧩 Example Action
|
|
158
|
+
|
|
159
|
+
**app/actions/hello.js**
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
export function hello(req) {
|
|
163
|
+
return { message: "Hello from Titan!" };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
globalThis.hello = hello;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
# ⚡ New: Built-In HTTP Fetch (`t.fetch`)
|
|
172
|
+
|
|
173
|
+
Titan now includes a built-in server-side `fetch` bridge powered by Rust.
|
|
174
|
+
|
|
175
|
+
Use it to call any external API:
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
function hello(req) {
|
|
179
|
+
const API_KEY = process.env.API_KEY || __titan_env.API_KEY;
|
|
180
|
+
|
|
181
|
+
const body = JSON.stringify({
|
|
182
|
+
model: "gpt-4.1-mini",
|
|
183
|
+
messages: [{ role: "user", content: "hii" }]
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const r = t.fetch("https://api.openai.com/v1/chat/completions", {
|
|
187
|
+
method: "POST",
|
|
188
|
+
headers: {
|
|
189
|
+
"Content-Type": "application/json",
|
|
190
|
+
"Authorization": `Bearer ${API_KEY}`
|
|
191
|
+
},
|
|
192
|
+
body
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const json = JSON.parse(r.body);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
ok: true,
|
|
199
|
+
message: json.choices[0].message.content
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
globalThis.hello = hello;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### `t.fetch` supports:
|
|
207
|
+
|
|
208
|
+
* GET, POST, PUT, DELETE
|
|
209
|
+
* Custom headers
|
|
210
|
+
* JSON bodies
|
|
211
|
+
* Authorization tokens
|
|
212
|
+
* External / internal APIs
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
# 🔥 Hot Reload Dev Server
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
tit dev
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Titan’s dev engine:
|
|
223
|
+
|
|
224
|
+
* Rebuilds routes
|
|
225
|
+
* Rebundil actions
|
|
226
|
+
* Restarts Rust server
|
|
227
|
+
* Updates instantly
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
# 🧱 Production Build
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
tit build
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Output includes:
|
|
239
|
+
|
|
240
|
+
* `titan-server` native binary
|
|
241
|
+
* JS bundles
|
|
242
|
+
* routing metadata
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
# 🐳 Docker Deployment (Zero Config)
|
|
247
|
+
|
|
248
|
+
Titan generates an optimized **multi-stage Dockerfile**:
|
|
249
|
+
|
|
250
|
+
Works on:
|
|
251
|
+
|
|
252
|
+
* Railway
|
|
253
|
+
* Fly.io
|
|
254
|
+
* Render
|
|
255
|
+
* VPS / Dedicated servers
|
|
256
|
+
* Docker Hub
|
|
257
|
+
* Kubernetes
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
# ☁ Uploading Titan to GitHub
|
|
262
|
+
|
|
263
|
+
Titan projects are designed for **direct repository upload**.
|
|
264
|
+
|
|
265
|
+
Include everything generated by `tit init`:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
app/
|
|
269
|
+
titan/
|
|
270
|
+
server/
|
|
271
|
+
Cargo.toml
|
|
272
|
+
Dockerfile
|
|
273
|
+
.gitignore
|
|
274
|
+
package.json
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Push to GitHub:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
git init
|
|
281
|
+
git add .
|
|
282
|
+
git commit -m "Initial Titan project"
|
|
283
|
+
git branch -M main
|
|
284
|
+
git remote add origin <your_repo_url>
|
|
285
|
+
git push -u origin main
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Your repo is now fully deployable with Docker.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
# ☁ Zero-Config Deployment with Docker
|
|
293
|
+
|
|
294
|
+
Once pushed to GitHub, you can deploy anywhere.
|
|
295
|
+
|
|
296
|
+
## Deploy to Railway
|
|
297
|
+
|
|
298
|
+
1. Go to Railway
|
|
299
|
+
2. Create New Project → Deploy from GitHub
|
|
300
|
+
3. Select your Titan repo
|
|
301
|
+
4. Railway auto-detects the Dockerfile
|
|
302
|
+
5. It builds + deploys automatically
|
|
303
|
+
|
|
304
|
+
Railway will:
|
|
305
|
+
|
|
306
|
+
* Build your Rust server
|
|
307
|
+
* Copy JS bundles
|
|
308
|
+
* Start the `titan-server` binary
|
|
309
|
+
* Expose the correct port
|
|
310
|
+
|
|
311
|
+
No configuration required.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
# ✨ Updating Titan
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
tit update
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Updates:
|
|
322
|
+
|
|
323
|
+
* Titan CLI
|
|
324
|
+
* DSL
|
|
325
|
+
* Bundler
|
|
326
|
+
* Dev server
|
|
327
|
+
* Rust runtime templates
|
|
328
|
+
* Dockerfile
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
# 📦 Version
|
|
333
|
+
|
|
334
|
+
**Titan v25 — Stable**
|
|
335
|
+
Optimized for production, cloud deployment, and AI workloads.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
# 🤝 Contributing
|
|
340
|
+
|
|
341
|
+
Pull requests welcome
|
|
342
|
+
https://github.com/ezet-galaxy/-ezetgalaxy-titan
|
|
343
|
+
|
|
344
|
+
---
|
package/package.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
// server/src/main.rs
|
|
2
|
-
use std::{collections::HashMap, env, fs, path::PathBuf, sync::Arc
|
|
2
|
+
use std::{collections::HashMap, env, fs, path::Path, path::PathBuf, sync::Arc};
|
|
3
3
|
|
|
4
4
|
use anyhow::Result;
|
|
5
5
|
use axum::{
|
|
6
|
-
|
|
6
|
+
Router,
|
|
7
|
+
body::{Body, to_bytes},
|
|
7
8
|
extract::State,
|
|
8
9
|
http::{Request, StatusCode},
|
|
9
10
|
response::{IntoResponse, Json},
|
|
10
11
|
routing::any,
|
|
11
|
-
Router,
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
use boa_engine::{
|
|
14
|
+
use boa_engine::{Context, JsValue, Source, object::ObjectInitializer};
|
|
15
15
|
use boa_engine::{js_string, native_function::NativeFunction, property::Attribute};
|
|
16
16
|
|
|
17
|
-
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
|
18
17
|
use reqwest::blocking::Client;
|
|
18
|
+
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
|
19
19
|
|
|
20
20
|
use serde::Deserialize;
|
|
21
21
|
use serde_json::Value;
|
|
@@ -152,9 +152,10 @@ fn inject_t_fetch(ctx: &mut Context) {
|
|
|
152
152
|
if !header_pairs.is_empty() {
|
|
153
153
|
let mut headers = HeaderMap::new();
|
|
154
154
|
for (k, v) in header_pairs.into_iter() {
|
|
155
|
-
if let (Ok(name), Ok(val)) =
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
if let (Ok(name), Ok(val)) = (
|
|
156
|
+
HeaderName::from_bytes(k.as_bytes()),
|
|
157
|
+
HeaderValue::from_str(&v),
|
|
158
|
+
) {
|
|
158
159
|
headers.insert(name, val);
|
|
159
160
|
}
|
|
160
161
|
}
|
|
@@ -230,7 +231,8 @@ async fn dynamic_handler_inner(
|
|
|
230
231
|
"action" => {
|
|
231
232
|
let action_name = route.value.as_str().unwrap_or("").trim();
|
|
232
233
|
if action_name.is_empty() {
|
|
233
|
-
return (StatusCode::INTERNAL_SERVER_ERROR, "Invalid action name")
|
|
234
|
+
return (StatusCode::INTERNAL_SERVER_ERROR, "Invalid action name")
|
|
235
|
+
.into_response();
|
|
234
236
|
}
|
|
235
237
|
|
|
236
238
|
// Resolve actions directory: prefer resolve_actions_dir(), fall back to heuristic find_actions_dir
|
|
@@ -272,30 +274,56 @@ async fn dynamic_handler_inner(
|
|
|
272
274
|
};
|
|
273
275
|
|
|
274
276
|
// Build env object
|
|
275
|
-
let mut env_map = serde_json::Map::new();
|
|
277
|
+
let mut env_map: serde_json::Map<String, Value> = serde_json::Map::new();
|
|
276
278
|
for (k, v) in std::env::vars() {
|
|
277
279
|
env_map.insert(k, Value::String(v));
|
|
278
280
|
}
|
|
279
|
-
let env_json = Value::Object(env_map);
|
|
281
|
+
let env_json: Value = Value::Object(env_map);
|
|
282
|
+
|
|
283
|
+
// Ensure body_str is valid JS: we will embed it as a JS expression.
|
|
284
|
+
// If body_str is a JSON object string, embedding it directly is fine.
|
|
285
|
+
// But to be safe, we create a quoted JS string and parse it in JS if necessary.
|
|
286
|
+
let safe_body_literal: String =
|
|
287
|
+
serde_json::to_string(&body_str).unwrap_or_else(|_| "null".to_string());
|
|
280
288
|
|
|
281
|
-
// Injected script: sets process.env
|
|
282
|
-
let injected = format!(
|
|
289
|
+
// Injected script: sets process.env, __titan_env, __titan_req and invokes action function.
|
|
290
|
+
let injected: String = format!(
|
|
283
291
|
r#"
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
// Runtime env injected by Titan
|
|
293
|
+
globalThis.process = {{ env: {} }};
|
|
294
|
+
globalThis.__titan_env = {};
|
|
295
|
+
// quick debug so prod logs show whether API key is present
|
|
296
|
+
try {{
|
|
297
|
+
console.log('TITAN: runtime API_KEY =', (process && process.env && process.env.API_KEY) || (typeof __titan_env !== 'undefined' && __titan_env.API_KEY) || null);
|
|
298
|
+
}} catch(e) {{ /* ignore */ }}
|
|
299
|
+
|
|
300
|
+
// Parse the incoming request body. If it's a JSON string we parse it into an object.
|
|
301
|
+
const __titan_req_body_literal = {};
|
|
302
|
+
let __titan_req;
|
|
303
|
+
try {{
|
|
304
|
+
// it's a quoted JSON string in Rust, so first unquote via JSON.parse
|
|
305
|
+
__titan_req = JSON.parse(__titan_req_body_literal);
|
|
306
|
+
}} catch (e) {{
|
|
307
|
+
// if parse fails, fall back to empty object
|
|
308
|
+
__titan_req = {{}};
|
|
309
|
+
}}
|
|
310
|
+
|
|
311
|
+
// Action code (bundled JS)
|
|
312
|
+
{};
|
|
313
|
+
// call exported action function
|
|
314
|
+
{}(__titan_req);
|
|
315
|
+
"#,
|
|
316
|
+
env_json.to_string(), // inserted JSON object for process.env
|
|
317
|
+
env_json.to_string(), // inserted also as __titan_env
|
|
318
|
+
safe_body_literal, // quoted JSON string literal of request body
|
|
319
|
+
js_code, // code from the .jsbundle
|
|
320
|
+
action_name // call function
|
|
293
321
|
);
|
|
294
322
|
|
|
295
|
-
let mut ctx = Context::default();
|
|
323
|
+
let mut ctx: Context = Context::default();
|
|
296
324
|
inject_t_fetch(&mut ctx);
|
|
297
325
|
|
|
298
|
-
let result = match ctx.eval(Source::from_bytes(&injected)) {
|
|
326
|
+
let result: JsValue = match ctx.eval(Source::from_bytes(&injected)) {
|
|
299
327
|
Ok(v) => v,
|
|
300
328
|
Err(e) => return Json(json_error(e.to_string())).into_response(),
|
|
301
329
|
};
|
|
@@ -361,7 +389,10 @@ async fn main() -> Result<()> {
|
|
|
361
389
|
println!(" ██║ ██║ ██║ ██╔══██║██║╚██╗██║");
|
|
362
390
|
println!(" ██║ ██║ ██║ ██║ ██║██║ ╚████║");
|
|
363
391
|
println!(" ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝\x1b[0m\n");
|
|
364
|
-
println!(
|
|
392
|
+
println!(
|
|
393
|
+
"\x1b[38;5;39mTitan server running at:\x1b[0m http://localhost:{}",
|
|
394
|
+
port
|
|
395
|
+
);
|
|
365
396
|
|
|
366
397
|
axum::serve(listener, app).await?;
|
|
367
398
|
Ok(())
|