@ezetgalaxy/titan 26.12.1 → 26.12.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezetgalaxy/titan",
3
- "version": "26.12.1",
3
+ "version": "26.12.2",
4
4
  "description": "Titan Planet is a JavaScript-first backend framework that embeds JS actions into a Rust + Axum server and ships as a single native binary. Routes are compiled to static metadata; only actions run in the embedded JS runtime. No Node.js. No event loop in production.",
5
5
  "license": "ISC",
6
6
  "author": "ezetgalaxy",
@@ -8,12 +8,19 @@ use std::time::{SystemTime, UNIX_EPOCH};
8
8
  use serde_json::Value;
9
9
  use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
10
10
  use bcrypt::{hash, verify, DEFAULT_COST};
11
+ use postgres::{Client as PgClient, NoTls};
12
+ use std::sync::Mutex;
13
+ use std::collections::HashMap;
11
14
 
12
15
  use crate::utils::{blue, gray, parse_expires_in};
13
16
  use super::{TitanRuntime, v8_str, v8_to_string, throw, ShareContextStore};
14
17
 
15
18
  const TITAN_CORE_JS: &str = include_str!("titan_core.js");
16
19
 
20
+ // Database connection pool
21
+ static DB_POOL: Mutex<Option<HashMap<String, PgClient>>> = Mutex::new(None);
22
+
23
+
17
24
  pub fn inject_builtin_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Object>, t_obj: v8::Local<v8::Object>) {
18
25
  // 1. Native API Bindings
19
26
 
@@ -43,6 +50,11 @@ pub fn inject_builtin_extensions(scope: &mut v8::HandleScope, global: v8::Local<
43
50
  let fetch_key = v8_str(scope, "fetch");
44
51
  t_obj.set(scope, fetch_key.into(), fetch_fn.into());
45
52
 
53
+ // t.loadEnv
54
+ let env_fn = v8::Function::new(scope, native_load_env).unwrap();
55
+ let env_key = v8_str(scope, "loadEnv");
56
+ t_obj.set(scope, env_key.into(), env_fn.into());
57
+
46
58
  // auth, jwt, password ... (keep native)
47
59
  setup_native_utils(scope, t_obj);
48
60
 
@@ -102,6 +114,17 @@ fn setup_native_utils(scope: &mut v8::HandleScope, t_obj: v8::Local<v8::Object>)
102
114
  let sc_key = v8_str(scope, "shareContext");
103
115
  let sc_val = sc_obj.into();
104
116
  t_obj.set(scope, sc_key.into(), sc_val);
117
+
118
+ // t.db (Database operations)
119
+ println!("[DEBUG] Setting up t.db...");
120
+ let db_obj = v8::Object::new(scope);
121
+ let db_connect_fn = v8::Function::new(scope, native_db_connect).unwrap();
122
+ let connect_key = v8_str(scope, "connect");
123
+ db_obj.set(scope, connect_key.into(), db_connect_fn.into());
124
+
125
+ let db_key = v8_str(scope, "db");
126
+ t_obj.set(scope, db_key.into(), db_obj.into());
127
+ println!("[DEBUG] t.db setup complete!");
105
128
  }
106
129
 
107
130
  fn native_read(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
@@ -428,6 +451,138 @@ fn native_password_verify(scope: &mut v8::HandleScope, args: v8::FunctionCallbac
428
451
  retval.set(v8::Boolean::new(scope, ok).into());
429
452
  }
430
453
 
454
+ fn native_load_env(scope: &mut v8::HandleScope, _args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
455
+ use serde_json::json;
456
+
457
+ let mut map = serde_json::Map::new();
458
+
459
+ for (key, value) in std::env::vars() {
460
+ map.insert(key, json!(value));
461
+ }
462
+
463
+ let json_str = serde_json::to_string(&map).unwrap();
464
+ let v8_str = v8::String::new(scope, &json_str).unwrap();
465
+
466
+ if let Some(obj) = v8::json::parse(scope, v8_str) {
467
+ retval.set(obj);
468
+ } else {
469
+ retval.set(v8::null(scope).into());
470
+ }
471
+ }
472
+
431
473
  fn native_define_action(_scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
432
474
  retval.set(args.get(0));
433
475
  }
476
+
477
+ fn native_db_connect(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
478
+ let conn_string = v8_to_string(scope, args.get(0));
479
+
480
+ if conn_string.is_empty() {
481
+ throw(scope, "t.db.connect(): connection string is required");
482
+ return;
483
+ }
484
+
485
+ // Test connection immediately
486
+ match PgClient::connect(&conn_string, NoTls) {
487
+ Ok(mut client) => {
488
+ // Store in pool
489
+ let mut pool = DB_POOL.lock().unwrap();
490
+ let map = pool.get_or_insert_with(HashMap::new);
491
+ map.insert(conn_string.clone(), client);
492
+ },
493
+ Err(e) => {
494
+ throw(scope, &format!("Database connection failed: {}", e));
495
+ return;
496
+ }
497
+ }
498
+
499
+ // Return a DB connection object with methods
500
+ let db_conn_obj = v8::Object::new(scope);
501
+
502
+ // Store connection string in a hidden property
503
+ let conn_key = v8_str(scope, "__conn_string");
504
+ let conn_val = v8_str(scope, &conn_string);
505
+ db_conn_obj.set(scope, conn_key.into(), conn_val.into());
506
+
507
+ // Add query method
508
+ let query_fn = v8::Function::new(scope, native_db_query).unwrap();
509
+ let query_key = v8_str(scope, "query");
510
+ db_conn_obj.set(scope, query_key.into(), query_fn.into());
511
+
512
+ retval.set(db_conn_obj.into());
513
+ }
514
+
515
+ fn native_db_query(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
516
+ // Get 'this' context (the db connection object)
517
+ let this = args.this();
518
+ let this_obj = this.to_object(scope).unwrap();
519
+
520
+ // Retrieve connection string
521
+ let conn_key = v8_str(scope, "__conn_string");
522
+ let conn_val = this_obj.get(scope, conn_key.into()).unwrap();
523
+ let conn_string = v8_to_string(scope, conn_val);
524
+
525
+ // Get query string
526
+ let query = v8_to_string(scope, args.get(0));
527
+
528
+ if query.is_empty() {
529
+ throw(scope, "db.query(): SQL query is required");
530
+ return;
531
+ }
532
+
533
+ // Get connection from pool
534
+ let mut pool = DB_POOL.lock().unwrap();
535
+ let map = pool.as_mut().unwrap();
536
+
537
+ let client = match map.get_mut(&conn_string) {
538
+ Some(c) => c,
539
+ None => {
540
+ throw(scope, "Database connection not found in pool");
541
+ return;
542
+ }
543
+ };
544
+
545
+ // Execute query
546
+ match client.query(&query, &[]) {
547
+ Ok(rows) => {
548
+ let mut result = Vec::new();
549
+
550
+ for row in rows {
551
+ let mut obj = serde_json::Map::new();
552
+
553
+ for (i, column) in row.columns().iter().enumerate() {
554
+ let col_name = column.name();
555
+ let col_value: serde_json::Value = if let Ok(val) = row.try_get::<_, Option<String>>(i) {
556
+ serde_json::json!(val)
557
+ } else if let Ok(val) = row.try_get::<_, Option<i32>>(i) {
558
+ serde_json::json!(val)
559
+ } else if let Ok(val) = row.try_get::<_, Option<i64>>(i) {
560
+ serde_json::json!(val)
561
+ } else if let Ok(val) = row.try_get::<_, Option<f64>>(i) {
562
+ serde_json::json!(val)
563
+ } else if let Ok(val) = row.try_get::<_, Option<bool>>(i) {
564
+ serde_json::json!(val)
565
+ } else {
566
+ serde_json::Value::Null
567
+ };
568
+
569
+ obj.insert(col_name.to_string(), col_value);
570
+ }
571
+
572
+ result.push(serde_json::Value::Object(obj));
573
+ }
574
+
575
+ let json_str = serde_json::to_string(&result).unwrap();
576
+ let v8_json_str = v8_str(scope, &json_str);
577
+
578
+ if let Some(val) = v8::json::parse(scope, v8_json_str) {
579
+ retval.set(val);
580
+ } else {
581
+ retval.set(v8::Array::new(scope, 0).into());
582
+ }
583
+ },
584
+ Err(e) => {
585
+ throw(scope, &format!("Query failed: {}", e));
586
+ }
587
+ }
588
+ }
@@ -132,11 +132,6 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
132
132
  // Call individual injectors
133
133
  builtin::inject_builtin_extensions(scope, global, t_obj);
134
134
  external::inject_external_extensions(scope, global, t_obj);
135
-
136
- // Inject t.db (Stub)
137
- let db_obj = v8::Object::new(scope);
138
- let db_key = v8_str(scope, "db");
139
- t_obj.set(scope, db_key.into(), db_obj.into());
140
135
 
141
136
  global.set(scope, t_key.into(), t_obj.into());
142
137
  }
@@ -14,4 +14,9 @@ globalThis.TextDecoder = class TextDecoder {
14
14
  }
15
15
  };
