titanpl 4.0.2 → 7.0.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +11 -5
  2. package/packages/cli/index.js +25 -11
  3. package/packages/cli/package.json +5 -5
  4. package/packages/cli/src/commands/build-ext.js +157 -0
  5. package/packages/cli/src/commands/build.js +12 -0
  6. package/packages/cli/src/commands/create.js +160 -0
  7. package/packages/cli/src/commands/init.js +5 -11
  8. package/packages/cli/src/commands/run-ext.js +104 -0
  9. package/packages/engine-darwin-arm64/README.md +0 -2
  10. package/packages/engine-darwin-arm64/package.json +1 -1
  11. package/packages/engine-linux-x64/README.md +0 -2
  12. package/packages/engine-linux-x64/package.json +1 -1
  13. package/packages/engine-win32-x64/README.md +0 -1
  14. package/packages/engine-win32-x64/bin/titan-server.exe +0 -0
  15. package/packages/engine-win32-x64/package.json +1 -1
  16. package/packages/native/README.md +0 -1
  17. package/packages/native/index.d.ts +10 -0
  18. package/packages/native/index.js +4 -0
  19. package/packages/native/package.json +1 -1
  20. package/packages/native/t.native.d.ts +175 -44
  21. package/packages/packet/README.md +0 -1
  22. package/packages/packet/index.js +19 -2
  23. package/packages/packet/package.json +1 -1
  24. package/packages/route/README.md +21 -0
  25. package/packages/route/index.d.ts +1 -0
  26. package/packages/route/index.js +22 -0
  27. package/packages/route/package.json +1 -1
  28. package/packages/sdk/index.js +2 -0
  29. package/packages/sdk/package.json +18 -0
  30. package/packages/sdk/test/index.js +120 -0
  31. package/templates/common/Dockerfile +9 -45
  32. package/templates/common/_tanfig.json +17 -13
  33. package/templates/extension/index.d.ts +26 -22
  34. package/templates/extension/index.js +15 -15
  35. package/templates/extension/native/Cargo.toml +5 -3
  36. package/templates/extension/native/src/lib.rs +2 -3
  37. package/templates/extension/package.json +10 -20
  38. package/templates/extension/titan.json +5 -16
  39. package/templates/extension/utils/registerExtension.js +44 -0
  40. package/templates/js/package.json +8 -8
  41. package/templates/rust-js/package.json +4 -4
  42. package/templates/rust-ts/package.json +4 -4
  43. package/templates/ts/package.json +8 -8
  44. package/templates/common/app/t.native.d.ts +0 -2043
  45. package/templates/common/app/t.native.js +0 -39
  46. package/titanpl-sdk/LICENSE +0 -15
  47. package/titanpl-sdk/README.md +0 -111
  48. package/titanpl-sdk/assets/titanpl-sdk.png +0 -0
  49. package/titanpl-sdk/bin/run.js +0 -274
  50. package/titanpl-sdk/index.js +0 -5
  51. package/titanpl-sdk/package-lock.json +0 -28
  52. package/titanpl-sdk/package.json +0 -40
  53. package/titanpl-sdk/templates/app/actions/hello.js +0 -5
  54. package/titanpl-sdk/templates/app/app.js +0 -7
  55. package/titanpl-sdk/templates/jsconfig.json +0 -19
  56. package/titanpl-sdk/templates/server/Cargo.toml +0 -52
  57. package/titanpl-sdk/templates/server/src/action_management.rs +0 -175
  58. package/titanpl-sdk/templates/server/src/errors.rs +0 -12
  59. package/titanpl-sdk/templates/server/src/extensions/builtin.rs +0 -1055
  60. package/titanpl-sdk/templates/server/src/extensions/external.rs +0 -338
  61. package/titanpl-sdk/templates/server/src/extensions/mod.rs +0 -580
  62. package/titanpl-sdk/templates/server/src/extensions/titan_core.js +0 -249
  63. package/titanpl-sdk/templates/server/src/fast_path.rs +0 -719
  64. package/titanpl-sdk/templates/server/src/main.rs +0 -607
  65. package/titanpl-sdk/templates/server/src/runtime.rs +0 -284
  66. package/titanpl-sdk/templates/server/src/utils.rs +0 -33
  67. package/titanpl-sdk/templates/titan/bundle.js +0 -259
  68. package/titanpl-sdk/templates/titan/dev.js +0 -390
  69. package/titanpl-sdk/templates/titan/error-box.js +0 -277
  70. package/titanpl-sdk/templates/titan/titan.js +0 -129
