@ezetgalaxy/titan 26.7.5 → 26.8.2

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 (53) hide show
  1. package/README.md +86 -200
  2. package/index.js +87 -23
  3. package/package.json +4 -3
  4. package/templates/js/_gitignore +37 -0
  5. package/templates/{package.json → js/package.json} +3 -0
  6. package/templates/{server → js/server}/actions/hello.jsbundle +4 -1
  7. package/templates/{server → js/server}/src/extensions.rs +149 -17
  8. package/templates/{server → js/server}/src/main.rs +1 -6
  9. package/templates/{titan → js/titan}/bundle.js +22 -9
  10. package/templates/js/titan/dev.js +258 -0
  11. package/templates/js/titan/titan.js +122 -0
  12. package/templates/rust/Dockerfile +66 -0
  13. package/templates/rust/_dockerignore +3 -0
  14. package/templates/rust/_gitignore +37 -0
  15. package/templates/rust/app/actions/hello.js +5 -0
  16. package/templates/rust/app/actions/rust_hello.rs +14 -0
  17. package/templates/rust/app/app.js +11 -0
  18. package/templates/rust/app/titan.d.ts +101 -0
  19. package/templates/rust/jsconfig.json +19 -0
  20. package/templates/rust/package.json +13 -0
  21. package/templates/rust/server/Cargo.lock +2869 -0
  22. package/templates/rust/server/Cargo.toml +39 -0
  23. package/templates/rust/server/action_map.json +3 -0
  24. package/templates/rust/server/actions/hello.jsbundle +47 -0
  25. package/templates/rust/server/routes.json +22 -0
  26. package/templates/rust/server/src/action_management.rs +131 -0
  27. package/templates/rust/server/src/actions_rust/mod.rs +19 -0
  28. package/templates/rust/server/src/actions_rust/rust_hello.rs +14 -0
  29. package/templates/rust/server/src/errors.rs +10 -0
  30. package/templates/rust/server/src/extensions.rs +989 -0
  31. package/templates/rust/server/src/main.rs +437 -0
  32. package/templates/rust/server/src/utils.rs +33 -0
  33. package/templates/rust/titan/bundle.js +157 -0
  34. package/templates/rust/titan/dev.js +266 -0
  35. package/templates/{titan → rust/titan}/titan.js +122 -98
  36. package/titanpl-sdk/templates/Dockerfile +4 -17
  37. package/titanpl-sdk/templates/server/src/extensions.rs +218 -423
  38. package/titanpl-sdk/templates/server/src/main.rs +68 -134
  39. package/templates/_gitignore +0 -25
  40. package/templates/titan/dev.js +0 -144
  41. /package/templates/{Dockerfile → js/Dockerfile} +0 -0
  42. /package/templates/{.dockerignore → js/_dockerignore} +0 -0
  43. /package/templates/{app → js/app}/actions/hello.js +0 -0
  44. /package/templates/{app → js/app}/app.js +0 -0
  45. /package/templates/{app → js/app}/titan.d.ts +0 -0
  46. /package/templates/{jsconfig.json → js/jsconfig.json} +0 -0
  47. /package/templates/{server → js/server}/Cargo.lock +0 -0
  48. /package/templates/{server → js/server}/Cargo.toml +0 -0
  49. /package/templates/{server → js/server}/action_map.json +0 -0
  50. /package/templates/{server → js/server}/routes.json +0 -0
  51. /package/templates/{server → js/server}/src/action_management.rs +0 -0
  52. /package/templates/{server → js/server}/src/errors.rs +0 -0
  53. /package/templates/{server → js/server}/src/utils.rs +0 -0
@@ -1,22 +1,22 @@
1
1
  #![allow(unused)]
2
- use bcrypt::{DEFAULT_COST, hash, verify};
3
- use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation, decode, encode};
2
+ use v8;
4
3
  use reqwest::{
5
4
  blocking::Client,
6
5
  header::{HeaderMap, HeaderName, HeaderValue},
7
6
  };
8
- use serde_json::Value;
9
- use std::path::PathBuf;
10
7
  use std::sync::Once;
8
+ use std::path::PathBuf;
11
9
  use std::time::{SystemTime, UNIX_EPOCH};
12
- use v8;
10
+ use serde_json::Value;
11
+ use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
12
+ use bcrypt::{hash, verify, DEFAULT_COST};
13
13
 
14
14
  use crate::utils::{blue, gray, green, parse_expires_in};
15
- use libloading::Library;
15
+ use libloading::{Library};
16
+ use walkdir::WalkDir;
17
+ use std::sync::Mutex;
16
18
  use std::collections::HashMap;
17
19
  use std::fs;
18
- use std::sync::Mutex;
19
- use walkdir::WalkDir;
20
20
 
21
21
  // ----------------------------------------------------------------------------
22
22
  // GLOBAL REGISTRY
@@ -25,7 +25,7 @@ use walkdir::WalkDir;
25
25
  static REGISTRY: Mutex<Option<Registry>> = Mutex::new(None);
26
26
  #[allow(dead_code)]
