@ezetgalaxy/titan 26.3.2 → 26.3.3

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.3.2",
3
+ "version": "26.3.3",
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",
@@ -44,6 +44,9 @@ COPY --from=builder /app/server/action_map.json ./action_map.json
44
44
  RUN mkdir -p /app/actions
45
45
  COPY --from=builder /app/server/actions /app/actions
46
46
 
47
+ # Copy static assets for t.read
48
+ COPY app/db /app/assets/db
49
+
47
50
  # Expose Titan port
48
51
  EXPOSE 3000
49
52
 
@@ -1,52 +1,102 @@
1
+ use bcrypt::{DEFAULT_COST, hash, verify};
1
2
  use boa_engine::{
2
- js_string, native_function::NativeFunction, object::ObjectInitializer, property::Attribute,
3
- Context, JsError, JsValue,
3
+ Context, JsError, JsValue, js_string, native_function::NativeFunction,
4
+ object::ObjectInitializer, property::Attribute,
4
5
  };
6
+ use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation, decode, encode};
7
+ use postgres::types::{ToSql, Type};
8
+ use postgres::{Client as PgClient, NoTls};
5
9
  use reqwest::{
6
10
  blocking::Client,
7
11
  header::{HeaderMap, HeaderName, HeaderValue},
8
12
  };
9
13
  use serde_json::Value;
14
+ use std::path::{Path, PathBuf};
10
15
  use std::time::{SystemTime, UNIX_EPOCH};
11
16
  use tokio::task;
12
- use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
13
- use bcrypt::{hash, verify, DEFAULT_COST};
14
- use postgres::{Client as PgClient, NoTls};
15
- use postgres::types::{ToSql, Type};
16
- use std::path::PathBuf;
17
17
 
18
18
  use crate::utils::{blue, gray, parse_expires_in};
19
19
 
20
+
21
+
20
22
  /// Here add all the runtime t base things
21
23
  /// Injects a synchronous `t.fetch(url, opts?)` function into the Boa `Context`.
