titanpl-sdk 3.0.0 → 6.0.0
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/README.md +2 -0
- package/package.json +38 -34
- package/templates/app/app.js +1 -4
- package/templates/server/Cargo.toml +2 -0
- package/templates/server/src/extensions/builtin.rs +271 -139
- package/templates/server/src/extensions/external.rs +338 -338
- package/templates/server/src/extensions/mod.rs +6 -6
- package/templates/server/src/extensions/titan_core.js +49 -13
- package/templates/server/src/main.rs +3 -3
- package/templates/titan/bundle.js +259 -264
- package/templates/titan/dev.js +46 -6
- package/templates/titan/error-box.js +277 -268
- package/templates/app/t.native.d.ts +0 -1983
- package/templates/app/t.native.js +0 -39
|
@@ -63,6 +63,7 @@ pub enum TitanAsyncOp {
|
|
|
63
63
|
DbQuery {
|
|
64
64
|
conn: String,
|
|
65
65
|
query: String,
|
|
66
|
+
params: Vec<String>,
|
|
66
67
|
},
|
|
67
68
|
FsRead {
|
|
68
69
|
path: String,
|
|
@@ -470,16 +471,15 @@ pub fn execute_action_optimized(
|
|
|
470
471
|
let p_val = v8_str(scope, req_path);
|
|
471
472
|
req_obj.set(scope, p_key.into(), p_val.into());
|
|
472
473
|
|
|
473
|
-
//
|
|
474
|
+
// body — attach raw bytes as ArrayBuffer under "rawBody" key
|
|
475
|
+
let rb_key = v8::Local::new(scope, &gk_raw_body);
|
|
474
476
|
let body_val: v8::Local<v8::Value> = if let Some(bytes) = req_body {
|
|
475
|
-
let
|
|
476
|
-
let
|
|
477
|
-
let ab = v8::ArrayBuffer::with_backing_store(scope, &store.make_shared());
|
|
477
|
+
let backing = v8::ArrayBuffer::new_backing_store_from_vec(bytes.to_vec());
|
|
478
|
+
let ab = v8::ArrayBuffer::with_backing_store(scope, &backing.make_shared());
|
|
478
479
|
ab.into()
|
|
479
480
|
} else {
|
|
480
481
|
v8::null(scope).into()
|
|
481
482
|
};
|
|
482
|
-
let rb_key = v8::Local::new(scope, &gk_raw_body);
|
|
483
483
|
req_obj.set(scope, rb_key.into(), body_val);
|
|
484
484
|
|
|
485
485
|
// headers
|
|
@@ -577,4 +577,4 @@ pub fn throw(scope: &mut v8::HandleScope, msg: &str) {
|
|
|
577
577
|
let message = v8_str(scope, msg);
|
|
578
578
|
let exception = v8::Exception::error(scope, message);
|
|
579
579
|
scope.throw_exception(exception);
|
|
580
|
-
}
|
|
580
|
+
}
|
|
@@ -15,6 +15,32 @@ if (!globalThis.__TITAN_CORE_LOADED__) {
|
|
|
15
15
|
const wrapped = function (req) {
|
|
16
16
|
const requestId = req.__titan_request_id;
|
|
17
17
|
|
|
18
|
+
if (req.rawBody && req.rawBody.byteLength !== undefined) {
|
|
19
|
+
try {
|
|
20
|
+
const decoder = new TextDecoder();
|
|
21
|
+
const text = decoder.decode(req.rawBody);
|
|
22
|
+
|
|
23
|
+
const contentType =
|
|
24
|
+
(req.headers && req.headers["content-type"]) ||
|
|
25
|
+
(req.headers && req.headers["Content-Type"]) ||
|
|
26
|
+
"";
|
|
27
|
+
|
|
28
|
+
if (contentType.includes("application/json")) {
|
|
29
|
+
req.body = text ? JSON.parse(text) : {};
|
|
30
|
+
} else if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
31
|
+
req.body = Object.fromEntries(new URLSearchParams(text));
|
|
32
|
+
} else {
|
|
33
|
+
req.body = text;
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
req.body = {};
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
req.body = {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ===============================
|
|
43
|
+
|
|
18
44
|
const isSuspend = (err) => {
|
|
19
45
|
const msg = err && (err.message || String(err));
|
|
20
46
|
return msg && (msg.includes("__SUSPEND__") || msg.includes("SUSPEND"));
|
|
@@ -25,9 +51,7 @@ if (!globalThis.__TITAN_CORE_LOADED__) {
|
|
|
25
51
|
|
|
26
52
|
if (result && typeof result.then === 'function') {
|
|
27
53
|
result.then(
|
|
28
|
-
(data) =>
|
|
29
|
-
t._finish_request(requestId, data);
|
|
30
|
-
},
|
|
54
|
+
(data) => t._finish_request(requestId, data),
|
|
31
55
|
(err) => {
|
|
32
56
|
if (isSuspend(err)) return;
|
|
33
57
|
t._finish_request(requestId, { error: err.message || String(err) });
|
|
@@ -54,10 +78,8 @@ if (!globalThis.__TITAN_CORE_LOADED__) {
|
|
|
54
78
|
}
|
|
55
79
|
};
|
|
56
80
|
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
env: t.loadEnv ? t.loadEnv() : {}
|
|
60
|
-
};
|
|
81
|
+
// Titan Environment API
|
|
82
|
+
t.env = t.loadEnv ? t.loadEnv() : {};
|
|
61
83
|
|
|
62
84
|
// Async Proxy Creator
|
|
63
85
|
function createAsyncOp(op) {
|
|
@@ -73,8 +95,7 @@ if (!globalThis.__TITAN_CORE_LOADED__) {
|
|
|
73
95
|
}
|
|
74
96
|
|
|
75
97
|
throw new Error(
|
|
76
|
-
`[Titan Error] Accessed '${String(prop)}' without drift(). `
|
|
77
|
-
`Fix: const res = drift(t.fetch(...));`
|
|
98
|
+
`[Titan Error] Accessed '${String(prop)}' without drift(). `
|
|
78
99
|
);
|
|
79
100
|
}
|
|
80
101
|
});
|
|
@@ -185,22 +206,37 @@ if (!globalThis.__TITAN_CORE_LOADED__) {
|
|
|
185
206
|
t.fetch.__titanWrapped = true;
|
|
186
207
|
}
|
|
187
208
|
|
|
209
|
+
// db.connect
|
|
188
210
|
// db.connect
|
|
189
211
|
if (t.db && !t.db.__titanWrapped) {
|
|
190
212
|
const nativeDbConnect = t.db.connect;
|
|
191
213
|
|
|
192
|
-
t.db.connect = function (connString) {
|
|
193
|
-
const conn = nativeDbConnect(connString);
|
|
214
|
+
t.db.connect = function (connString, options = {}) {
|
|
215
|
+
const conn = nativeDbConnect(connString, options);
|
|
194
216
|
|
|
195
217
|
if (!conn.query.__titanWrapped) {
|
|
196
218
|
const nativeQuery = conn.query;
|
|
197
|
-
|
|
219
|
+
|
|
220
|
+
conn.query = function (sql, params = []) {
|
|
221
|
+
if (typeof sql !== "string" || !sql.trim()) {
|
|
222
|
+
throw new Error("db.query(): SQL string required");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!Array.isArray(params)) {
|
|
226
|
+
throw new Error("db.query(): params must be array");
|
|
227
|
+
}
|
|
228
|
+
|
|
198
229
|
return createAsyncOp({
|
|
199
230
|
__titanAsync: true,
|
|
200
231
|
type: "db_query",
|
|
201
|
-
data: {
|
|
232
|
+
data: {
|
|
233
|
+
conn: connString,
|
|
234
|
+
query: sql,
|
|
235
|
+
params
|
|
236
|
+
}
|
|
202
237
|
});
|
|
203
238
|
};
|
|
239
|
+
|
|
204
240
|
conn.query.__titanWrapped = true;
|
|
205
241
|
}
|
|
206
242
|
|
|
@@ -96,7 +96,7 @@ async fn handler(State(state): State<AppState>, req: Request<Body>) -> impl Into
|
|
|
96
96
|
"Server-Timing",
|
|
97
97
|
format!("reply;dur={:.2}", elapsed.as_secs_f64() * 1000.0)
|
|
98
98
|
.parse()
|
|
99
|
-
.
|
|
99
|
+
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("")),
|
|
100
100
|
);
|
|
101
101
|
|
|
102
102
|
if log_enabled {
|
|
@@ -140,7 +140,7 @@ async fn handler(State(state): State<AppState>, req: Request<Body>) -> impl Into
|
|
|
140
140
|
"Server-Timing",
|
|
141
141
|
format!("fastpath;dur={:.2}", elapsed.as_secs_f64() * 1000.0)
|
|
142
142
|
.parse()
|
|
143
|
-
.
|
|
143
|
+
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("")),
|
|
144
144
|
);
|
|
145
145
|
|
|
146
146
|
if log_enabled {
|
|
@@ -409,7 +409,7 @@ async fn handler(State(state): State<AppState>, req: Request<Body>) -> impl Into
|
|
|
409
409
|
.join(", ");
|
|
410
410
|
response
|
|
411
411
|
.headers_mut()
|
|
412
|
-
.insert("Server-Timing", server_timing.parse().
|
|
412
|
+
.insert("Server-Timing", server_timing.parse().unwrap_or_else(|_| axum::http::HeaderValue::from_static("")));
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
// Logging
|