@ezetgalaxy/titan 26.7.2 → 26.7.5

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,188 @@ 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 };
89
- 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()) {
92
- Ok(c) => c,
93
- Err(_) => continue,
94
- };
95
- let config: TitanConfig = match serde_json::from_str(&config_content) {
96
- Ok(c) => c,
97
- Err(_) => continue,
98
- };
99
102
 
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
- }
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();
132
111
 
133
- let js_path = dir.join(&config.main);
134
- let js_content = fs::read_to_string(js_path).unwrap_or_default();
112
+ println!("{} Scanning extension directories:", blue("[Titan]"));
113
+ for d in &search_dirs {
114
+ println!(" • {}", d.display());
115
+ }
135
116
 
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);
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
137
+ 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) {
204
+ 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
+ }
214
+ };
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
+ );
143
228
  }
144
229
  }
145
230
  }
146
231
 
147
- *REGISTRY.lock().unwrap() = Some(Registry { _libs: libs, modules, natives: all_natives });
148
- }
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
+ }
149
251
 
252
+ *REGISTRY.lock().unwrap() = Some(Registry {
253
+ _libs: libs,
254
+ modules,
255
+ natives: all_natives,
256
+ });
257
+ }
150
258
 
151
259
  static V8_INIT: Once = Once::new();
152
260
 