22
24
  pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &PathBuf) {
23
-
24
25
  // =========================================================
25
26
  // t.read(path)
26
27
  // =========================================================
27
- let root = project_root.clone();
28
- let t_read_native = unsafe { NativeFunction::from_closure(move |_this, args, ctx| {
29
- let path_str = args.get(0)
30
- .and_then(|v| v.to_string(ctx).ok())
31
- .map(|s| s.to_std_string_escaped())
32
- .ok_or_else(|| {
28
+ fn resolve_read_root(project_root: &PathBuf) -> PathBuf {
29
+ if cfg!(debug_assertions) {
30
+ project_root.join("app")
31
+ } else {
32
+ project_root.join("assets")
33
+ }
34
+ }
35
+
36
+ let root = resolve_read_root(project_root).canonicalize().expect("read root must exist");
37
+
38
+ let t_read_native = unsafe {
39
+ NativeFunction::from_closure(move |_this, args, ctx| {
40
+ // ----------------------------------
41
+ // 1. Read argument
42
+ // ----------------------------------
43
+ let rel = args
44
+ .get(0)
45
+ .and_then(|v| v.to_string(ctx).ok())
46
+ .and_then(|s| s.to_std_string().ok())
47
+ .ok_or_else(|| {
48
+ JsError::from_native(
49
+ boa_engine::JsNativeError::typ()
50
+ .with_message("t.read(path): path is required"),
51
+ )
52
+ })?;
53
+
54
+ if Path::new(&rel).is_absolute() {
55
+ return Err(JsError::from_native(
56
+ boa_engine::JsNativeError::typ()
57
+ .with_message("t.read expects a relative path like 'db/file.sql'"),
58
+ ));
59
+ }
60
+
61
+
62
+
63
+
64
+
65
+ let joined = root.join(&rel);
66
+
67
+ // ----------------------------------
68
+ // 3. Canonicalize (resolves ../)
69
+ // ----------------------------------
70
+ let target = joined.canonicalize().map_err(|_| {
33
71
  JsError::from_native(
34
- boa_engine::JsNativeError::typ().with_message("t.read(path): path is required")
72
+ boa_engine::JsNativeError::error()
73
+ .with_message(format!("t.read: file not found: {}", rel)),
35
74
  )
36
75
  })?;
37
-
38
- let target_path = root.join(path_str);
39
-
40
- // Security check? For now assuming trusted code.
41
- // We should arguably check if it's inside project_root, but dev tool.
42
-
43
- let content = std::fs::read_to_string(target_path)
44
- .map_err(|e| JsError::from_native(
45
- boa_engine::JsNativeError::error().with_message(format!("Failed to read file: {}", e))
46
- ))?;
47
-
48
- Ok(JsValue::from(js_string!(content)))
49
- })};
76
+
77
+ // ----------------------------------
78
+ // 4. Enforce root boundary
79
+ // ----------------------------------
80
+ if !target.starts_with(&root) {
81
+ return Err(JsError::from_native(
82
+ boa_engine::JsNativeError::error()
83
+ .with_message("t.read: path escapes allowed root"),
84
+ ));
85
+ }
86
+
87
+ // ----------------------------------
88
+ // 5. Read file
89
+ // ----------------------------------
90
+ let content = std::fs::read_to_string(&target).map_err(|e| {
91
+ JsError::from_native(
92
+ boa_engine::JsNativeError::error()
93
+ .with_message(format!("t.read failed: {}", e)),
94
+ )
95
+ })?;
96
+
97
+ Ok(JsValue::from(js_string!(content)))
98
+ })
99
+ };
50
100
 
51
101
  // =========================================================
52
102
  // t.log(...) — unsafe by design (Boa requirement)
@@ -64,7 +114,11 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
64
114
  println!(
65
115
  "{} {}",
66
116
  blue("[Titan]"),
67
- gray(&format!("\x1b[90mlog({})\x1b[0m\x1b[97m: {}\x1b[0m", action, parts.join(" ")))
117
+ gray(&format!(
118
+ "\x1b[90mlog({})\x1b[0m\x1b[97m: {}\x1b[0m",
119
+ action,
120
+ parts.join(" ")
121
+ ))
68
122
  );
69
123
 
70
124
  Ok(JsValue::undefined())
@@ -104,14 +158,13 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
104
158
  .unwrap_or("GET")
105
159
  .to_string();
106
160
 
107
- let body_opt = opts_json.get("body").map(|v| {
108
- if v.is_string() {
109
- v.as_str().unwrap().to_string()
110
- } else {
111
- serde_json::to_string(v).unwrap_or_default()
112
- }
113
- });
114
-
161
+ let body_opt = opts_json.get("body").map(|v| {
162
+ if v.is_string() {
163
+ v.as_str().unwrap().to_string()
164
+ } else {
165
+ serde_json::to_string(v).unwrap_or_default()
166
+ }
167
+ });
115
168
 
116
169
  let mut header_pairs = Vec::new();
117
170
  if let Some(Value::Object(map)) = opts_json.get("headers") {
@@ -127,11 +180,10 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
127
180
  // -----------------------------
128
181
  let out_json = task::block_in_place(move || {
129
182
  let client = Client::builder()
130
- .use_rustls_tls()
131
- .tcp_nodelay(true)
132
- .build()
133
- .unwrap();
134
-
183
+ .use_rustls_tls()
184
+ .tcp_nodelay(true)
185
+ .build()
186
+ .unwrap();
135
187
 
136
188
  let mut req = client.request(method.parse().unwrap_or(reqwest::Method::GET), &url);
137
189
 
@@ -181,7 +233,8 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
181
233
  // =========================================================
182
234
  let t_jwt_sign = NativeFunction::from_fn_ptr(|_this, args, ctx| {
183
235
  // payload (must be object)
184
- let mut payload = args.get(0)
236
+ let mut payload = args
237
+ .get(0)
185
238
  .and_then(|v| v.to_json(ctx).ok())
186
239
  .and_then(|v| v.as_object().cloned())
187
240
  .ok_or_else(|| {
@@ -190,13 +243,13 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
190
243
  .with_message("t.jwt.sign(payload, secret[, options])"),
191
244
  )
192
245
  })?;
193
-
246
+
194
247
  // secret
195
- let secret = args.get(1)
248
+ let secret = args
249
+ .get(1)
196
250
  .and_then(|v| v.to_string(ctx).ok())
197
251
  .map(|s| s.to_std_string_escaped())
198
252
  .unwrap_or_default();
199
-
200
253
 
201
254
  if let Some(opts) = args.get(2) {
202
255
  if let Ok(Value::Object(opts)) = opts.to_json(ctx) {
@@ -204,23 +257,23 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
204
257
  let seconds = match exp {
205
258
  Value::Number(n) => n.as_u64(),
206
259
  Value::String(s) => parse_expires_in(s),
207
- _ => None,
260
+ _ => None,
208
261
  };
209
262
 
210
- if let Some(sec) = seconds {
211
- let now = SystemTime::now()
212
- .duration_since(UNIX_EPOCH)
213
- .unwrap()
214
- .as_secs();
263
+ if let Some(sec) = seconds {
264
+ let now = SystemTime::now()
265
+ .duration_since(UNIX_EPOCH)
266
+ .unwrap()
267
+ .as_secs();
215
268
 
216
- payload.insert(
217
- "exp".to_string(),
218
- Value::Number(serde_json::Number::from(now + sec)),
219
- );
269
+ payload.insert(
270
+ "exp".to_string(),
271
+ Value::Number(serde_json::Number::from(now + sec)),
272
+ );
273
+ }
274
+ }
220
275
  }
221
276
  }
222
- }
223
- }
224
277
 
225
278
  let token = encode(
226
279
  &Header::default(),
@@ -228,28 +281,28 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
228
281
  &EncodingKey::from_secret(secret.as_bytes()),
229
282
  )
230
283
  .map_err(|e| {
231
- JsError::from_native(
232
- boa_engine::JsNativeError::error().with_message(e.to_string()),
233
- )
284
+ JsError::from_native(boa_engine::JsNativeError::error().with_message(e.to_string()))
234
285
  })?;
235
-
286
+
236
287
  Ok(JsValue::from(js_string!(token)))
237
288
  });
238
-
289
+
239
290
  let t_jwt_verify = NativeFunction::from_fn_ptr(|_this, args, ctx| {
240
- let token = args.get(0)
291
+ let token = args
292
+ .get(0)
241
293
  .and_then(|v| v.to_string(ctx).ok())
242
294
  .map(|s| s.to_std_string_escaped())
243
295
  .unwrap_or_default();
244
-
245
- let secret = args.get(1)
296
+
297
+ let secret = args
298
+ .get(1)
246
299
  .and_then(|v| v.to_string(ctx).ok())
247
300
  .map(|s| s.to_std_string_escaped())
248
301
  .unwrap_or_default();
249
-
302
+
250
303
  let mut validation = Validation::default();
251
304
  validation.validate_exp = true;
252
-
305
+
253
306
  let data = decode::<Value>(
254
307
  &token,
255
308
  &DecodingKey::from_secret(secret.as_bytes()),
@@ -257,46 +310,45 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
257
310
  )
258
311
  .map_err(|_| {
259
312
  JsError::from_native(
260
- boa_engine::JsNativeError::error()
261
- .with_message("Invalid or expired JWT"),
313
+ boa_engine::JsNativeError::error().with_message("Invalid or expired JWT"),
262
314
  )
263
315
  })?;
264
-
316
+
265
317
  JsValue::from_json(&data.claims, ctx).map_err(|e| e.into())
266
318
  });
267
-
268
319
 
269
-
270
320
  // =========================================================
271
321
  // t.password
272
322
  // =========================================================
273
323
  let t_password_hash = NativeFunction::from_fn_ptr(|_this, args, ctx| {
274
- let password = args.get(0)
324
+ let password = args
325
+ .get(0)
275
326
  .and_then(|v| v.to_string(ctx).ok())
276
327
  .map(|s| s.to_std_string_escaped())
277
328
  .unwrap_or_default();
278
-
279
- let hashed = hash(password, DEFAULT_COST)
280
- .map_err(|e| JsError::from_native(
281
- boa_engine::JsNativeError::error().with_message(e.to_string())
282
- ))?;
283
-
284
- Ok(JsValue::from(js_string!(hashed)))
329
+
330
+ let hashed = hash(password, DEFAULT_COST).map_err(|e| {
331
+ JsError::from_native(boa_engine::JsNativeError::error().with_message(e.to_string()))
332
+ })?;
333
+
334
+ Ok(JsValue::from(js_string!(hashed)))
285
335
  });
286
336
 
287
337
  let t_password_verify = NativeFunction::from_fn_ptr(|_this, args, ctx| {
288
- let password = args.get(0)
338
+ let password = args
339
+ .get(0)
289
340
  .and_then(|v| v.to_string(ctx).ok())
290
341
  .map(|s| s.to_std_string_escaped())
291
342
  .unwrap_or_default();
292
-
293
- let hash_str = args.get(1)
343
+
344
+ let hash_str = args
345
+ .get(1)
294
346
  .and_then(|v| v.to_string(ctx).ok())
295
347
  .map(|s| s.to_std_string_escaped())
296
348
  .unwrap_or_default();
297
-
349
+
298
350
  let ok = verify(password, &hash_str).unwrap_or(false);
299
-
351
+
300
352
  Ok(JsValue::from(ok))
301
353
  });
302
354
 
@@ -304,7 +356,8 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
304
356
  // t.db (Synchronous Postgres)
305
357
  // =========================================================
306
358
  let t_db_connect = NativeFunction::from_fn_ptr(|_this, args, ctx| {
307
- let url = args.get(0)
359
+ let url = args
360
+ .get(0)
308
361
  .and_then(|v| v.to_string(ctx).ok())
309
362
  .map(|s| s.to_std_string_escaped())
310
363
  .ok_or_else(|| {
@@ -314,112 +367,119 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
314
367
  )
315
368
  })?;
316
369
 
317
-
318
-
319
370
  let url_clone = url.clone();
320
-
371
+
321
372
  let query_fn = unsafe {
322
373
  NativeFunction::from_closure(move |_this, args, ctx| {
323
- let sql = args.get(0)
374
+ let sql = args
375
+ .get(0)
324
376
  .and_then(|v| v.to_string(ctx).ok())
325
377
  .map(|s| s.to_std_string_escaped())
326
378
  .ok_or_else(|| {
327
379
  JsError::from_native(
328
- boa_engine::JsNativeError::typ().with_message("db.query(sql, params): sql is required")
380
+ boa_engine::JsNativeError::typ()
381
+ .with_message("db.query(sql, params): sql is required"),
329
382
  )
330
383
  })?;
331
384
 
332
-
333
385
  let params_val = args.get(1).cloned().unwrap_or(JsValue::Null);
334
-
335
-
386
+
336
387
  let json_params: Vec<Value> = if let Ok(val) = params_val.to_json(ctx) {
337
- if let Value::Array(arr) = val { arr } else { vec![] }
388
+ if let Value::Array(arr) = val {
389
+ arr
390
+ } else {
391
+ vec![]
392
+ }
338
393
  } else {
339
- vec![]
394
+ vec![]
340
395
  };
341
-
396
+
342
397
  let url_for_query = url_clone.clone();
343
398
 
344
399
  let result_json = task::block_in_place(move || {
345
- let mut client = match PgClient::connect(&url_for_query, NoTls) {
400
+ let mut client = match PgClient::connect(&url_for_query, NoTls) {
346
401
  Ok(c) => c,
347
402
  Err(e) => return Err(format!("Connection failed: {}", e)),
348
- };
349
-
350
- // We need to map `Vec<Value>` to `&[&dyn ToSql]`.
351
-
352
- let mut typed_params: Vec<Box<dyn ToSql + Sync>> = Vec::new();
353
-
354
- for p in json_params {
355
- match p {
356
- Value::String(s) => typed_params.push(Box::new(s)),
357
- Value::Number(n) => {
358
- if let Some(i) = n.as_i64() {
359
- typed_params.push(Box::new(i));
360
- } else if let Some(f) = n.as_f64() {
361
- typed_params.push(Box::new(f));
362
- }
363
- },
364
- Value::Bool(b) => typed_params.push(Box::new(b)),
365
- Value::Null => typed_params.push(Box::new(Option::<String>::None)), // Typed null?
366
- // Fallback others to JSON
367
- obj => typed_params.push(Box::new(obj)),
368
- }
369
- }
370
-
371
- let param_refs: Vec<&(dyn ToSql + Sync)> = typed_params
372
- .iter()
373
- .map(|x| x.as_ref())
374
- .collect();
375
-
376
- let rows = client.query(&sql, &param_refs).map_err(|e| e.to_string())?;
377
-
378
- // Convert rows to JSON
379
- let mut out_rows = Vec::new();
380
- for row in rows {
381
- let mut map = serde_json::Map::new();
382
- // We need column names.
383
- for (i, col) in row.columns().iter().enumerate() {
384
- let name = col.name().to_string();
385
-
386
- let val: Value = match *col.type_() {
387
- Type::BOOL => Value::Bool(row.get(i)),
388
- Type::INT2 | Type::INT4 | Type::INT8 => {
389
- let v: Option<i64> = row.get::<_, Option<i64>>(i);
390
- v.map(|n| Value::Number(n.into())).unwrap_or(Value::Null)
391
- },
392
- Type::FLOAT4 | Type::FLOAT8 => {
393
- let v: Option<f64> = row.get::<_, Option<f64>>(i);
394
- v.map(|n| serde_json::Number::from_f64(n).map(Value::Number).unwrap_or(Value::Null)).unwrap_or(Value::Null)
395
- },
396
- Type::TEXT | Type::VARCHAR | Type::BPCHAR | Type::NAME => {
397
- let v: Option<String> = row.get(i);
398
- v.map(Value::String).unwrap_or(Value::Null)
399
- },
400
- Type::JSON | Type::JSONB => {
401
- let v: Option<Value> = row.get(i);
402
- v.unwrap_or(Value::Null)
403
- },
404
- _ => Value::Null
405
- };
406
- map.insert(name, val);
407
- }
408
- out_rows.push(Value::Object(map));
409
- }
410
-
411
- Ok(out_rows)
403
+ };
404
+
405
+ // We need to map `Vec<Value>` to `&[&dyn ToSql]`.
406
+
407
+ let mut typed_params: Vec<Box<dyn ToSql + Sync>> = Vec::new();
408
+
409
+ for p in json_params {
410
+ match p {
411
+ Value::String(s) => typed_params.push(Box::new(s)),
412
+ Value::Number(n) => {
413
+ if let Some(i) = n.as_i64() {
414
+ typed_params.push(Box::new(i));
415
+ } else if let Some(f) = n.as_f64() {
416
+ typed_params.push(Box::new(f));
417
+ }
418
+ }
419
+ Value::Bool(b) => typed_params.push(Box::new(b)),
420
+ Value::Null => typed_params.push(Box::new(Option::<String>::None)), // Typed null?
421
+ // Fallback others to JSON
422
+ obj => typed_params.push(Box::new(obj)),
423
+ }
424
+ }
425
+
426
+ let param_refs: Vec<&(dyn ToSql + Sync)> =
427
+ typed_params.iter().map(|x| x.as_ref()).collect();
428
+
429
+ let rows = client.query(&sql, &param_refs).map_err(|e| e.to_string())?;
430
+
431
+ // Convert rows to JSON
432
+ let mut out_rows = Vec::new();
433
+ for row in rows {
434
+ let mut map = serde_json::Map::new();
435
+ // We need column names.
436
+ for (i, col) in row.columns().iter().enumerate() {
437
+ let name = col.name().to_string();
438
+
439
+ let val: Value = match *col.type_() {
440
+ Type::BOOL => Value::Bool(row.get(i)),
441
+ Type::INT2 | Type::INT4 | Type::INT8 => {
442
+ let v: Option<i64> = row.get::<_, Option<i64>>(i);
443
+ v.map(|n| Value::Number(n.into())).unwrap_or(Value::Null)
444
+ }
445
+ Type::FLOAT4 | Type::FLOAT8 => {
446
+ let v: Option<f64> = row.get::<_, Option<f64>>(i);
447
+ v.map(|n| {
448
+ serde_json::Number::from_f64(n)
449
+ .map(Value::Number)
450
+ .unwrap_or(Value::Null)
451
+ })
452
+ .unwrap_or(Value::Null)
453
+ }
454
+ Type::TEXT | Type::VARCHAR | Type::BPCHAR | Type::NAME => {
455
+ let v: Option<String> = row.get(i);
456
+ v.map(Value::String).unwrap_or(Value::Null)
457
+ }
458
+ Type::JSON | Type::JSONB => {
459
+ let v: Option<Value> = row.get(i);
460
+ v.unwrap_or(Value::Null)
461
+ }
462
+ _ => Value::Null,
463
+ };
464
+ map.insert(name, val);
465
+ }
466
+ out_rows.push(Value::Object(map));
467
+ }
468
+
469
+ Ok(out_rows)
412
470
  });
413
-
471
+
414
472
  match result_json {
415
473
  Ok(rows) => JsValue::from_json(&Value::Array(rows), ctx),
416
- Err(e) => Err(JsError::from_native(boa_engine::JsNativeError::error().with_message(e)))
474
+ Err(e) => Err(JsError::from_native(
475
+ boa_engine::JsNativeError::error().with_message(e),
476
+ )),
417
477
  }
418
478
  })
419
479
  };
420
-
480
+
421
481
  // Build object
422
-
482
+
423
483
  let realm = ctx.realm().clone(); // Fix context borrow
424
484
  let obj = ObjectInitializer::new(ctx)
425
485
  .property(
@@ -428,29 +488,48 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
428
488
  Attribute::all(),
429
489
  )
430
490
  .build();
431
-
491
+
432
492
  Ok(JsValue::from(obj))
433
493
  });
434
494
 
435
-
436
495
  // =========================================================
437
496
  // Build global `t`
438
497
  // =========================================================
439
498
  let realm = ctx.realm().clone();
440
499
 
441
500
  let jwt_obj = ObjectInitializer::new(ctx)
442
- .property(js_string!("sign"), t_jwt_sign.to_js_function(&realm), Attribute::all())
443
- .property(js_string!("verify"), t_jwt_verify.to_js_function(&realm), Attribute::all())
444
- .build();
501
+ .property(
502
+ js_string!("sign"),
503
+ t_jwt_sign.to_js_function(&realm),
504
+ Attribute::all(),
505
+ )
506
+ .property(
507
+ js_string!("verify"),
508
+ t_jwt_verify.to_js_function(&realm),
509
+ Attribute::all(),
510
+ )
511
+ .build();
445
512
 
446
513
  let password_obj = ObjectInitializer::new(ctx)
447
- .property(js_string!("hash"), t_password_hash.to_js_function(&realm), Attribute::all())
448
- .property(js_string!("verify"), t_password_verify.to_js_function(&realm), Attribute::all())
449
- .build();
450
-
514
+ .property(
515
+ js_string!("hash"),
516
+ t_password_hash.to_js_function(&realm),
517
+ Attribute::all(),
518
+ )
519
+ .property(
520
+ js_string!("verify"),
521
+ t_password_verify.to_js_function(&realm),
522
+ Attribute::all(),
523
+ )
524
+ .build();
525
+
451
526
  let db_obj = ObjectInitializer::new(ctx)
452
- .property(js_string!("connect"), t_db_connect.to_js_function(&realm), Attribute::all())
453
- .build();
527
+ .property(
528
+ js_string!("connect"),
529
+ t_db_connect.to_js_function(&realm),
530
+ Attribute::all(),
531
+ )
532
+ .build();
454
533
 
455
534
  let t_obj = ObjectInitializer::new(ctx)
456
535
  .property(
@@ -462,11 +541,11 @@ pub fn inject_t_runtime(ctx: &mut Context, action_name: &str, project_root: &Pat
462
541
  js_string!("fetch"),
463
542
  t_fetch_native.to_js_function(&realm),
464
543
  Attribute::all(),
465
- )
544
+ )
466
545
  .property(
467
546
  js_string!("read"),
468
547
  t_read_native.to_js_function(&realm),
469
- Attribute::all()
548
+ Attribute::all(),
470
549
  )
471
550
  .property(js_string!("jwt"), jwt_obj, Attribute::all())
472
551
  .property(js_string!("password"), password_obj, Attribute::all())