16
16
 
17
+ // Process environment variables
18
+ globalThis.process = {
19
+ env: t.loadEnv()
20
+ };
21
+
17
22
  // Everything is strictly synchronous and request-driven.
@@ -8,12 +8,19 @@ use std::time::{SystemTime, UNIX_EPOCH};
8
8
  use serde_json::Value;
9
9
  use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
10
10
  use bcrypt::{hash, verify, DEFAULT_COST};
11
+ use postgres::{Client as PgClient, NoTls};
12
+ use std::sync::Mutex;
13
+ use std::collections::HashMap;
11
14
 
12
15
  use crate::utils::{blue, gray, parse_expires_in};
13
16
  use super::{TitanRuntime, v8_str, v8_to_string, throw, ShareContextStore};
14
17
 
15
18
  const TITAN_CORE_JS: &str = include_str!("titan_core.js");
16
19
 
20
+ // Database connection pool
21
+ static DB_POOL: Mutex<Option<HashMap<String, PgClient>>> = Mutex::new(None);
22
+
23
+
17
24
  pub fn inject_builtin_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Object>, t_obj: v8::Local<v8::Object>) {
18
25
  // 1. Native API Bindings
19
26
 
@@ -43,6 +50,11 @@ pub fn inject_builtin_extensions(scope: &mut v8::HandleScope, global: v8::Local<
43
50
  let fetch_key = v8_str(scope, "fetch");
44
51
  t_obj.set(scope, fetch_key.into(), fetch_fn.into());
45
52
 
53
+ // t.loadEnv
54
+ let env_fn = v8::Function::new(scope, native_load_env).unwrap();
55
+ let env_key = v8_str(scope, "loadEnv");
56
+ t_obj.set(scope, env_key.into(), env_fn.into());
57
+
46
58
  // auth, jwt, password ... (keep native)
47
59
  setup_native_utils(scope, t_obj);
48
60
 
@@ -102,6 +114,17 @@ fn setup_native_utils(scope: &mut v8::HandleScope, t_obj: v8::Local<v8::Object>)
102
114
  let sc_key = v8_str(scope, "shareContext");
103
115
  let sc_val = sc_obj.into();
104
116
  t_obj.set(scope, sc_key.into(), sc_val);
117
+
118
+ // t.db (Database operations)
119
+ println!("[DEBUG] Setting up t.db...");
120
+ let db_obj = v8::Object::new(scope);
121
+ let db_connect_fn = v8::Function::new(scope, native_db_connect).unwrap();
122
+ let connect_key = v8_str(scope, "connect");
123
+ db_obj.set(scope, connect_key.into(), db_connect_fn.into());
124
+
125
+ let db_key = v8_str(scope, "db");
126
+ t_obj.set(scope, db_key.into(), db_obj.into());
127
+ println!("[DEBUG] t.db setup complete!");
105
128
  }
106
129
 
107
130
  fn native_read(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
@@ -428,6 +451,138 @@ fn native_password_verify(scope: &mut v8::HandleScope, args: v8::FunctionCallbac
428
451
  retval.set(v8::Boolean::new(scope, ok).into());
429
452
  }
430
453
 
454
+ fn native_load_env(scope: &mut v8::HandleScope, _args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
455
+ use serde_json::json;
456
+
457
+ let mut map = serde_json::Map::new();
458
+
459
+ for (key, value) in std::env::vars() {
460
+ map.insert(key, json!(value));
461
+ }
462
+
463
+ let json_str = serde_json::to_string(&map).unwrap();
464
+ let v8_str = v8::String::new(scope, &json_str).unwrap();
465
+
466
+ if let Some(obj) = v8::json::parse(scope, v8_str) {
467
+ retval.set(obj);
468
+ } else {
469
+ retval.set(v8::null(scope).into());
470
+ }
471
+ }
472
+
431
473
  fn native_define_action(_scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
432
474
  retval.set(args.get(0));
433
475
  }
476
+
477
+ fn native_db_connect(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
478
+ let conn_string = v8_to_string(scope, args.get(0));
479
+
480
+ if conn_string.is_empty() {
481
+ throw(scope, "t.db.connect(): connection string is required");
482
+ return;
483
+ }
484
+
485
+ // Test connection immediately
486
+ match PgClient::connect(&conn_string, NoTls) {
487
+ Ok(mut client) => {
488
+ // Store in pool
489
+ let mut pool = DB_POOL.lock().unwrap();
490
+ let map = pool.get_or_insert_with(HashMap::new);
491
+ map.insert(conn_string.clone(), client);
492
+ },
493
+ Err(e) => {
494
+ throw(scope, &format!("Database connection failed: {}", e));
495
+ return;
496
+ }
497
+ }
498
+
499
+ // Return a DB connection object with methods
500
+ let db_conn_obj = v8::Object::new(scope);
501
+
502
+ // Store connection string in a hidden property
503
+ let conn_key = v8_str(scope, "__conn_string");
504
+ let conn_val = v8_str(scope, &conn_string);
505
+ db_conn_obj.set(scope, conn_key.into(), conn_val.into());
506
+
507
+ // Add query method
508
+ let query_fn = v8::Function::new(scope, native_db_query).unwrap();
509
+ let query_key = v8_str(scope, "query");
510
+ db_conn_obj.set(scope, query_key.into(), query_fn.into());
511
+
512
+ retval.set(db_conn_obj.into());
513
+ }
514
+
515
+ fn native_db_query(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
516
+ // Get 'this' context (the db connection object)
517
+ let this = args.this();
518
+ let this_obj = this.to_object(scope).unwrap();
519
+
520
+ // Retrieve connection string
521
+ let conn_key = v8_str(scope, "__conn_string");
522
+ let conn_val = this_obj.get(scope, conn_key.into()).unwrap();
523
+ let conn_string = v8_to_string(scope, conn_val);
524
+
525
+ // Get query string
526
+ let query = v8_to_string(scope, args.get(0));
527
+
528
+ if query.is_empty() {
529
+ throw(scope, "db.query(): SQL query is required");
530
+ return;
531
+ }
532
+
533
+ // Get connection from pool
534
+ let mut pool = DB_POOL.lock().unwrap();
535
+ let map = pool.as_mut().unwrap();
536
+
537
+ let client = match map.get_mut(&conn_string) {
538
+ Some(c) => c,
539
+ None => {
540
+ throw(scope, "Database connection not found in pool");
541
+ return;
542
+ }
543
+ };
544
+
545
+ // Execute query
546
+ match client.query(&query, &[]) {
547
+ Ok(rows) => {
548
+ let mut result = Vec::new();
549
+
550
+ for row in rows {
551
+ let mut obj = serde_json::Map::new();
552
+
553
+ for (i, column) in row.columns().iter().enumerate() {
554
+ let col_name = column.name();
555
+ let col_value: serde_json::Value = if let Ok(val) = row.try_get::<_, Option<String>>(i) {
556
+ serde_json::json!(val)
557
+ } else if let Ok(val) = row.try_get::<_, Option<i32>>(i) {
558
+ serde_json::json!(val)
559
+ } else if let Ok(val) = row.try_get::<_, Option<i64>>(i) {
560
+ serde_json::json!(val)
561
+ } else if let Ok(val) = row.try_get::<_, Option<f64>>(i) {
562
+ serde_json::json!(val)
563
+ } else if let Ok(val) = row.try_get::<_, Option<bool>>(i) {
564
+ serde_json::json!(val)
565
+ } else {
566
+ serde_json::Value::Null
567
+ };
568
+
569
+ obj.insert(col_name.to_string(), col_value);
570
+ }
571
+
572
+ result.push(serde_json::Value::Object(obj));
573
+ }
574
+
575
+ let json_str = serde_json::to_string(&result).unwrap();
576
+ let v8_json_str = v8_str(scope, &json_str);
577
+
578
+ if let Some(val) = v8::json::parse(scope, v8_json_str) {
579
+ retval.set(val);
580
+ } else {
581
+ retval.set(v8::Array::new(scope, 0).into());
582
+ }
583
+ },
584
+ Err(e) => {
585
+ throw(scope, &format!("Query failed: {}", e));
586
+ }
587
+ }
588
+ }
@@ -132,11 +132,6 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
132
132
  // Call individual injectors
133
133
  builtin::inject_builtin_extensions(scope, global, t_obj);
134
134
  external::inject_external_extensions(scope, global, t_obj);
135
-
136
- // Inject t.db (Stub)
137
- let db_obj = v8::Object::new(scope);
138
- let db_key = v8_str(scope, "db");
139
- t_obj.set(scope, db_key.into(), db_obj.into());
140
135
 
141
136
  global.set(scope, t_key.into(), t_obj.into());
142
137
  }
@@ -14,4 +14,9 @@ globalThis.TextDecoder = class TextDecoder {
14
14
  }
15
15
  };
