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