@@ -176,7 +284,11 @@ fn throw(scope: &mut v8::HandleScope, msg: &str) {
176
284
  // NATIVE CALLBACKS
177
285
  // ----------------------------------------------------------------------------
178
286
 
179
- fn native_read(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
287
+ fn native_read(
288
+ scope: &mut v8::HandleScope,
289
+ args: v8::FunctionCallbackArguments,
290
+ mut retval: v8::ReturnValue,
291
+ ) {
180
292
  let path_val = args.get(0);
181
293
  // 1. Read argument
182
294
  if !path_val.is_string() {
@@ -195,7 +307,7 @@ fn native_read(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments,
195
307
  let global = context.global(scope);
196
308
  let root_key = v8_str(scope, "__titan_root");
197
309
  let root_val = global.get(scope, root_key.into()).unwrap();
198
-
310
+
199
311
  let root_str = if root_val.is_string() {
200
312
  v8_to_string(scope, root_val)
201
313
  } else {
@@ -226,14 +338,18 @@ fn native_read(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments,
226
338
  match std::fs::read_to_string(&target) {
227
339
  Ok(content) => {
228
340
  retval.set(v8_str(scope, &content).into());
229
- },
341
+ }
230
342
  Err(e) => {
231
343
  throw(scope, &format!("t.read failed: {}", e));
232
344
  }
233
345
  }
234
346
  }
235
347
 
236
- fn native_log(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut _retval: v8::ReturnValue) {
348
+ fn native_log(
349
+ scope: &mut v8::HandleScope,
350
+ args: v8::FunctionCallbackArguments,
351
+ mut _retval: v8::ReturnValue,
352
+ ) {
237
353
  let context = scope.get_current_context();
238
354
  let global = context.global(scope);
239
355
  let action_key = v8_str(scope, "__titan_action");
@@ -244,30 +360,38 @@ fn native_log(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments,
244
360
  for i in 0..args.length() {
245
361
  let val = args.get(i);
246
362
  let mut appended = false;
247
-
363
+
248
364
  // Try to JSON stringify objects so they are readable in logs
249
365
  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
- }
366
+ if let Some(json) = v8::json::stringify(scope, val) {
367
+ parts.push(json.to_rust_string_lossy(scope));
368
+ appended = true;
369
+ }
254
370
  }
255
-
371
+
256
372
  if !appended {
257
373
  parts.push(v8_to_string(scope, val));
258
374
  }
259
375
  }
260
-
376
+
261
377
  println!(
262
378
  "{} {}",
263
379
  blue("[Titan]"),
264
- gray(&format!("\x1b[90mlog({})\x1b[0m\x1b[97m: {}\x1b[0m", action_name, parts.join(" ")))
380
+ gray(&format!(
381
+ "\x1b[90mlog({})\x1b[0m\x1b[97m: {}\x1b[0m",
382
+ action_name,
383
+ parts.join(" ")
384
+ ))
265
385
  );
266
386
  }
267
387
 
268
- fn native_fetch(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
388
+ fn native_fetch(
389
+ scope: &mut v8::HandleScope,
390
+ args: v8::FunctionCallbackArguments,
391
+ mut retval: v8::ReturnValue,
392
+ ) {
269
393
  let url = v8_to_string(scope, args.get(0));
270
-
394
+
271
395
  // Check for options (method, headers, body)
272
396
  let mut method = "GET".to_string();
273
397
  let mut body_str = None;
@@ -276,7 +400,7 @@ fn native_fetch(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments
276
400
  let opts_val = args.get(1);
277
401
  if opts_val.is_object() {
278
402
  let opts_obj = opts_val.to_object(scope).unwrap();
279
-
403
+
280
404
  // method
281
405
  let m_key = v8_str(scope, "method");
282
406
  if let Some(m_val) = opts_obj.get(scope, m_key.into()) {
@@ -284,18 +408,18 @@ fn native_fetch(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments
284
408
  method = v8_to_string(scope, m_val);
285
409
  }
286
410
  }
287
-
411
+
288
412
  // body
289
413
  let b_key = v8_str(scope, "body");
290
414
  if let Some(b_val) = opts_obj.get(scope, b_key.into()) {
291
415
  if b_val.is_string() {
292
416
  body_str = Some(v8_to_string(scope, b_val));
293
417
  } 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));
418
+ let json_obj = v8::json::stringify(scope, b_val).unwrap();
419
+ body_str = Some(json_obj.to_rust_string_lossy(scope));
296
420
  }
297
421
  }
298
-
422
+
299
423
  // headers
300
424
  let h_key = v8_str(scope, "headers");
301
425
  if let Some(h_val) = opts_obj.get(scope, h_key.into()) {
@@ -305,57 +429,61 @@ fn native_fetch(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments
305
429
  for i in 0..keys.length() {
306
430
  let key = keys.get_index(scope, i).unwrap();
307
431
  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
- ));
432
+ headers_vec.push((v8_to_string(scope, key), v8_to_string(scope, val)));
312
433
  }
313
434
  }
314
435
  }
315
436
  }
316
437
  }
317
438
 
318
- let client = Client::builder().use_rustls_tls().tcp_nodelay(true).build().unwrap_or(Client::new());
319
-
439
+ let client = Client::builder()
440
+ .use_rustls_tls()
441
+ .tcp_nodelay(true)
442
+ .build()
443
+ .unwrap_or(Client::new());
444
+
320
445
  let mut req = client.request(method.parse().unwrap_or(reqwest::Method::GET), &url);
321
-
446
+
322
447
  for (k, v) in headers_vec {
323
- if let (Ok(name), Ok(val)) = (HeaderName::from_bytes(k.as_bytes()), HeaderValue::from_str(&v)) {
448
+ if let (Ok(name), Ok(val)) = (
449
+ HeaderName::from_bytes(k.as_bytes()),
450
+ HeaderValue::from_str(&v),
451
+ ) {
324
452
  let mut map = HeaderMap::new();
325
453
  map.insert(name, val);
326
454
  req = req.headers(map);
327
455
  }
328
456
  }
329
-
457
+
330
458
  if let Some(b) = body_str {
331
459
  req = req.body(b);
332
460
  }
333
-
461
+
334
462
  let res = req.send();
335
-
463
+
336
464
  let obj = v8::Object::new(scope);
337
465
  match res {
338
466
  Ok(r) => {
339
467
  let status = r.status().as_u16();
340
468
  let text = r.text().unwrap_or_default();
341
-
469
+
342
470
  let status_key = v8_str(scope, "status");
343
471
  let status_val = v8::Number::new(scope, status as f64);
344
472
  obj.set(scope, status_key.into(), status_val.into());
345
-
473
+
346
474
  let body_key = v8_str(scope, "body");
347
475
  let body_val = v8_str(scope, &text);
348
476
  obj.set(scope, body_key.into(), body_val.into());
349
-
477
+
350
478
  let ok_key = v8_str(scope, "ok");
351
479
  let ok_val = v8::Boolean::new(scope, true);
352
480
  obj.set(scope, ok_key.into(), ok_val.into());
353
- },
481
+ }
354
482
  Err(e) => {
355
483
  let ok_key = v8_str(scope, "ok");
356
484
  let ok_val = v8::Boolean::new(scope, false);
357
485
  obj.set(scope, ok_key.into(), ok_val.into());
358
-
486
+
359
487
  let err_key = v8_str(scope, "error");
360
488
  let err_val = v8_str(scope, &e.to_string());
361
489
  obj.set(scope, err_key.into(), err_val.into());
@@ -364,33 +492,46 @@ fn native_fetch(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments
364
492
  retval.set(obj.into());
365
493
  }
366
494
 
367
- fn native_jwt_sign(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
495
+ fn native_jwt_sign(
496
+ scope: &mut v8::HandleScope,
497
+ args: v8::FunctionCallbackArguments,
498
+ mut retval: v8::ReturnValue,
499
+ ) {
368
500
  // payload, secret, options
369
501
  let payload_val = args.get(0);
370
502
  // 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();
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();
373
508
 
374
509
  let secret = v8_to_string(scope, args.get(1));
375
-
510
+
376
511
  let opts_val = args.get(2);
377
512
  if opts_val.is_object() {
378
513
  let opts_obj = opts_val.to_object(scope).unwrap();
379
514
  let exp_key = v8_str(scope, "expiresIn");
380
-
515
+
381
516
  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
- }
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
+ }
394
535
  }
395
536
  }
396
537
 
@@ -406,33 +547,41 @@ fn native_jwt_sign(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArgume
406
547
  }
407
548
  }
408
549
 
409
- fn native_jwt_verify(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
550
+ fn native_jwt_verify(
551
+ scope: &mut v8::HandleScope,
552
+ args: v8::FunctionCallbackArguments,
553
+ mut retval: v8::ReturnValue,
554
+ ) {
410
555
  let token = v8_to_string(scope, args.get(0));
411
556
  let secret = v8_to_string(scope, args.get(1));
412
-
557
+
413
558
  let mut validation = Validation::default();
414
559
  validation.validate_exp = true;
415
-
560
+
416
561
  let data = decode::<Value>(
417
562
  &token,
418
563
  &DecodingKey::from_secret(secret.as_bytes()),
419
564
  &validation,
420
565
  );
421
-
566
+
422
567
  match data {
423
568
  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
- },
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
+ }
431
576
  Err(e) => throw(scope, &format!("Invalid or expired JWT: {}", e)),
432
577
  }
433
578
  }
434
579
 
435
- fn native_password_hash(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
580
+ fn native_password_hash(
581
+ scope: &mut v8::HandleScope,
582
+ args: v8::FunctionCallbackArguments,
583
+ mut retval: v8::ReturnValue,
584
+ ) {
436
585
  let pw = v8_to_string(scope, args.get(0));
437
586
  match hash(pw, DEFAULT_COST) {
438
587
  Ok(h) => retval.set(v8_str(scope, &h).into()),
@@ -440,15 +589,23 @@ fn native_password_hash(scope: &mut v8::HandleScope, args: v8::FunctionCallbackA
440
589
  }
441
590
  }
442
591
 
443
- fn native_password_verify(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
592
+ fn native_password_verify(
593
+ scope: &mut v8::HandleScope,
594
+ args: v8::FunctionCallbackArguments,
595
+ mut retval: v8::ReturnValue,
596
+ ) {
444
597
  let pw = v8_to_string(scope, args.get(0));
445
598
  let hash_str = v8_to_string(scope, args.get(1));
446
-
599
+
447
600
  let ok = verify(pw, &hash_str).unwrap_or(false);
448
601
  retval.set(v8::Boolean::new(scope, ok).into());
449
602
  }
450
603
 
451
- fn native_define_action(_scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
604
+ fn native_define_action(
605
+ _scope: &mut v8::HandleScope,
606
+ args: v8::FunctionCallbackArguments,
607
+ mut retval: v8::ReturnValue,
608
+ ) {
452
609
  retval.set(args.get(0));
453
610
  }
454
611
 
@@ -458,13 +615,17 @@ fn native_define_action(_scope: &mut v8::HandleScope, args: v8::FunctionCallback
458
615
 
459
616
  // generic wrappers could go here if needed
460
617
 
461
- fn native_invoke_extension(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
618
+ fn native_invoke_extension(
619
+ scope: &mut v8::HandleScope,
620
+ args: v8::FunctionCallbackArguments,
621
+ mut retval: v8::ReturnValue,
622
+ ) {
462
623
  let fn_idx = args.get(0).to_integer(scope).unwrap().value() as usize;
463
624
 
464
625
  // Get pointer from registry
465
626
  let mut ptr = 0;
466
627
  let mut sig = Signature::Unknown;
467
-
628
+
468
629
  if let Ok(guard) = REGISTRY.lock() {
469
630
  if let Some(registry) = &*guard {
470
631
  if let Some(entry) = registry.natives.get(fn_idx) {
@@ -473,33 +634,39 @@ fn native_invoke_extension(scope: &mut v8::HandleScope, args: v8::FunctionCallba
473
634
  }
474
635
  }
475
636
  }
476
-
637
+
477
638
  if ptr == 0 {
478
- throw(scope, "Native function not found");
479
- return;
639
+ throw(scope, "Native function not found");
640
+ return;
480
641
  }
481
642
 
482
643
  match sig {
483
644
  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
- },
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
+ }
493
662
  _ => throw(scope, "Unsupported signature"),
494
663
  }
495
664
  }
496
665
 
497
-
498
666
  // ----------------------------------------------------------------------------
499
667
  // INJECTOR
500
668
  // ----------------------------------------------------------------------------
501
669
 
502
-
503
670
  pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Object>) {
504
671
  // Ensure globalThis reference
505
672
  let gt_key = v8_str(scope, "globalThis");
@@ -508,13 +675,15 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
508
675
  let t_obj = v8::Object::new(scope);
509
676
  let t_key = v8_str(scope, "t");
510
677
  // Use create_data_property to guarantee definition
511
- global.create_data_property(scope, t_key.into(), t_obj.into()).unwrap();
678
+ global
679
+ .create_data_property(scope, t_key.into(), t_obj.into())
680
+ .unwrap();
512
681
 
513
682
  // defineAction (identity function for clean typing)
514
683
  let def_fn = v8::Function::new(scope, native_define_action).unwrap();
515
684
  let def_key = v8_str(scope, "defineAction");
516
685
  global.set(scope, def_key.into(), def_fn.into());
517
-
686
+
518
687
  // t.read
519
688
  let read_fn = v8::Function::new(scope, native_read).unwrap();
520
689
  let read_key = v8_str(scope, "read");
@@ -524,7 +693,7 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
524
693
  let log_fn = v8::Function::new(scope, native_log).unwrap();
525
694
  let log_key = v8_str(scope, "log");
526
695
  t_obj.set(scope, log_key.into(), log_fn.into());
527
-
696
+
528
697
  // t.fetch
529
698
  let fetch_fn = v8::Function::new(scope, native_fetch).unwrap();
530
699
  let fetch_key = v8_str(scope, "fetch");
@@ -534,12 +703,12 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
534
703
  let jwt_obj = v8::Object::new(scope);
535
704
  let sign_fn = v8::Function::new(scope, native_jwt_sign).unwrap();
536
705
  let verify_fn = v8::Function::new(scope, native_jwt_verify).unwrap();
537
-
706
+
538
707
  let sign_key = v8_str(scope, "sign");
539
708
  jwt_obj.set(scope, sign_key.into(), sign_fn.into());
540
709
  let verify_key = v8_str(scope, "verify");
541
710
  jwt_obj.set(scope, verify_key.into(), verify_fn.into());
542
-
711
+
543
712
  let jwt_key = v8_str(scope, "jwt");
544
713
  t_obj.set(scope, jwt_key.into(), jwt_obj.into());
545
714
 
@@ -547,16 +716,15 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
547
716
  let pw_obj = v8::Object::new(scope);
548
717
  let hash_fn = v8::Function::new(scope, native_password_hash).unwrap();
549
718
  let pw_verify_fn = v8::Function::new(scope, native_password_verify).unwrap();
550
-
719
+
551
720
  let hash_key = v8_str(scope, "hash");
552
721
  pw_obj.set(scope, hash_key.into(), hash_fn.into());
553
722
  let pw_verify_key = v8_str(scope, "verify");
554
723
  pw_obj.set(scope, pw_verify_key.into(), pw_verify_fn.into());
555
-
724
+
556
725
  let pw_key = v8_str(scope, "password");
557
726
  t_obj.set(scope, pw_key.into(), pw_obj.into());
558
727
 
559
-
560
728
  // Inject __titan_invoke_native
561
729
  let invoke_fn = v8::Function::new(scope, native_invoke_extension).unwrap();
562
730
  let invoke_key = v8_str(scope, "__titan_invoke_native");
@@ -574,60 +742,97 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
574
742
  };
575
743
 
576
744
  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
- }
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
+ }
631
836
  }
632
837
 
633
838
  // t.db (Stub for now)