27
27
  struct Registry {
28
- _libs: Vec<Library>,
28
+ _libs: Vec<Library>,
29
29
  modules: Vec<ModuleDef>,
30
30
  natives: Vec<NativeFnEntry>, // Flattened list of all native functions
31
31
  }
@@ -73,189 +73,81 @@ pub fn load_project_extensions(root: PathBuf) {
73
73
  let mut libs = Vec::new();
74
74
  let mut all_natives = Vec::new();
75
75
 
76
- // =====================================================
77
- // 1. Resolve all extension search directories
78
- // =====================================================
79
-
80
- let mut search_dirs = Vec::new();
81
-
82
- let ext_dir = root.join(".ext"); // Production
83
- let nm_root = root.join("node_modules"); // Dev
84
- let nm_parent = root.parent().map(|p| p.join("node_modules")); // Monorepo
85
-
86
- // 1) Production
87
- if ext_dir.exists() {
88
- search_dirs.push(ext_dir);
89
- }
90
-
91
- // 2) Dev: project node_modules
92
- if nm_root.exists() {
93
- search_dirs.push(nm_root.clone());
94
- }
95
-
96
- // 3) Dev monorepo: parent/node_modules
97
- if let Some(nmp) = &nm_parent {
98
- if nmp.exists() {
99
- search_dirs.push(nmp.clone());
76
+ let mut node_modules = root.join("node_modules");
77
+ if !node_modules.exists() {
78
+ if let Some(parent) = root.parent() {
79
+ let parent_modules = parent.join("node_modules");
80
+ if parent_modules.exists() {
81
+ node_modules = parent_modules;
82
+ }
100
83
  }
101
84
  }
102
-
103
- // 4) Never return empty — add root/node_modules even if missing
104
- if search_dirs.is_empty() {
105
- search_dirs.push(nm_root);
106
- }
107
-
108
- // Normalize and dedupe
109
- search_dirs.sort();
110
- search_dirs.dedup();
111
-
112
- println!("{} Scanning extension directories:", blue("[Titan]"));
113
- for d in &search_dirs {
114
- println!(" • {}", d.display());
115
- }
116
-
117
- // =====================================================
118
- // 2. Walk and locate titan.json inside search paths
119
- // =====================================================
120
- for dir in &search_dirs {
121
- if !dir.exists() {
122
- println!(" {} Skipping non-existent directory: {}", crate::utils::yellow("⚠"), dir.display());
123
- continue;
124
- }
125
-
126
- for entry in WalkDir::new(&dir)
127
- .min_depth(1)
128
- .max_depth(5) // Increased depth
129
- .follow_links(true)
130
- {
131
- let entry = match entry {
132
- Ok(e) => e,
133
- Err(_) => continue,
134
- };
135
-
136
- // Only accept titan.json files
85
+
86
+ if node_modules.exists() {
87
+ for entry in WalkDir::new(&node_modules).follow_links(true).min_depth(1).max_depth(4) {
88
+ let entry = match entry { Ok(e) => e, Err(_) => continue };
137
89
  if entry.file_type().is_file() && entry.file_name() == "titan.json" {
138
- let path = entry.path();
139
- // Load config file
140
- let config_content = match fs::read_to_string(path) {
141
- Ok(c) => c,
142
- Err(e) => {
143
- println!("{} Failed to read {}: {}", crate::utils::red("[Titan]"), path.display(), e);
144
- continue;
145
- }
146
- };
147
-
148
- let config: TitanConfig = match serde_json::from_str(&config_content) {
149
- Ok(c) => c,
150
- Err(e) => {
151
- println!("{} Failed to parse {}: {}", crate::utils::red("[Titan]"), path.display(), e);
152
- continue;
153
- }
154
- };
155
-
156
- let pkg_dir = path.parent().unwrap();
157
- let mut mod_natives_map = HashMap::new();
158
-
159
- // =====================================================
160
- // 3. Load native extension (optional)
161
- // =====================================================
162
- if let Some(native_conf) = config.native {
163
- let lib_path = pkg_dir.join(&native_conf.path);
164
-
165
- unsafe {
166
- match Library::new(&lib_path) {
167
- Ok(lib) => {
168
- for (fn_name, fn_conf) in native_conf.functions {
169
- let sig = if fn_conf.parameters == ["f64", "f64"]
170
- && fn_conf.result == "f64"
171
- {
172
- Signature::F64TwoArgsRetF64
173
- } else {
174
- Signature::Unknown
175
- };
176
-
177
- if let Ok(symbol) = lib.get::<*const ()>(fn_conf.symbol.as_bytes())
178
- {
179
- let idx = all_natives.len();
180
- all_natives.push(NativeFnEntry {
181
- ptr: *symbol as usize,
182
- sig,
183
- });
184
- mod_natives_map.insert(fn_name, idx);
185
- }
186
- }
187
- libs.push(lib);
188
- }
189
- Err(e) => println!(
190
- "{} Failed to load native library {} ({})",
191
- blue("[Titan]"),
192
- lib_path.display(),
193
- e
194
- ),
195
- }
196
- }
197
- }
198
-
199
- // =====================================================
200
- // 4. Load JS module file
201
- // =====================================================
202
- let js_path = pkg_dir.join(&config.main);
203
- let js_content = match fs::read_to_string(&js_path) {
90
+ let dir = entry.path().parent().unwrap();
91
+ let config_content = match fs::read_to_string(entry.path()) {
204
92
  Ok(c) => c,
205
- Err(e) => {
206
- println!("{} Failed to read JS main {} for extension {}: {}",
207
- crate::utils::red("[Titan]"),
208
- js_path.display(),
209
- config.name,
210
- e
211
- );
212
- continue;
213
- }
93
+ Err(_) => continue,
214
94
  };
215
-
216
- modules.push(ModuleDef {
217
- name: config.name.clone(),
218
- js: js_content,
219
- native_indices: mod_natives_map,
220
- });
221
-
222
- println!(
223
- "{} {} {}",
224
- blue("[Titan]"),
225
- green("Extension loaded:"),
226
- config.name
227
- );
95
+ let config: TitanConfig = match serde_json::from_str(&config_content) {
96
+ Ok(c) => c,
97
+ Err(_) => continue,
98
+ };
99
+
100
+ let mut mod_natives_map = HashMap::new();
101
+
102
+ if let Some(native_conf) = config.native {
103
+ let lib_path = dir.join(&native_conf.path);
104
+ unsafe {
105
+ match Library::new(&lib_path) {
106
+ Ok(lib) => {
107
+ for (fn_name, fn_conf) in native_conf.functions {
108
+ let sig = if fn_conf.parameters.len() == 2
109
+ && fn_conf.parameters[0] == "f64"
110
+ && fn_conf.parameters[1] == "f64"
111
+ && fn_conf.result == "f64" {
112
+ Signature::F64TwoArgsRetF64
113
+ } else {
114
+ Signature::Unknown
115
+ };
116
+
117
+ if let Ok(symbol) = lib.get::<*const ()>(fn_conf.symbol.as_bytes()) {
118
+ let idx = all_natives.len();
119
+ all_natives.push(NativeFnEntry {
120
+ ptr: *symbol as usize,
121
+ sig
122
+ });
123
+ mod_natives_map.insert(fn_name, idx);
124
+ }
125
+ }
126
+ libs.push(lib);
127
+ },
128
+ Err(e) => println!("Failed to load extension library {}: {}", lib_path.display(), e),
129
+ }
130
+ }
131
+ }
132
+
133
+ let js_path = dir.join(&config.main);
134
+ let js_content = fs::read_to_string(js_path).unwrap_or_default();
135
+
136
+ modules.push(ModuleDef {
137
+ name: config.name.clone(),
138
+ js: js_content,
139
+ native_indices: mod_natives_map,
140
+ });
141
+
142
+ println!("{} {} {}", blue("[Titan]"), green("Extension loaded:"), config.name);
228
143
  }
229
144
  }
230
145
  }
231
146
 
232
- // =====================================================
233
- // 5. Store registry globally
234
- // =====================================================
235
- if modules.is_empty() {
236
- println!("{} {}", blue("[Titan]"), crate::utils::yellow("No extensions loaded."));
237
- // Debug: list files in search dirs to assist debugging
238
- for dir in &search_dirs {
239
- if dir.exists() {
240
- println!("{} Listing contents of {}:", blue("[Titan]"), dir.display());
241
- for entry in WalkDir::new(dir).max_depth(5) {
242
- if let Ok(e) = entry {
243
- println!(" - {}", e.path().display());
244
- }
245
- }
246
- } else {
247
- println!("{} Directory not found: {}", blue("[Titan]"), dir.display());
248
- }
249
- }
250
- }
251
-
252
- *REGISTRY.lock().unwrap() = Some(Registry {
253
- _libs: libs,
254
- modules,
255
- natives: all_natives,
256
- });
147
+ *REGISTRY.lock().unwrap() = Some(Registry { _libs: libs, modules, natives: all_natives });
257
148
  }
258
149
 
150
+
259
151
  static V8_INIT: Once = Once::new();
260
152
 
261
153
  pub fn init_v8() {
@@ -284,11 +176,7 @@ fn throw(scope: &mut v8::HandleScope, msg: &str) {
284
176
  // NATIVE CALLBACKS
285
177
  // ----------------------------------------------------------------------------
286
178
 
287
- fn native_read(
288
- scope: &mut v8::HandleScope,
289
- args: v8::FunctionCallbackArguments,
290
- mut retval: v8::ReturnValue,
291
- ) {
179
+ fn native_read(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
292
180
  let path_val = args.get(0);
293
181
  // 1. Read argument
294
182
  if !path_val.is_string() {
@@ -307,7 +195,7 @@ fn native_read(
307
195
  let global = context.global(scope);
308
196
  let root_key = v8_str(scope, "__titan_root");
309
197
  let root_val = global.get(scope, root_key.into()).unwrap();
310
-
198
+
311
199
  let root_str = if root_val.is_string() {
312
200
  v8_to_string(scope, root_val)
313
201
  } else {
@@ -338,18 +226,14 @@ fn native_read(
338
226
  match std::fs::read_to_string(&target) {
339
227
  Ok(content) => {
340
228
  retval.set(v8_str(scope, &content).into());
341
- }
229
+ },
342
230
  Err(e) => {
343
231
  throw(scope, &format!("t.read failed: {}", e));
344
232
  }
345
233
  }
346
234
  }
347
235
 
348
- fn native_log(
349
- scope: &mut v8::HandleScope,
350
- args: v8::FunctionCallbackArguments,
351
- mut _retval: v8::ReturnValue,
352
- ) {
236
+ fn native_log(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut _retval: v8::ReturnValue) {
353
237
  let context = scope.get_current_context();
354
238
  let global = context.global(scope);
355
239
  let action_key = v8_str(scope, "__titan_action");
@@ -360,38 +244,30 @@ fn native_log(
360
244
  for i in 0..args.length() {
361
245
  let val = args.get(i);
362
246
  let mut appended = false;
363
-
247
+
364
248
  // Try to JSON stringify objects so they are readable in logs
365
249
  if val.is_object() && !val.is_function() {
366
- if let Some(json) = v8::json::stringify(scope, val) {
367
- parts.push(json.to_rust_string_lossy(scope));
368
- appended = true;
369
- }
250
+ if let Some(json) = v8::json::stringify(scope, val) {
251
+ parts.push(json.to_rust_string_lossy(scope));
252
+ appended = true;
253
+ }
370
254
  }
371
-
255
+
372
256
  if !appended {
373
257
  parts.push(v8_to_string(scope, val));
374
258
  }
375
259
  }
376
-
260
+
377
261
  println!(
378
262
  "{} {}",
379
263
  blue("[Titan]"),
380
- gray(&format!(
381
- "\x1b[90mlog({})\x1b[0m\x1b[97m: {}\x1b[0m",
382
- action_name,
383
- parts.join(" ")
384
- ))
264
+ gray(&format!("\x1b[90mlog({})\x1b[0m\x1b[97m: {}\x1b[0m", action_name, parts.join(" ")))
385
265
  );
386
266
  }
387
267
 
388
- fn native_fetch(
389
- scope: &mut v8::HandleScope,
390
- args: v8::FunctionCallbackArguments,
391
- mut retval: v8::ReturnValue,
392
- ) {
268
+ fn native_fetch(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
393
269
  let url = v8_to_string(scope, args.get(0));
394
-
270
+
395
271
  // Check for options (method, headers, body)
396
272
  let mut method = "GET".to_string();
397
273
  let mut body_str = None;
@@ -400,7 +276,7 @@ fn native_fetch(
400
276
  let opts_val = args.get(1);
401
277
  if opts_val.is_object() {
402
278
  let opts_obj = opts_val.to_object(scope).unwrap();
403
-
279
+
404
280
  // method
405
281
  let m_key = v8_str(scope, "method");
406
282
  if let Some(m_val) = opts_obj.get(scope, m_key.into()) {
@@ -408,18 +284,18 @@ fn native_fetch(
408
284
  method = v8_to_string(scope, m_val);
409
285
  }
410
286
  }
411
-
287
+
412
288
  // body
413
289
  let b_key = v8_str(scope, "body");
414
290
  if let Some(b_val) = opts_obj.get(scope, b_key.into()) {
415
291
  if b_val.is_string() {
416
292
  body_str = Some(v8_to_string(scope, b_val));
417
293
  } else if b_val.is_object() {
418
- let json_obj = v8::json::stringify(scope, b_val).unwrap();
419
- body_str = Some(json_obj.to_rust_string_lossy(scope));
294
+ let json_obj = v8::json::stringify(scope, b_val).unwrap();
295
+ body_str = Some(json_obj.to_rust_string_lossy(scope));
420
296
  }
421
297
  }
422
-
298
+
423
299
  // headers
424
300
  let h_key = v8_str(scope, "headers");
425
301
  if let Some(h_val) = opts_obj.get(scope, h_key.into()) {
@@ -429,61 +305,57 @@ fn native_fetch(
429
305
  for i in 0..keys.length() {
430
306
  let key = keys.get_index(scope, i).unwrap();
431
307
  let val = h_obj.get(scope, key).unwrap();
432
- headers_vec.push((v8_to_string(scope, key), v8_to_string(scope, val)));
308
+ headers_vec.push((
309
+ v8_to_string(scope, key),
310
+ v8_to_string(scope, val),
311
+ ));
433
312
  }
434
313
  }
435
314
  }
436
315
  }
437
316
  }
438
317
 
439
- let client = Client::builder()
440
- .use_rustls_tls()
441
- .tcp_nodelay(true)
442
- .build()
443
- .unwrap_or(Client::new());
444
-
318
+ let client = Client::builder().use_rustls_tls().tcp_nodelay(true).build().unwrap_or(Client::new());
319
+
445
320
  let mut req = client.request(method.parse().unwrap_or(reqwest::Method::GET), &url);
446
-
321
+
447
322
  for (k, v) in headers_vec {
448
- if let (Ok(name), Ok(val)) = (
449
- HeaderName::from_bytes(k.as_bytes()),
450
- HeaderValue::from_str(&v),
451
- ) {
323
+ if let (Ok(name), Ok(val)) = (HeaderName::from_bytes(k.as_bytes()), HeaderValue::from_str(&v)) {
452
324
  let mut map = HeaderMap::new();
453
325
  map.insert(name, val);
454
326
  req = req.headers(map);
455
327
  }
456
328
  }
457
-
329
+
458
330
  if let Some(b) = body_str {
459
331
  req = req.body(b);
460
332
  }
461
-
333
+
462
334
  let res = req.send();
463
-
335
+
464
336
  let obj = v8::Object::new(scope);
465
337
  match res {
466
338
  Ok(r) => {
467
339
  let status = r.status().as_u16();
468
340
  let text = r.text().unwrap_or_default();
469
-
341
+
470
342
  let status_key = v8_str(scope, "status");
471
343
  let status_val = v8::Number::new(scope, status as f64);
472
344
  obj.set(scope, status_key.into(), status_val.into());
473
-
345
+
474
346
  let body_key = v8_str(scope, "body");
475
347
  let body_val = v8_str(scope, &text);
476
348
  obj.set(scope, body_key.into(), body_val.into());
477
-
349
+
478
350
  let ok_key = v8_str(scope, "ok");
479
351
  let ok_val = v8::Boolean::new(scope, true);
480
352
  obj.set(scope, ok_key.into(), ok_val.into());
481
- }
353
+ },
482
354
  Err(e) => {
483
355
  let ok_key = v8_str(scope, "ok");
484
356
  let ok_val = v8::Boolean::new(scope, false);
485
357
  obj.set(scope, ok_key.into(), ok_val.into());
486
-
358
+
487
359
  let err_key = v8_str(scope, "error");
488
360
  let err_val = v8_str(scope, &e.to_string());
489
361
  obj.set(scope, err_key.into(), err_val.into());
@@ -492,46 +364,33 @@ fn native_fetch(
492
364
  retval.set(obj.into());
493
365
  }
494
366
 
495
- fn native_jwt_sign(
496
- scope: &mut v8::HandleScope,
497
- args: v8::FunctionCallbackArguments,
498
- mut retval: v8::ReturnValue,
499
- ) {
367
+ fn native_jwt_sign(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
500
368
  // payload, secret, options
501
369
  let payload_val = args.get(0);
502
370
  // Parse payload to serde_json::Map
503
- let json_str = v8::json::stringify(scope, payload_val)
504
- .unwrap()
505
- .to_rust_string_lossy(scope);
506
- let mut payload: serde_json::Map<String, Value> =
507
- serde_json::from_str(&json_str).unwrap_or_default();
371
+ let json_str = v8::json::stringify(scope, payload_val).unwrap().to_rust_string_lossy(scope);
372
+ let mut payload: serde_json::Map<String, Value> = serde_json::from_str(&json_str).unwrap_or_default();
508
373
 
509
374
  let secret = v8_to_string(scope, args.get(1));
510
-
375
+
511
376
  let opts_val = args.get(2);
512
377
  if opts_val.is_object() {
513
378
  let opts_obj = opts_val.to_object(scope).unwrap();
514
379
  let exp_key = v8_str(scope, "expiresIn");
515
-
380
+
516
381
  if let Some(val) = opts_obj.get(scope, exp_key.into()) {
517
- let seconds = if val.is_number() {
518
- Some(val.to_number(scope).unwrap().value() as u64)
519
- } else if val.is_string() {
520
- parse_expires_in(&v8_to_string(scope, val))
521
- } else {
522
- None
523
- };
524
-
525
- if let Some(sec) = seconds {
526
- let now = SystemTime::now()
527
- .duration_since(UNIX_EPOCH)
528
- .unwrap()
529
- .as_secs();
530
- payload.insert(
531
- "exp".to_string(),
532
- Value::Number(serde_json::Number::from(now + sec)),
533
- );
534
- }
382
+ let seconds = if val.is_number() {
383
+ Some(val.to_number(scope).unwrap().value() as u64)
384
+ } else if val.is_string() {
385
+ parse_expires_in(&v8_to_string(scope, val))
386
+ } else {
387
+ None
388
+ };
389
+
390
+ if let Some(sec) = seconds {
391
+ let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
392
+ payload.insert("exp".to_string(), Value::Number(serde_json::Number::from(now + sec)));
393
+ }
535
394
  }
536
395
  }
537
396
 
@@ -547,41 +406,33 @@ fn native_jwt_sign(
547
406
  }
548
407
  }
549
408
 
550
- fn native_jwt_verify(
551
- scope: &mut v8::HandleScope,
552
- args: v8::FunctionCallbackArguments,
553
- mut retval: v8::ReturnValue,
554
- ) {
409
+ fn native_jwt_verify(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
555
410
  let token = v8_to_string(scope, args.get(0));
556
411
  let secret = v8_to_string(scope, args.get(1));
557
-
412
+
558
413
  let mut validation = Validation::default();
559
414
  validation.validate_exp = true;
560
-
415
+
561
416
  let data = decode::<Value>(
562
417
  &token,
563
418
  &DecodingKey::from_secret(secret.as_bytes()),
564
419
  &validation,
565
420
  );
566
-
421
+
567
422
  match data {
568
423
  Ok(d) => {
569
- // Convert claim back to V8 object via JSON
570
- let json_str = serde_json::to_string(&d.claims).unwrap();
571
- let v8_json_str = v8_str(scope, &json_str);
572
- if let Some(val) = v8::json::parse(scope, v8_json_str) {
573
- retval.set(val);
574
- }
575
- }
424
+ // Convert claim back to V8 object via JSON
425
+ let json_str = serde_json::to_string(&d.claims).unwrap();
426
+ let v8_json_str = v8_str(scope, &json_str);
427
+ if let Some(val) = v8::json::parse(scope, v8_json_str) {
428
+ retval.set(val);
429
+ }
430
+ },
576
431
  Err(e) => throw(scope, &format!("Invalid or expired JWT: {}", e)),
577
432
  }
578
433
  }
579
434
 
580
- fn native_password_hash(
581
- scope: &mut v8::HandleScope,
582
- args: v8::FunctionCallbackArguments,
583
- mut retval: v8::ReturnValue,
584
- ) {
435
+ fn native_password_hash(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
585
436
  let pw = v8_to_string(scope, args.get(0));
586
437
  match hash(pw, DEFAULT_COST) {
587
438
  Ok(h) => retval.set(v8_str(scope, &h).into()),
@@ -589,23 +440,15 @@ fn native_password_hash(
589
440
  }
590
441
  }
591
442
 
592
- fn native_password_verify(
593
- scope: &mut v8::HandleScope,
594
- args: v8::FunctionCallbackArguments,
595
- mut retval: v8::ReturnValue,
596
- ) {
443
+ fn native_password_verify(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
597
444
  let pw = v8_to_string(scope, args.get(0));
598
445
  let hash_str = v8_to_string(scope, args.get(1));
599
-
446
+
600
447
  let ok = verify(pw, &hash_str).unwrap_or(false);
601
448
  retval.set(v8::Boolean::new(scope, ok).into());
602
449
  }
603
450
 
604
- fn native_define_action(
605
- _scope: &mut v8::HandleScope,
606
- args: v8::FunctionCallbackArguments,
607
- mut retval: v8::ReturnValue,
608
- ) {
451
+ fn native_define_action(_scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
609
452
  retval.set(args.get(0));
610
453
  }
611
454
 
@@ -615,17 +458,13 @@ fn native_define_action(
615
458
 
616
459
  // generic wrappers could go here if needed
617
460
 
618
- fn native_invoke_extension(
619
- scope: &mut v8::HandleScope,
620
- args: v8::FunctionCallbackArguments,
621
- mut retval: v8::ReturnValue,
622
- ) {
461
+ fn native_invoke_extension(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
623
462
  let fn_idx = args.get(0).to_integer(scope).unwrap().value() as usize;
624
463
 
625
464
  // Get pointer from registry
626
465
  let mut ptr = 0;
627
466
  let mut sig = Signature::Unknown;
628
-
467
+
629
468
  if let Ok(guard) = REGISTRY.lock() {
630
469
  if let Some(registry) = &*guard {
631
470
  if let Some(entry) = registry.natives.get(fn_idx) {
@@ -634,39 +473,33 @@ fn native_invoke_extension(
634
473
  }
635
474
  }
636
475
  }
637
-
476
+
638
477
  if ptr == 0 {
639
- throw(scope, "Native function not found");
640
- return;
478
+ throw(scope, "Native function not found");
479
+ return;
641
480
  }
642
481
 
643
482
  match sig {
644
483
  Signature::F64TwoArgsRetF64 => {
645
- let a = args
646
- .get(1)
647
- .to_number(scope)
648
- .unwrap_or(v8::Number::new(scope, 0.0))
649
- .value();
650
- let b = args
651
- .get(2)
652
- .to_number(scope)
653
- .unwrap_or(v8::Number::new(scope, 0.0))
654
- .value();
655
-
656
- unsafe {
657
- let func: extern "C" fn(f64, f64) -> f64 = std::mem::transmute(ptr);
658
- let res = func(a, b);
659
- retval.set(v8::Number::new(scope, res).into());
660
- }
661
- }
484
+ let a = args.get(1).to_number(scope).unwrap_or(v8::Number::new(scope, 0.0)).value();
485
+ let b = args.get(2).to_number(scope).unwrap_or(v8::Number::new(scope, 0.0)).value();
486
+
487
+ unsafe {
488
+ let func: extern "C" fn(f64, f64) -> f64 = std::mem::transmute(ptr);
489
+ let res = func(a, b);
490
+ retval.set(v8::Number::new(scope, res).into());
491
+ }
492
+ },
662
493
  _ => throw(scope, "Unsupported signature"),
663
494
  }
664
495
  }
665
496
 
497
+
666
498
  // ----------------------------------------------------------------------------
667
499
  // INJECTOR
668
500
  // ----------------------------------------------------------------------------
669
501
 
502
+
670
503
  pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Object>) {
671
504
  // Ensure globalThis reference
672
505
  let gt_key = v8_str(scope, "globalThis");
@@ -675,15 +508,13 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
675
508
  let t_obj = v8::Object::new(scope);
676
509
  let t_key = v8_str(scope, "t");
677
510
  // Use create_data_property to guarantee definition
678
- global
679
- .create_data_property(scope, t_key.into(), t_obj.into())
680
- .unwrap();
511
+ global.create_data_property(scope, t_key.into(), t_obj.into()).unwrap();
681
512
 
682
513
  // defineAction (identity function for clean typing)
683
514
  let def_fn = v8::Function::new(scope, native_define_action).unwrap();
684
515
  let def_key = v8_str(scope, "defineAction");
685
516
  global.set(scope, def_key.into(), def_fn.into());
686
-
517
+
687
518
  // t.read
688
519
  let read_fn = v8::Function::new(scope, native_read).unwrap();
689
520
  let read_key = v8_str(scope, "read");
@@ -693,7 +524,7 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
693
524
  let log_fn = v8::Function::new(scope, native_log).unwrap();
694
525
  let log_key = v8_str(scope, "log");
695
526
  t_obj.set(scope, log_key.into(), log_fn.into());
696
-
527
+
697
528
  // t.fetch
698
529
  let fetch_fn = v8::Function::new(scope, native_fetch).unwrap();
699
530
  let fetch_key = v8_str(scope, "fetch");
@@ -703,12 +534,12 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
703
534
  let jwt_obj = v8::Object::new(scope);
704
535
  let sign_fn = v8::Function::new(scope, native_jwt_sign).unwrap();
705
536
  let verify_fn = v8::Function::new(scope, native_jwt_verify).unwrap();
706
-
537
+
707
538
  let sign_key = v8_str(scope, "sign");
708
539
  jwt_obj.set(scope, sign_key.into(), sign_fn.into());
709
540
  let verify_key = v8_str(scope, "verify");
710
541
  jwt_obj.set(scope, verify_key.into(), verify_fn.into());
711
-
542
+
712
543
  let jwt_key = v8_str(scope, "jwt");
713
544
  t_obj.set(scope, jwt_key.into(), jwt_obj.into());
714
545
 
@@ -716,15 +547,16 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
716
547
  let pw_obj = v8::Object::new(scope);
717
548
  let hash_fn = v8::Function::new(scope, native_password_hash).unwrap();
718
549
  let pw_verify_fn = v8::Function::new(scope, native_password_verify).unwrap();
719
-
550
+
720
551
  let hash_key = v8_str(scope, "hash");
721
552
  pw_obj.set(scope, hash_key.into(), hash_fn.into());
722
553
  let pw_verify_key = v8_str(scope, "verify");
723
554
  pw_obj.set(scope, pw_verify_key.into(), pw_verify_fn.into());
724
-
555
+
725
556
  let pw_key = v8_str(scope, "password");
726
557
  t_obj.set(scope, pw_key.into(), pw_obj.into());
727
558
 
559
+
728
560
  // Inject __titan_invoke_native
729
561
  let invoke_fn = v8::Function::new(scope, native_invoke_extension).unwrap();
730
562
  let invoke_key = v8_str(scope, "__titan_invoke_native");
@@ -742,97 +574,60 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
742
574
  };
743
575
 
744
576
  for module in modules {
745
- // 1. Prepare Native Wrappers
746
- let natives_obj = v8::Object::new(scope);
747
- for (fn_name, &idx) in &module.native_indices {
748
- let code = format!(
749
- "(function(a, b) {{ return __titan_invoke_native({}, a, b); }})",
750
- idx
751
- );
752
- let source = v8_str(scope, &code);
753
- // Compile wrappers
754
- if let Some(script) = v8::Script::compile(scope, source, None) {
755
- if let Some(val) = script.run(scope) {
756
- let key = v8_str(scope, fn_name);
757
- natives_obj.set(scope, key.into(), val);
758
- }
759
- }
760
- }
761
-
762
- // 2. Prepare JS Wrapper (CommonJS shim)
763
- // We pass 't' and 'native' (the object we just made) to the module.
764
- let wrapper_src = format!(
765
- r#"(function(t, native) {{
766
- var module = {{ exports: {{}} }};
767
- var exports = module.exports;
768
- {}
769
- return module.exports;
770
- }})"#,
771
- module.js
772
- );
773
-
774
- let source = v8_str(scope, &wrapper_src);
775
- let tc = &mut v8::TryCatch::new(scope);
776
-
777
- // 3. Compile and Run
778
- if let Some(script) = v8::Script::compile(tc, source, None) {
779
- if let Some(factory_val) = script.run(tc) {
780
- if let Ok(factory) = v8::Local::<v8::Function>::try_from(factory_val) {
781
- let recv = v8::undefined(&mut *tc).into();
782
- // Pass t_obj and natives_obj
783
- let args = [t_obj.into(), natives_obj.into()];
784
-
785
- if let Some(exports_val) = factory.call(&mut *tc, recv, &args) {
786
- // 4. Assign exports to t.<extName>
787
- let mod_key = v8_str(&mut *tc, &module.name);
788
- t_obj.set(&mut *tc, mod_key.into(), exports_val);
789
-
790
- // println!(
791
- // "{} {} {}",
792
- // crate::utils::blue("[Titan]"),
793
- // crate::utils::green("Injected extension:"),
794
- // module.name
795
- // );
796
- } else {
797
- // Execution error
798
- if let Some(msg) = tc.message() {
799
- let text = msg.get(&mut *tc).to_rust_string_lossy(&mut *tc);
800
- println!(
801
- "{} {} {} -> {}",
802
- crate::utils::blue("[Titan]"),
803
- crate::utils::red("Error running extension"),
804
- module.name,
805
- text
806
- );
807
- }
808
- }
809
- }
810
- } else {
811
- // Runtime error during script run
812
- if let Some(msg) = tc.message() {
813
- let text = msg.get(&mut *tc).to_rust_string_lossy(&mut *tc);
814
- println!(
815
- "{} {} {} -> {}",
816
- crate::utils::blue("[Titan]"),
817
- crate::utils::red("Error evaluating extension wrapper"),
818
- module.name,
819
- text
820
- );
821
- }
822
- }
823
- } else {
824
- // Compile error
825
- if let Some(msg) = tc.message() {
826
- let text = msg.get(&mut *tc).to_rust_string_lossy(&mut *tc);
827
- println!(
828
- "{} {} {} -> {}",
829
- crate::utils::blue("[Titan]"),
830
- crate::utils::red("Syntax Error in extension"),
831
- module.name,
832
- text
833
- );
834
- }
835
- }
577
+ let mod_obj = v8::Object::new(scope);
578
+
579
+ // Generate JS wrappers
580
+ for (fn_name, &idx) in &module.native_indices {
581
+ let code = format!("(function(a, b) {{ return __titan_invoke_native({}, a, b); }})", idx);
582
+ let source = v8_str(scope, &code);
583
+ if let Some(script) = v8::Script::compile(scope, source, None) {
584
+ if let Some(val) = script.run(scope) {
585
+ let key = v8_str(scope, fn_name);
586
+ mod_obj.set(scope, key.into(), val);
587
+ }
588
+ }
589
+ }
590
+
591
+ // Inject t.<module_name>
592
+ let mod_key = v8_str(scope, &module.name);
593
+ t_obj.set(scope, mod_key.into(), mod_obj.into());
594
+
595
+ // Set context for logging
596
+ let action_key = v8_str(scope, "__titan_action");
597
+ let action_val = v8_str(scope, &module.name);
598
+ global.set(scope, action_key.into(), action_val.into());
599
+
600
+ // Execute JS
601
+ // Wrap in IIFE passing 't' to ensure visibility
602
+ let wrapped_js = format!("(function(t) {{ {} }})", module.js);
603
+ let source = v8_str(scope, &wrapped_js);
604
+ let tc = &mut v8::TryCatch::new(scope);
605
+
606
+ if let Some(script) = v8::Script::compile(tc, source, None) {
607
+ if let Some(func_val) = script.run(tc) {
608
+ // func_val is the function. Call it with [t_obj]
609
+ if let Ok(func) = v8::Local::<v8::Function>::try_from(func_val) {
610
+ let receiver = v8::undefined(&mut *tc).into();
611
+ let args = [t_obj.into()];
612
+ // Pass tc (which is a scope)
613
+ if func.call(&mut *tc, receiver, &args).is_none() {
614
+ println!("{} {}", crate::utils::blue("[Titan]"), crate::utils::red("Extension Execution Failed"));
615
+ if let Some(msg) = tc.message() {
616
+ let text = msg.get(&mut *tc).to_rust_string_lossy(&mut *tc);
617
+ println!("{} {}", crate::utils::red("Error details:"), text);
618
+ }
619
+ }
620
+ }
621
+ } else {
622
+ let msg = tc.message().unwrap();
623
+ let text = msg.get(&mut *tc).to_rust_string_lossy(&mut *tc);
624
+ println!("{} {} {}", crate::utils::blue("[Titan]"), crate::utils::red("Extension JS Error:"), text);
625
+ }
626
+ } else {
627
+ let msg = tc.message().unwrap();
628
+ let text = msg.get(&mut *tc).to_rust_string_lossy(&mut *tc);
629
+ println!("{} {} {}", crate::utils::blue("[Titan]"), crate::utils::red("Extension Compile Error:"), text);
630
+ }
836
631
  }
837
632
 
838
633
  // t.db (Stub for now)