@@ -1,338 +0,0 @@
1
- //! External native extension loading and FFI.
2
- //!
3
- //! Supports loading `.dll` / `.so` extensions defined in `titan.json` files.
4
-
5
- use v8;
6
- use std::path::PathBuf;
7
- use std::collections::HashMap;
8
- use std::fs;
9
- use std::sync::{Mutex, Arc};
10
- use walkdir::WalkDir;
11
- use libloading::Library;
12
- use crate::utils::{blue, green, red};
13
- use super::{TitanRuntime, v8_str, throw};
14
- use serde_json::Value;
15
-
16
- pub static REGISTRY: Mutex<Option<Registry>> = Mutex::new(None);
17
-
18
- #[allow(dead_code)]
19
- pub struct Registry {
20
- pub _libs: Vec<Library>,
21
- pub modules: Vec<ModuleDef>,
22
- pub natives: Vec<NativeFnEntry>,
23
- }
24
-
25
- #[derive(Clone)]
26
- pub struct ModuleDef {
27
- pub name: String,
28
- pub js: String,
29
- pub native_indices: HashMap<String, usize>,
30
- }
31
-
32
- #[derive(Clone, Debug, PartialEq)]
33
- pub enum ParamType {
34
- String, F64, Bool, Json, Buffer,
35
- }
36
-
37
- #[derive(Clone, Debug, PartialEq)]
38
- pub enum ReturnType {
39
- String, F64, Bool, Json, Buffer, Void,
40
- }
41
-
42
- #[derive(Clone, Debug)]
43
- pub struct Signature {
44
- pub params: Vec<ParamType>,
45
- pub ret: ReturnType,
46
- }
47
-
48
- pub struct NativeFnEntry {
49
- pub symbol_ptr: usize,
50
- pub sig: Signature,
51
- }
52
-
53
- #[derive(serde::Deserialize)]
54
- struct TitanConfig {
55
- name: String,
56
- main: String,
57
- native: Option<TitanNativeConfig>,
58
- }
59
-
60
- #[derive(serde::Deserialize)]
61
- struct TitanNativeConfig {
62
- path: String,
63
- functions: HashMap<String, TitanNativeFunc>,
64
- }
65
-
66
- #[derive(serde::Deserialize)]
67
- struct TitanNativeFunc {
68
- symbol: String,
69
- #[serde(default)]
70
- parameters: Vec<String>,
71
- #[serde(default)]
72
- result: String,
73
- }
74
-
75
- fn parse_type(s: &str) -> ParamType {
76
- match s {
77
- "string" => ParamType::String,
78
- "f64" => ParamType::F64,
79
- "bool" => ParamType::Bool,
80
- "json" => ParamType::Json,
81
- "buffer" => ParamType::Buffer,
82
- _ => ParamType::Json,
83
- }
84
- }
85
-
86
- fn parse_return(s: &str) -> ReturnType {
87
- match s {
88
- "string" => ReturnType::String,
89
- "f64" => ReturnType::F64,
90
- "bool" => ReturnType::Bool,
91
- "json" => ReturnType::Json,
92
- "buffer" => ReturnType::Buffer,
93
- "void" => ReturnType::Void,
94
- _ => ReturnType::Void,
95
- }
96
- }
97
-
98
- pub fn load_project_extensions(root: PathBuf) {
99
- let mut modules = Vec::new();
100
- let mut libs = Vec::new();
101
- let mut all_natives = Vec::new();
102
-
103
- let mut node_modules = root.join("node_modules");
104
- if !node_modules.exists() {
105
- if let Some(parent) = root.parent() {
106
- let parent_modules = parent.join("node_modules");
107
- if parent_modules.exists() { node_modules = parent_modules; }
108
- }
109
- }
110
-
111
- // Generic scanner helper
112
- let scan_dir = |path: PathBuf, modules: &mut Vec<ModuleDef>, libs: &mut Vec<Library>, all_natives: &mut Vec<NativeFnEntry>| {
113
- if !path.exists() { return; }
114
- for entry in WalkDir::new(&path).follow_links(true).min_depth(1).max_depth(4) {
115
- let entry = match entry { Ok(e) => e, Err(_) => continue };
116
- if entry.file_type().is_file() && entry.file_name() == "titan.json" {
117
- let dir = entry.path().parent().unwrap();
118
- let config_content = fs::read_to_string(entry.path()).unwrap_or_default();
119
- let config: TitanConfig = match serde_json::from_str(&config_content) {
120
- Ok(c) => c,
121
- Err(_) => continue,
122
- };
123
- let mut mod_natives_map = HashMap::new();
124
- if let Some(native_conf) = config.native {
125
- let lib_path = dir.join(&native_conf.path);
126
- unsafe {
127
- // Try loading library
128
- let lib_load = Library::new(&lib_path);
129
- // If failed, try resolving relative to current dir or LD_LIBRARY_PATH implicit
130
- // But usually absolute path from `dir` works.
131
- match lib_load {
132
- Ok(lib) => {
133
- for (fn_name, fn_conf) in native_conf.functions {
134
- let params = fn_conf.parameters.iter().map(|p| parse_type(&p.to_lowercase())).collect();
135
- let ret = parse_return(&fn_conf.result.to_lowercase());
136
- if let Ok(symbol) = lib.get::<*const ()>(fn_conf.symbol.as_bytes()) {
137
- let idx = all_natives.len();
138
- all_natives.push(NativeFnEntry { symbol_ptr: *symbol as usize, sig: Signature { params, ret } });
139
- mod_natives_map.insert(fn_name, idx);
140
- } else {
141
- println!("{} {} {} -> {}", blue("[Titan]"), red("Symbol not found:"), fn_conf.symbol, config.name);
142
- }
143
- }
144
- libs.push(lib);
145
- },
146
- Err(e) => {
147
- println!("{} {} {} -> {:?}", blue("[Titan]"), red("Failed to load native lib:"), config.name, e);
148
- }
149
- }
150
- }
151
- }
152
- let js_path = dir.join(&config.main);
153
- modules.push(ModuleDef { name: config.name.clone(), js: fs::read_to_string(js_path).unwrap_or_default(), native_indices: mod_natives_map });
154
- println!("{} {} {}", blue("[Titan]"), green("Extension loaded:"), config.name);
155
- }
156
- }
157
- };
158
-
159
- // Scan node_modules
160
- if node_modules.exists() {
161
- scan_dir(node_modules, &mut modules, &mut libs, &mut all_natives);
162
- }
163
-
164
- // Scan .ext (Production / Docker)
165
- let ext_dir = root.join(".ext");
166
- if ext_dir.exists() {
167
- scan_dir(ext_dir, &mut modules, &mut libs, &mut all_natives);
168
- }
169
-
170
- *REGISTRY.lock().unwrap() = Some(Registry { _libs: libs, modules, natives: all_natives });
171
- }
172
-
173
- pub fn inject_external_extensions(scope: &mut v8::HandleScope, global: v8::Local<v8::Object>, t_obj: v8::Local<v8::Object>) {
174
- let invoke_fn = v8::Function::new(scope, native_invoke_extension).unwrap();
175
- let invoke_key = v8_str(scope, "__titan_invoke_native");
176
- global.set(scope, invoke_key.into(), invoke_fn.into());
177
-
178
- let modules = if let Ok(guard) = REGISTRY.lock() {
179
- guard.as_ref().map(|r| r.modules.clone()).unwrap_or_default()
180
- } else { vec![] };
181
-
182
- for module in modules {
183
- let mod_obj = v8::Object::new(scope);
184
- for (fn_name, &idx) in &module.native_indices {
185
- let code = format!("(function(...args) {{ return __titan_invoke_native({}, args); }})", idx);
186
- let code_str = v8_str(scope, &code);
187
- if let Some(script) = v8::Script::compile(scope, code_str, None) {
188
- if let Some(val) = script.run(scope) {
189
- let key = v8_str(scope, fn_name);
190
- mod_obj.set(scope, key.into(), val);
191
- }
192
- }
193
- }
194
- let mod_key = v8_str(scope, &module.name);
195
- t_obj.set(scope, mod_key.into(), mod_obj.into());
196
-
197
- let act_key = v8_str(scope, "__titan_action");
198
- let act_val = v8_str(scope, &module.name);
199
- global.set(scope, act_key.into(), act_val.into());
200
-
201
- let wrapped_js = format!("(function(t) {{ {} }})", module.js);
202
- let wrapped_js_str = v8_str(scope, &wrapped_js);
203
- let tc = &mut v8::TryCatch::new(scope);
204
- if let Some(script) = v8::Script::compile(tc, wrapped_js_str, None) {
205
- if let Some(func_val) = script.run(tc) {
206
- if let Ok(func) = v8::Local::<v8::Function>::try_from(func_val) {
207
- let receiver = v8::undefined(&mut *tc).into();
208
- let args = [t_obj.into()];
209
- func.call(&mut *tc, receiver, &args);
210
- }
211
- }
212
- }
213
- }
214
- }
215
-
216
- fn native_invoke_extension(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut retval: v8::ReturnValue) {
217
- let fn_idx = args.get(0).to_integer(scope).unwrap().value() as usize;
218
- let js_args_val = args.get(1);
219
- let (ptr, sig) = if let Ok(guard) = REGISTRY.lock() {
220
- if let Some(entry) = guard.as_ref().and_then(|r| r.natives.get(fn_idx)) {
221
- (entry.symbol_ptr, entry.sig.clone())
222
- } else { return; }
223
- } else { return; };
224
-
225
- if ptr == 0 { throw(scope, "Native function not found"); return; }
226
-
227
- let js_args = if js_args_val.is_array() {
228
- v8::Local::<v8::Array>::try_from(js_args_val).unwrap()
229
- } else { v8::Array::new(scope, 0) };
230
-
231
- let argc = sig.params.len();
232
- unsafe {
233
- let mut vals = Vec::new();
234
- for (i, param) in sig.params.iter().enumerate() {
235
- let val = js_args.get_index(scope, i as u32).unwrap_or_else(|| v8::undefined(scope).into());
236
- vals.push(arg_from_v8(scope, val, param));
237
- }
238
-
239
- let res_val: serde_json::Value = match argc {
240
- 0 => { dispatch_ret!(ptr, sig.ret, (), ()) },
241
- 1 => {
242
- let v0 = vals.remove(0);
243
- match sig.params[0] {
244
- ParamType::String => {
245
- let c = std::ffi::CString::new(v0.as_str().unwrap_or("")).unwrap();
246
- dispatch_ret!(ptr, sig.ret, (*const std::os::raw::c_char), (c.as_ptr()))
247
- },
248
- ParamType::F64 => { dispatch_ret!(ptr, sig.ret, (f64), (v0.as_f64().unwrap_or(0.0))) },
249
- ParamType::Bool => { dispatch_ret!(ptr, sig.ret, (bool), (v0.as_bool().unwrap_or(false))) },
250
- ParamType::Json => {
251
- let c = std::ffi::CString::new(v0.to_string()).unwrap();
252
- dispatch_ret!(ptr, sig.ret, (*const std::os::raw::c_char), (c.as_ptr()))
253
- },
254
- ParamType::Buffer => {
255
- let a0: Vec<u8> = v0.as_array().map(|a| a.iter().map(|v| v.as_u64().unwrap_or(0) as u8).collect()).unwrap_or_default();
256
- dispatch_ret!(ptr, sig.ret, (Vec<u8>), (a0))
257
- },
258
- }
259
- },
260
- 2 => {
261
- let v0 = vals.remove(0); let v1 = vals.remove(0);
262
- match (sig.params[0].clone(), sig.params[1].clone()) {
263
- (ParamType::String, ParamType::String) => {
264
- let c0 = std::ffi::CString::new(v0.as_str().unwrap_or("")).unwrap();
265
- let c1 = std::ffi::CString::new(v1.as_str().unwrap_or("")).unwrap();
266
- dispatch_ret!(ptr, sig.ret, (*const std::os::raw::c_char, *const std::os::raw::c_char), (c0.as_ptr(), c1.as_ptr()))
267
- },
268
- (ParamType::String, ParamType::F64) => {
269
- let c0 = std::ffi::CString::new(v0.as_str().unwrap_or("")).unwrap();
270
- dispatch_ret!(ptr, sig.ret, (*const std::os::raw::c_char, f64), (c0.as_ptr(), v1.as_f64().unwrap_or(0.0)))
271
- },
272
- _ => serde_json::Value::Null
273
- }
274
- },
275
- _ => serde_json::Value::Null
276
- };
277
- retval.set(js_from_value(scope, &sig.ret, res_val));
278
- }
279
- }
280
-
281
- fn arg_from_v8(scope: &mut v8::HandleScope, val: v8::Local<v8::Value>, ty: &ParamType) -> serde_json::Value {
282
- match ty {
283
- ParamType::String => serde_json::Value::String(val.to_rust_string_lossy(scope)),
284
- ParamType::F64 => serde_json::json!(val.to_number(scope).map(|n| n.value()).unwrap_or(0.0)),
285
- ParamType::Bool => serde_json::json!(val.boolean_value(scope)),
286
- ParamType::Json => {
287
- v8::json::stringify(scope, val).map(|s| serde_json::from_str(&s.to_rust_string_lossy(scope)).unwrap_or(Value::Null)).unwrap_or(Value::Null)
288
- },
289
- ParamType::Buffer => {
290
- if let Ok(u8arr) = v8::Local::<v8::Uint8Array>::try_from(val) {
291
- let store = v8::ArrayBuffer::get_backing_store(&u8arr.buffer(scope).unwrap());
292
- let offset = usize::from(u8arr.byte_offset());
293
- let length = usize::from(u8arr.byte_length());
294
- let vec_u8: Vec<Value> = store[offset..offset+length].iter().map(|b| Value::from(b.get() as u64)).collect();
295
- Value::Array(vec_u8)
296
- } else { Value::Array(vec![]) }
297
- }
298
- }
299
- }
300
-
301
- fn js_from_value<'a>(scope: &mut v8::HandleScope<'a>, ret_type: &ReturnType, val: serde_json::Value) -> v8::Local<'a, v8::Value> {
302
- match ret_type {
303
- ReturnType::String => v8_str(scope, val.as_str().unwrap_or("")).into(),
304
- ReturnType::F64 => v8::Number::new(scope, val.as_f64().unwrap_or(0.0)).into(),
305
- ReturnType::Bool => v8::Boolean::new(scope, val.as_bool().unwrap_or(false)).into(),
306
- ReturnType::Json => {
307
- let s = v8_str(scope, &val.to_string());
308
- v8::json::parse(scope, s).unwrap_or_else(|| v8::null(scope).into())
309
- },
310
- ReturnType::Buffer => v8::undefined(scope).into(),
311
- ReturnType::Void => v8::undefined(scope).into(),
312
- }
313
- }
314
-
315
- macro_rules! dispatch_ret {
316
- ($ptr:expr, $ret:expr, ($($arg_ty:ty),*), ($($arg:expr),*)) => {
317
- match $ret {
318
- ReturnType::String => {
319
- let f: extern "C" fn($($arg_ty),*) -> *mut std::os::raw::c_char = unsafe { std::mem::transmute($ptr) };
320
- let ptr = f($($arg),*);
321
- if ptr.is_null() { Value::String(String::new()) } else { Value::String(unsafe { std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned() }) }
322
- },
323
- ReturnType::F64 => { let f: extern "C" fn($($arg_ty),*) -> f64 = unsafe { std::mem::transmute($ptr) }; serde_json::json!(f($($arg),*)) },
324
- ReturnType::Bool => { let f: extern "C" fn($($arg_ty),*) -> bool = unsafe { std::mem::transmute($ptr) }; serde_json::json!(f($($arg),*)) },
325
- ReturnType::Json => {
326
- let f: extern "C" fn($($arg_ty),*) -> *mut std::os::raw::c_char = unsafe { std::mem::transmute($ptr) };
327
- let ptr = f($($arg),*);
328
- if ptr.is_null() { Value::Null } else { serde_json::from_str(&unsafe { std::ffi::CStr::from_ptr(ptr).to_string_lossy() }).unwrap_or(Value::Null) }
329
- },
330
- ReturnType::Buffer => {
331
- let f: extern "C" fn($($arg_ty),*) -> Vec<u8> = unsafe { std::mem::transmute($ptr) };
332
- Value::Array(f($($arg),*).into_iter().map(Value::from).collect())
333
- },
334
- ReturnType::Void => { let f: extern "C" fn($($arg_ty),*) = unsafe { std::mem::transmute($ptr) }; f($($arg),*); Value::Null },
335
- }
336
- }
337
- }
338
- pub(crate) use dispatch_ret;