16
16
 
17
+ // Process environment variables
18
+ globalThis.process = {
19
+ env: t.loadEnv()
20
+ };
21
+
17
22
  // Everything is strictly synchronous and request-driven.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "titanpl-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Development SDK for Titan Planet. Provides TypeScript type definitions for the global 't' runtime object and a 'lite' test-harness runtime for building and verifying extensions.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -8,12 +8,19 @@ use std::time::{SystemTime, UNIX_EPOCH};
8
8
  use serde_json::Value;
9
9
  use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
10
10
  use bcrypt::{hash, verify, DEFAULT_COST};
11
+ use postgres::{Client as PgClient, NoTls};
12
+ use std::sync::Mutex;
13
+ use std::collections::HashMap;
11
14
 
12
15
  use crate::utils::{blue, gray, parse_expires_in};
13
16
  use super::{TitanRuntime, v8_str, v8_to_string, throw, ShareContextStore};
14
17
 
15
18
  const TITAN_CORE_JS: &str = include_str!("titan_core.js");
16
19
 
20
+ // Database connection pool
21
+ static DB_POOL: Mutex<Option<HashMap<String, PgClient>>> = Mutex::new(None);
22
+
23
+
17
24
  pub fn inject_builtin_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Object>, t_obj: v8::Local<v8::Object>) {
18
25
  // 1. Native API Bindings
19
26
 
@@ -43,6 +50,11 @@ pub fn inject_builtin_extensions(scope: &mut v8::HandleScope, global: v8::Local<
43
50
  let fetch_key = v8_str(scope, "fetch");
44
51
  t_obj.set(scope, fetch_key.into(), fetch_fn.into());
45
52
 
53
+ // t.loadEnv
54
+ let env_fn = v8::Function::new(scope, native_load_env).unwrap();
55
+ let env_key = v8_str(scope, "loadEnv");
56
+ t_obj.set(scope, env_key.into(), env_fn.into());
57
+
46
58
  // auth, jwt, password ... (keep native)
47
59
  setup_native_utils(scope, t_obj);
48
60
 
@@ -102,6 +114,17 @@ fn setup_native_utils(scope: &mut v8::HandleScope, t_obj: v8::Local<v8::Object>)
102
114
  let sc_key = v8_str(scope, "shareContext");
103
115
  let sc_val = sc_obj.into();
104
116
  t_obj.set(scope, sc_key.into(), sc_val);
117
+
118
+ // t.db (Database operations)
119
+ println!("[DEBUG] Setting up t.db...");
120
+ let db_obj = v8::Object::new(scope);
121
+ let db_connect_fn = v8::Function::new(scope, native_db_connect).unwrap();
122
+ let connect_key = v8_str(scope, "connect");
123
+ db_obj.set(scope, connect_key.into(), db_connect_fn.into());
124
+
125
+ let db_key = v8_str(scope, "db");
126
+ t_obj.set(scope, db_key.into(), db_obj.into());
127
+ println!("[DEBUG] t.db setup complete!");
105
128
  }
106
129
 
107
130
  fn native_read(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
@@ -428,6 +451,138 @@ fn native_password_verify(scope: &mut v8::HandleScope, args: v8::FunctionCallbac
428
451
  retval.set(v8::Boolean::new(scope, ok).into());
429
452
  }
430
453
 
454
+ fn native_load_env(scope: &mut v8::HandleScope, _args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
455
+ use serde_json::json;
456
+
457
+ let mut map = serde_json::Map::new();
458
+
459
+ for (key, value) in std::env::vars() {
460
+ map.insert(key, json!(value));
461
+ }
462
+
463
+ let json_str = serde_json::to_string(&map).unwrap();
464
+ let v8_str = v8::String::new(scope, &json_str).unwrap();
465
+
466
+ if let Some(obj) = v8::json::parse(scope, v8_str) {
467
+ retval.set(obj);
468
+ } else {
469
+ retval.set(v8::null(scope).into());
470
+ }
471
+ }
472
+
431
473
  fn native_define_action(_scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
432
474
  retval.set(args.get(0));
433
475
  }
476
+
477
+ fn native_db_connect(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
478
+ let conn_string = v8_to_string(scope, args.get(0));
479
+
480
+ if conn_string.is_empty() {
481
+ throw(scope, "t.db.connect(): connection string is required");
482
+ return;
483
+ }
484
+
485
+ // Test connection immediately
486
+ match PgClient::connect(&conn_string, NoTls) {
487
+ Ok(mut client) => {
488
+ // Store in pool
489
+ let mut pool = DB_POOL.lock().unwrap();
490
+ let map = pool.get_or_insert_with(HashMap::new);
491
+ map.insert(conn_string.clone(), client);
492
+ },
493
+ Err(e) => {
494
+ throw(scope, &format!("Database connection failed: {}", e));
495
+ return;
496
+ }
497
+ }
498
+
499
+ // Return a DB connection object with methods
500
+ let db_conn_obj = v8::Object::new(scope);
501
+
502
+ // Store connection string in a hidden property
503
+ let conn_key = v8_str(scope, "__conn_string");
504
+ let conn_val = v8_str(scope, &conn_string);
505
+ db_conn_obj.set(scope, conn_key.into(), conn_val.into());
506
+
507
+ // Add query method
508
+ let query_fn = v8::Function::new(scope, native_db_query).unwrap();
509
+ let query_key = v8_str(scope, "query");
510
+ db_conn_obj.set(scope, query_key.into(), query_fn.into());
511
+
512
+ retval.set(db_conn_obj.into());
513
+ }
514
+
515
+ fn native_db_query(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
516
+ // Get 'this' context (the db connection object)
517
+ let this = args.this();
518
+ let this_obj = this.to_object(scope).unwrap();
519
+
520
+ // Retrieve connection string
521
+ let conn_key = v8_str(scope, "__conn_string");
522
+ let conn_val = this_obj.get(scope, conn_key.into()).unwrap();
523
+ let conn_string = v8_to_string(scope, conn_val);
524
+
525
+ // Get query string
526
+ let query = v8_to_string(scope, args.get(0));
527
+
528
+ if query.is_empty() {
529
+ throw(scope, "db.query(): SQL query is required");
530
+ return;
531
+ }
532
+
533
+ // Get connection from pool
534
+ let mut pool = DB_POOL.lock().unwrap();
535
+ let map = pool.as_mut().unwrap();
536
+
537
+ let client = match map.get_mut(&conn_string) {
538
+ Some(c) => c,
539
+ None => {
540
+ throw(scope, "Database connection not found in pool");
541
+ return;
542
+ }
543
+ };
544
+
545
+ // Execute query
546
+ match client.query(&query, &[]) {
547
+ Ok(rows) => {
548
+ let mut result = Vec::new();
549
+
550
+ for row in rows {
551
+ let mut obj = serde_json::Map::new();
552
+
553
+ for (i, column) in row.columns().iter().enumerate() {
554
+ let col_name = column.name();
555
+ let col_value: serde_json::Value = if let Ok(val) = row.try_get::<_, Option<String>>(i) {
556
+ serde_json::json!(val)
557
+ } else if let Ok(val) = row.try_get::<_, Option<i32>>(i) {
558
+ serde_json::json!(val)
559
+ } else if let Ok(val) = row.try_get::<_, Option<i64>>(i) {
560
+ serde_json::json!(val)
561
+ } else if let Ok(val) = row.try_get::<_, Option<f64>>(i) {
562
+ serde_json::json!(val)
563
+ } else if let Ok(val) = row.try_get::<_, Option<bool>>(i) {
564
+ serde_json::json!(val)
565
+ } else {
566
+ serde_json::Value::Null
567
+ };
568
+
569
+ obj.insert(col_name.to_string(), col_value);
570
+ }
571
+
572
+ result.push(serde_json::Value::Object(obj));
573
+ }
574
+
575
+ let json_str = serde_json::to_string(&result).unwrap();
576
+ let v8_json_str = v8_str(scope, &json_str);
577
+
578
+ if let Some(val) = v8::json::parse(scope, v8_json_str) {
579
+ retval.set(val);
580
+ } else {
581
+ retval.set(v8::Array::new(scope, 0).into());
582
+ }
583
+ },
584
+ Err(e) => {
585
+ throw(scope, &format!("Query failed: {}", e));
586
+ }
587
+ }
588
+ }
@@ -132,11 +132,6 @@ pub fn inject_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Obje
132
132
  // Call individual injectors
133
133
  builtin::inject_builtin_extensions(scope, global, t_obj);
134
134
  external::inject_external_extensions(scope, global, t_obj);
135
-
136
- // Inject t.db (Stub)
137
- let db_obj = v8::Object::new(scope);
138
- let db_key = v8_str(scope, "db");
139
- t_obj.set(scope, db_key.into(), db_obj.into());
140
135
 
141
136
  global.set(scope, t_key.into(), t_obj.into());
142
137
  }
@@ -14,4 +14,9 @@ globalThis.TextDecoder = class TextDecoder {
14
14
  }
15
15
  };
16
16
 
17
+ // Process environment variables
18
+ globalThis.process = {
19
+ env: t.loadEnv()
20
+ };
21
+
17
22
  // Everything is strictly synchronous and request-driven.